Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: Bluetooth: Add BT Tester GAP smoke test #86146

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions tests/bluetooth/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ LIST(APPEND QEMU_EXTRA_FLAGS -serial unix:/tmp/bt-stack-tester)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(tester)

if(CONFIG_SOC_SERIES_BSIM_NRFXX)
zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)
endif() # CONFIG_SOC_SERIES_BSIM_NRFXX
Comment on lines +10 to +15
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reviewers: Should we just always include this? Would allow us to use IS_ENABLED and include BSIM header files without the guard

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may not have understood the question, but BSIM_COMPONENTS_PATH won't be defined when not building for an nrf*bsim target. So this -I would be pointing to missing directories. And even if it were, users building for other targets may not have a bsim installation so they wouldn't be able to include bsim headers.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we do need the guard?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think so :)


zephyr_library_include_directories(${ZEPHYR_BASE}/samples/bluetooth)
zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/bluetooth/mesh)
zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/bluetooth/host)
Expand Down
1 change: 1 addition & 0 deletions tests/bluetooth/tester/Kconfig.sysbuild
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ config NET_CORE_BOARD
string
default "nrf5340dk/nrf5340/cpunet" if "$(BOARD)" = "nrf5340dk"
default "nrf5340_audio_dk/nrf5340/cpunet" if "$(BOARD)" = "nrf5340_audio_dk"
default "nrf5340bsim/nrf5340/cpunet" if $(BOARD_TARGET_STRING) = "NRF5340BSIM_NRF5340_CPUAPP"

config NET_CORE_IMAGE_HCI_IPC
bool "HCI IPC image on network core"
Expand Down
15 changes: 15 additions & 0 deletions tests/bluetooth/tester/boards/nrf52_bsim.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# CONFIG_TEST enforces minimal logging, which we don't want
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For reviewers: Should we place the BSIM board files in the test instead of the BT Tester directory?

CONFIG_TEST=n

CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y

CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=y
CONFIG_LOG_FUNC_NAME_PREFIX_ERR=y
CONFIG_LOG_FUNC_NAME_PREFIX_WRN=y

CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_BTTESTER_LOG_LEVEL_DBG=y

CONFIG_UART_PIPE=n
15 changes: 15 additions & 0 deletions tests/bluetooth/tester/boards/nrf5340bsim_nrf5340_cpuapp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# CONFIG_TEST enforces minimal logging, which we don't want
CONFIG_TEST=n

CONFIG_ASSERT=y
CONFIG_THREAD_NAME=y

CONFIG_LOG=y
CONFIG_LOG_BACKEND_SHOW_COLOR=y
CONFIG_LOG_FUNC_NAME_PREFIX_ERR=y
CONFIG_LOG_FUNC_NAME_PREFIX_WRN=y

CONFIG_LOG_DEFAULT_LEVEL=3
CONFIG_BTTESTER_LOG_LEVEL_DBG=y

CONFIG_UART_PIPE=n
13 changes: 13 additions & 0 deletions tests/bluetooth/tester/src/btp.c
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,19 @@ static void uart_send(const uint8_t *data, size_t len)
{
uart_pipe_send(data, len);
}
#elif defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
extern void bsim_btp_register_tester(uint8_t *buffer, size_t len, uart_pipe_recv_cb cb);
extern void bsim_btp_send_to_bsim(const uint8_t *data, size_t len);

static void uart_init(uint8_t *data)
{
bsim_btp_register_tester(data, BTP_MTU, recv_cb);
}

static void uart_send(const uint8_t *data, size_t len)
{
bsim_btp_send_to_bsim(data, len);
}
#else /* !CONFIG_UART_PIPE */
static uint8_t *recv_buf;
static size_t recv_off;
Expand Down
2 changes: 2 additions & 0 deletions tests/bluetooth/tester/src/btp/btp.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@

#define BTP_STATUS_VAL(err) (err) ? BTP_STATUS_FAILED : BTP_STATUS_SUCCESS

#define BTP_EVENT_OPCODE 0x80
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/* TODO indicate delay response, should be removed when all commands are
* converted to cmd+status+ev pattern
*/
Expand Down
4 changes: 3 additions & 1 deletion tests/bluetooth/tester/src/btp_gap.c
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,7 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,
if ((cmd_len < sizeof(*cp)) ||
(cmd_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len +
sizeof(duration) + sizeof(own_addr_type))) {
LOG_DBG("Invalid command length: %u", cmd_len);
return BTP_STATUS_FAILED;
}

Expand Down Expand Up @@ -759,6 +760,7 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,

err = tester_gap_create_adv_instance(&param, own_addr_type, ad, adv_len, sd, sd_len, NULL);
if (err != 0) {
LOG_DBG("Failed to create adv instance: %d", err);
return BTP_STATUS_FAILED;
}

Expand All @@ -771,7 +773,7 @@ static uint8_t start_advertising(const void *cmd, uint16_t cmd_len,

/* BTP API don't allow to set empty scan response data. */
if (err < 0) {
LOG_ERR("Failed to start advertising");
LOG_ERR("Failed to start advertising: %d", err);

return BTP_STATUS_FAILED;
}
Expand Down
12 changes: 12 additions & 0 deletions tests/bluetooth/tester/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@

/*
* Copyright (c) 2015-2016 Intel Corporation
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#include <zephyr/autoconf.h>

#include <zephyr/kernel.h>
#include <zephyr/types.h>
#include <zephyr/toolchain.h>

#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
#include "bstests.h"
#endif /* CONFIG_SOC_SERIES_BSIM_NRFXX */

#include <zephyr/logging/log.h>
#define LOG_MODULE_NAME bttester_main
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
Expand All @@ -19,5 +26,10 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_BTTESTER_LOG_LEVEL);
int main(void)
{
tester_init();

#if defined(CONFIG_SOC_SERIES_BSIM_NRFXX)
bst_main();
#endif /* CONFIG_SOC_SERIES_BSIM_NRFXX */

return 0;
}
2 changes: 1 addition & 1 deletion tests/bluetooth/tester/sysbuild.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ if(SB_CONFIG_NET_CORE_IMAGE_HCI_IPC)
)

set(${NET_APP}_EXTRA_CONF_FILE
${APP_DIR}/hci_ipc_cpunet.conf
${ZEPHYR_BASE}/tests/bluetooth/tester/hci_ipc_cpunet.conf
CACHE INTERNAL ""
)

Expand Down
1 change: 1 addition & 0 deletions tests/bsim/bluetooth/compile.nrf5340bsim_nrf5340_cpuapp.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export BOARD="${BOARD:-nrf5340bsim/nrf5340/cpuapp}"
source ${ZEPHYR_BASE}/tests/bsim/compile.source

${ZEPHYR_BASE}/tests/bsim/bluetooth/host/compile.sh
${ZEPHYR_BASE}/tests/bsim/bluetooth/tester/compile.sh

app=tests/bsim/bluetooth/ll/conn conf_file=prj_split_privacy.conf sysbuild=1 compile
app=tests/bsim/bluetooth/ll/throughput sysbuild=1 compile
Expand Down
1 change: 1 addition & 0 deletions tests/bsim/bluetooth/compile.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ ${ZEPHYR_BASE}/tests/bsim/bluetooth/samples/compile.sh
if [ ${BOARD} == "nrf52_bsim/native" ]; then
${ZEPHYR_BASE}/tests/bsim/bluetooth/hci_uart/compile.sh
fi
${ZEPHYR_BASE}/tests/bsim/bluetooth/tester/compile.sh

wait_for_background_jobs
31 changes: 31 additions & 0 deletions tests/bsim/bluetooth/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-License-Identifier: Apache-2.0

cmake_minimum_required(VERSION 3.20.0)

find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(tester_bsim)

# Add the BSIM tester
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/tester ${CMAKE_BINARY_DIR}/tester_build)

# This contains babblesim-specific helpers, e.g. device synchronization.
add_subdirectory(${ZEPHYR_BASE}/tests/bsim/babblekit babblekit)
target_link_libraries(app PRIVATE babblekit)

# Add the testlib
add_subdirectory(${ZEPHYR_BASE}/tests/bluetooth/common/testlib testlib)
target_link_libraries(app PRIVATE testlib)

zephyr_include_directories(
${BSIM_COMPONENTS_PATH}/libUtilv1/src/
${BSIM_COMPONENTS_PATH}/libPhyComv1/src/
)

zephyr_include_directories(src/)

target_sources(app PRIVATE
src/btp.c
src/test_main.c
src/gap/gap_central.c
src/gap/gap_peripheral.c
)
4 changes: 4 additions & 0 deletions tests/bsim/bluetooth/tester/Kconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright (c) 2024-2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

source "${ZEPHYR_BASE}/tests/bluetooth/tester/Kconfig"
10 changes: 10 additions & 0 deletions tests/bsim/bluetooth/tester/Kconfig.sysbuild
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) 2023-2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

source "${ZEPHYR_BASE}/tests/bluetooth/tester/Kconfig.sysbuild"

config NATIVE_SIMULATOR_PRIMARY_MCU_INDEX
int
# Let's pass the test arguments to the application MCU test
# otherwise by default they would have gone to the net core.
default 0 if $(BOARD_TARGET_STRING) = "NRF5340BSIM_NRF5340_CPUAPP"
12 changes: 12 additions & 0 deletions tests/bsim/bluetooth/tester/compile.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env bash
# Copyright 2025 Nordic Semiconductor ASA
# SPDX-License-Identifier: Apache-2.0

# Compile all the applications needed by the bsim tests in these subfolders

#set -x #uncomment this line for debugging
set -ue

: "${ZEPHYR_BASE:?ZEPHYR_BASE must be set to point to the zephyr root directory}"

west twister -T ${ZEPHYR_BASE}/tests/bsim/bluetooth/tester/
2 changes: 2 additions & 0 deletions tests/bsim/bluetooth/tester/prj.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Please build using the sample configuration file:
# ${ZEPHYR_BASE}/tests/bluetooth/tester/prj.conf
156 changes: 156 additions & 0 deletions tests/bsim/bluetooth/tester/src/bsim_btp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#include <string.h>
#include <zephyr/bluetooth/addr.h>
#include <zephyr/net_buf.h>

#include "btp/btp.h"

void bsim_btp_send_to_tester(const uint8_t *data, size_t len);
void bsim_btp_wait_for_evt(uint8_t service, uint8_t opcode, struct net_buf **out_buf);

static inline void bsim_btp_core_register(uint8_t id)
{

NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;
struct btp_core_register_service_cmd *cmd;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_CORE;
cmd_hdr->opcode = BTP_CORE_REGISTER_SERVICE;
cmd_hdr->index = BTP_INDEX_NONE;
cmd = net_buf_simple_add(&cmd_buffer, sizeof(*cmd));
cmd->id = id;

cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}

static inline void bsim_btp_gap_set_discoverable(uint8_t discoverable)
{
NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;
struct btp_gap_set_discoverable_cmd *cmd;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_GAP;
cmd_hdr->opcode = BTP_GAP_SET_DISCOVERABLE;
cmd_hdr->index = BTP_INDEX;
cmd = net_buf_simple_add(&cmd_buffer, sizeof(*cmd));
cmd->discoverable = discoverable;

cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}

static inline void bsim_btp_gap_start_advertising(uint8_t adv_data_len, uint8_t scan_rsp_len,
const uint8_t adv_sr_data[], uint32_t duration,
uint8_t own_addr_type)
{
NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;
struct btp_gap_start_advertising_cmd *cmd;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_GAP;
cmd_hdr->opcode = BTP_GAP_START_ADVERTISING;
cmd_hdr->index = BTP_INDEX;
cmd = net_buf_simple_add(&cmd_buffer, sizeof(*cmd));
cmd->adv_data_len = adv_data_len;
cmd->scan_rsp_len = scan_rsp_len;
if (adv_data_len > 0U) {
net_buf_simple_add_mem(&cmd_buffer, adv_sr_data, adv_data_len);
}
if (scan_rsp_len > 0U) {
net_buf_simple_add_mem(&cmd_buffer, adv_sr_data, scan_rsp_len);
}
net_buf_simple_add_le32(&cmd_buffer, duration);
net_buf_simple_add_u8(&cmd_buffer, own_addr_type);

cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}

static inline void bsim_btp_gap_start_discovery(uint8_t flags)
{
NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;
struct btp_gap_start_discovery_cmd *cmd;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_GAP;
cmd_hdr->opcode = BTP_GAP_START_DISCOVERY;
cmd_hdr->index = BTP_INDEX;
cmd = net_buf_simple_add(&cmd_buffer, sizeof(*cmd));
cmd->flags = flags;

cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}

static inline void bsim_btp_gap_stop_discovery(void)
{
NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_GAP;
cmd_hdr->opcode = BTP_GAP_STOP_DISCOVERY;
cmd_hdr->index = BTP_INDEX;
cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}

static inline void bsim_btp_gap_connect(const bt_addr_le_t *address, uint8_t own_addr_type)
{
NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;
struct btp_gap_connect_cmd *cmd;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_GAP;
cmd_hdr->opcode = BTP_GAP_CONNECT;
cmd_hdr->index = BTP_INDEX;
cmd = net_buf_simple_add(&cmd_buffer, sizeof(*cmd));
bt_addr_le_copy(&cmd->address, address);
cmd->own_addr_type = own_addr_type;

cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}

static inline void bsim_btp_gap_disconnect(const bt_addr_le_t *address)
{
NET_BUF_SIMPLE_DEFINE(cmd_buffer, BTP_MTU);

struct btp_hdr *cmd_hdr;
struct btp_gap_disconnect_cmd *cmd;

cmd_hdr = net_buf_simple_add(&cmd_buffer, sizeof(*cmd_hdr));
cmd_hdr->service = BTP_SERVICE_ID_GAP;
cmd_hdr->opcode = BTP_GAP_DISCONNECT;
cmd_hdr->index = BTP_INDEX;
cmd = net_buf_simple_add(&cmd_buffer, sizeof(*cmd));
bt_addr_le_copy(&cmd->address, address);

cmd_hdr->len = cmd_buffer.len - sizeof(*cmd_hdr);

bsim_btp_send_to_tester(cmd_buffer.data, cmd_buffer.len);
}
Loading
Loading