tests bsim: Change folder structure
Bsim won't be limited anymore to BT tests. In preparation for adding more tests in network areas swap the tests/bluetooth/bsim with tests/bsim/bluetooth There is no other changes in this commit beyond that. Signed-off-by: Alberto Escolar Piedras <alberto.escolar.piedras@nordicsemi.no>
This commit is contained in:
parent
388d522c32
commit
f27c0b4905
500 changed files with 222 additions and 222 deletions
21
tests/bsim/bluetooth/host/gatt/caching/CMakeLists.txt
Normal file
21
tests/bsim/bluetooth/host/gatt/caching/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
# 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 REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_gatt)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources} )
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
16
tests/bsim/bluetooth/host/gatt/caching/prj.conf
Normal file
16
tests/bsim/bluetooth/host/gatt/caching/prj.conf
Normal file
|
@ -0,0 +1,16 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_DEVICE_NAME="GATT tester"
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_GATT_DYNAMIC_DB=y
|
||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
||||
CONFIG_BT_L2CAP_ECRED=y
|
||||
CONFIG_BT_EATT=y
|
||||
CONFIG_BT_EATT_MAX=1
|
||||
CONFIG_BT_EATT_AUTO_CONNECT=n
|
||||
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_BT_TESTING=y
|
66
tests/bsim/bluetooth/host/gatt/caching/src/common.c
Normal file
66
tests/bsim/bluetooth/host/gatt/caching/src/common.c
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "argparse.h"
|
||||
|
||||
void test_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
if (bst_result != Passed) {
|
||||
FAIL("test failed (not passed after %i seconds)\n", WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
void test_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME);
|
||||
bst_result = In_progress;
|
||||
}
|
||||
|
||||
#define CHANNEL_ID 0
|
||||
#define MSG_SIZE 1
|
||||
|
||||
void backchannel_init(void)
|
||||
{
|
||||
uint device_number = get_device_nbr();
|
||||
uint peer_number = device_number ^ 1;
|
||||
uint device_numbers[] = { peer_number };
|
||||
uint channel_numbers[] = { CHANNEL_ID };
|
||||
uint *ch;
|
||||
|
||||
ch = bs_open_back_channel(device_number, device_numbers, channel_numbers,
|
||||
ARRAY_SIZE(channel_numbers));
|
||||
if (!ch) {
|
||||
FAIL("Unable to open backchannel\n");
|
||||
}
|
||||
}
|
||||
|
||||
void backchannel_sync_send(void)
|
||||
{
|
||||
uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
|
||||
|
||||
printk("Sending sync\n");
|
||||
bs_bc_send_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
|
||||
}
|
||||
|
||||
void backchannel_sync_wait(void)
|
||||
{
|
||||
uint8_t sync_msg[MSG_SIZE];
|
||||
|
||||
while (true) {
|
||||
if (bs_bc_is_msg_received(CHANNEL_ID) > 0) {
|
||||
bs_bc_receive_msg(CHANNEL_ID, sync_msg, ARRAY_SIZE(sync_msg));
|
||||
if (sync_msg[0] != get_device_nbr()) {
|
||||
/* Received a message from another device, exit */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(1));
|
||||
}
|
||||
|
||||
printk("Sync received\n");
|
||||
}
|
69
tests/bsim/bluetooth/host/gatt/caching/src/common.h
Normal file
69
tests/bsim/bluetooth/host/gatt/caching/src/common.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Common functions and helpers for BSIM GATT tests
|
||||
*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
#include "bs_pc_backchannel.h"
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
#define WAIT_TIME (60 * 1e6) /*seconds*/
|
||||
|
||||
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
|
||||
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
|
||||
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
|
||||
#define WAIT_FOR_FLAG(flag) \
|
||||
while (!(bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
#define CHRC_SIZE 10
|
||||
|
||||
#define TEST_SERVICE_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00)
|
||||
|
||||
#define TEST_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0x00)
|
||||
|
||||
#define TEST_ADDITIONAL_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0x11)
|
||||
|
||||
void test_tick(bs_time_t HW_device_time);
|
||||
void test_init(void);
|
||||
void backchannel_init(void);
|
||||
void backchannel_sync_send(void);
|
||||
void backchannel_sync_wait(void);
|
572
tests/bsim/bluetooth/host/gatt/caching/src/gatt_client_test.c
Normal file
572
tests/bsim/bluetooth/host/gatt/caching/src/gatt_client_test.c
Normal file
|
@ -0,0 +1,572 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_discover_complete);
|
||||
CREATE_FLAG(flag_write_complete);
|
||||
CREATE_FLAG(flag_chan_1_read);
|
||||
CREATE_FLAG(flag_chan_2_read);
|
||||
CREATE_FLAG(flag_db_hash_read);
|
||||
CREATE_FLAG(flag_encrypted);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
static uint16_t chrc_handle;
|
||||
static uint16_t csf_handle;
|
||||
static const struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
|
||||
{
|
||||
if (err != BT_SECURITY_ERR_SUCCESS) {
|
||||
FAIL("Encryption failed\n");
|
||||
} else if (level < BT_SECURITY_L2) {
|
||||
FAIL("Insufficient security\n");
|
||||
} else {
|
||||
SET_FLAG(flag_encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed,
|
||||
};
|
||||
|
||||
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 (g_conn != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_HCI_ADV_IND && type != BT_HCI_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);
|
||||
|
||||
printk("Stopping scan\n");
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
FAIL("Could not stop scan (err %d)\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
|
||||
if (err != 0) {
|
||||
FAIL("Could not connect to peer (err %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
if (chrc_handle == 0) {
|
||||
FAIL("Did not discover chrc (%x)\n", chrc_handle);
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_discover_complete);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("[ATTRIBUTE] handle %u\n", attr->handle);
|
||||
|
||||
if (params->type == BT_GATT_DISCOVER_PRIMARY &&
|
||||
bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
|
||||
printk("Found test service\n");
|
||||
params->uuid = NULL;
|
||||
params->start_handle = attr->handle + 1;
|
||||
params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
|
||||
const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
|
||||
|
||||
if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
|
||||
printk("Found chrc\n");
|
||||
chrc_handle = chrc->value_handle;
|
||||
|
||||
} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_CLIENT_FEATURES) == 0) {
|
||||
printk("Found csf\n");
|
||||
csf_handle = chrc->value_handle;
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void gatt_discover(const struct bt_uuid *uuid, uint8_t type)
|
||||
{
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
int err;
|
||||
|
||||
printk("Discovering services and characteristics\n");
|
||||
|
||||
discover_params.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 = type;
|
||||
discover_params.chan_opt = BT_ATT_CHAN_OPT_NONE;
|
||||
|
||||
UNSET_FLAG(flag_discover_complete);
|
||||
|
||||
err = bt_gatt_discover(g_conn, &discover_params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed(err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_discover_complete);
|
||||
printk("Discover complete\n");
|
||||
}
|
||||
static struct bt_gatt_read_params chan_1_read = {
|
||||
.handle_count = 1,
|
||||
.single = {
|
||||
.handle = 0, /* Will be set later */
|
||||
.offset = 0,
|
||||
},
|
||||
.chan_opt = BT_ATT_CHAN_OPT_NONE,
|
||||
};
|
||||
static struct bt_gatt_read_params chan_2_read = {
|
||||
.handle_count = 1,
|
||||
.single = {
|
||||
.handle = 0, /* Will be set later */
|
||||
.offset = 0,
|
||||
},
|
||||
.chan_opt = BT_ATT_CHAN_OPT_NONE,
|
||||
};
|
||||
static struct bt_gatt_read_params db_hash_read = {
|
||||
.handle_count = 0,
|
||||
.by_uuid = {
|
||||
.start_handle = BT_ATT_FIRST_ATTRIBUTE_HANDLE,
|
||||
.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
.uuid = BT_UUID_GATT_DB_HASH,
|
||||
},
|
||||
.chan_opt = BT_ATT_CHAN_OPT_NONE,
|
||||
};
|
||||
|
||||
void expect_status(uint8_t err, uint8_t status)
|
||||
{
|
||||
if (err != status) {
|
||||
FAIL("Unexpected status from read: 0x%02X, expected 0x%02X\n", err, status);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t gatt_read_expect_success_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params, const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
printk("GATT read cb: err 0x%02X\n", err);
|
||||
expect_status(err, BT_ATT_ERR_SUCCESS);
|
||||
|
||||
if (params == &db_hash_read) {
|
||||
SET_FLAG(flag_db_hash_read);
|
||||
} else if (params == &chan_1_read) {
|
||||
SET_FLAG(flag_chan_1_read);
|
||||
} else if (params == &chan_2_read) {
|
||||
SET_FLAG(flag_chan_2_read);
|
||||
} else {
|
||||
FAIL("Unexpected params\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t gatt_read_expect_err_unlikely_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
printk("GATT read cb: err 0x%02X\n", err);
|
||||
expect_status(err, BT_ATT_ERR_UNLIKELY);
|
||||
|
||||
if (params == &chan_1_read) {
|
||||
SET_FLAG(flag_chan_1_read);
|
||||
} else if (params == &chan_2_read) {
|
||||
SET_FLAG(flag_chan_2_read);
|
||||
} else {
|
||||
FAIL("Unexpected params\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t gatt_read_expect_err_out_of_sync_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
printk("GATT read cb: err 0x%02X\n", err);
|
||||
expect_status(err, BT_ATT_ERR_DB_OUT_OF_SYNC);
|
||||
|
||||
if (params == &chan_1_read) {
|
||||
SET_FLAG(flag_chan_1_read);
|
||||
} else if (params == &chan_2_read) {
|
||||
SET_FLAG(flag_chan_2_read);
|
||||
} else {
|
||||
FAIL("Unexpected params\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gatt_read(struct bt_gatt_read_params *read_params)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk("Reading\n");
|
||||
|
||||
err = bt_gatt_read(g_conn, read_params);
|
||||
if (err != 0) {
|
||||
FAIL("bt_gatt_read failed: %d\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
|
||||
{
|
||||
if (err != BT_ATT_ERR_SUCCESS) {
|
||||
FAIL("Write failed: 0x%02X\n", err);
|
||||
}
|
||||
|
||||
SET_FLAG(flag_write_complete);
|
||||
}
|
||||
|
||||
static void enable_robust_caching(void)
|
||||
{
|
||||
/* Client Supported Features Characteristic Value
|
||||
* Bit 0: Robust Caching
|
||||
* Bit 1: EATT
|
||||
*/
|
||||
static const uint8_t csf[] = { BIT(0) | BIT(1) };
|
||||
static struct bt_gatt_write_params write_params = {
|
||||
.func = write_cb,
|
||||
.offset = 0,
|
||||
.data = csf,
|
||||
.length = sizeof(csf),
|
||||
.chan_opt = BT_ATT_CHAN_OPT_NONE,
|
||||
};
|
||||
int err;
|
||||
|
||||
printk("Writing to Client Supported Features Characteristic\n");
|
||||
|
||||
write_params.handle = csf_handle;
|
||||
UNSET_FLAG(flag_write_complete);
|
||||
|
||||
err = bt_gatt_write(g_conn, &write_params);
|
||||
if (err) {
|
||||
FAIL("bt_gatt_write failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_write_complete);
|
||||
printk("Success\n");
|
||||
}
|
||||
|
||||
static void test_main_common(bool connect_eatt)
|
||||
{
|
||||
int err;
|
||||
|
||||
backchannel_init();
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
|
||||
if (err != 0) {
|
||||
FAIL("Scanning failed to start (err %d)\n", err);
|
||||
}
|
||||
|
||||
printk("Scanning successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
|
||||
if (err) {
|
||||
FAIL("Failed to start encryption procedure\n");
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_encrypted);
|
||||
|
||||
gatt_discover(test_svc_uuid, BT_GATT_DISCOVER_PRIMARY);
|
||||
gatt_discover(BT_UUID_GATT_CLIENT_FEATURES, BT_GATT_DISCOVER_CHARACTERISTIC);
|
||||
|
||||
enable_robust_caching();
|
||||
|
||||
if (connect_eatt) {
|
||||
while (bt_eatt_count(g_conn) < 1) {
|
||||
/* Wait for EATT channel to connect, in case it hasn't already */
|
||||
k_sleep(K_MSEC(10));
|
||||
}
|
||||
}
|
||||
|
||||
/* Tell the server to register additional service */
|
||||
backchannel_sync_send();
|
||||
|
||||
/* Wait for new service to be added by server */
|
||||
backchannel_sync_wait();
|
||||
|
||||
chan_1_read.single.handle = chrc_handle;
|
||||
chan_2_read.single.handle = chrc_handle;
|
||||
}
|
||||
|
||||
static void test_main_db_hash_read_eatt(void)
|
||||
{
|
||||
test_main_common(true);
|
||||
|
||||
/* Read the DB hash to become change-aware */
|
||||
db_hash_read.func = gatt_read_expect_success_cb;
|
||||
gatt_read(&db_hash_read);
|
||||
WAIT_FOR_FLAG(flag_db_hash_read);
|
||||
|
||||
/* These shall now succeed */
|
||||
chan_1_read.func = gatt_read_expect_success_cb;
|
||||
chan_2_read.func = gatt_read_expect_success_cb;
|
||||
UNSET_FLAG(flag_chan_1_read);
|
||||
UNSET_FLAG(flag_chan_2_read);
|
||||
gatt_read(&chan_1_read);
|
||||
gatt_read(&chan_2_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_2_read);
|
||||
|
||||
/* Signal to server that reads are done */
|
||||
backchannel_sync_send();
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_out_of_sync_eatt(void)
|
||||
{
|
||||
test_main_common(true);
|
||||
|
||||
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
|
||||
chan_2_read.func = gatt_read_expect_err_out_of_sync_cb;
|
||||
gatt_read(&chan_1_read);
|
||||
gatt_read(&chan_2_read);
|
||||
|
||||
/* Wait until received response on both reads. When robust caching is implemented
|
||||
* on the client side, the waiting shall be done automatically by the host when
|
||||
* reading the DB hash.
|
||||
*/
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_2_read);
|
||||
|
||||
/* Read the DB hash to become change-aware */
|
||||
db_hash_read.func = gatt_read_expect_success_cb;
|
||||
gatt_read(&db_hash_read);
|
||||
WAIT_FOR_FLAG(flag_db_hash_read);
|
||||
|
||||
/* These shall now succeed */
|
||||
chan_1_read.func = gatt_read_expect_success_cb;
|
||||
chan_2_read.func = gatt_read_expect_success_cb;
|
||||
UNSET_FLAG(flag_chan_1_read);
|
||||
UNSET_FLAG(flag_chan_2_read);
|
||||
gatt_read(&chan_1_read);
|
||||
gatt_read(&chan_2_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_2_read);
|
||||
|
||||
/* Signal to server that reads are done */
|
||||
backchannel_sync_send();
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_retry_reads_eatt(void)
|
||||
{
|
||||
test_main_common(true);
|
||||
|
||||
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
|
||||
chan_2_read.func = gatt_read_expect_err_out_of_sync_cb;
|
||||
gatt_read(&chan_1_read);
|
||||
gatt_read(&chan_2_read);
|
||||
|
||||
/* Wait until received response on both reads. When robust caching is implemented
|
||||
* on the client side, the waiting shall be done automatically by the host when
|
||||
* reading the DB hash.
|
||||
*/
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_2_read);
|
||||
|
||||
/* Retry the reads, these shall time out */
|
||||
chan_1_read.func = gatt_read_expect_err_unlikely_cb;
|
||||
chan_2_read.func = gatt_read_expect_err_unlikely_cb;
|
||||
UNSET_FLAG(flag_chan_1_read);
|
||||
UNSET_FLAG(flag_chan_2_read);
|
||||
gatt_read(&chan_1_read);
|
||||
gatt_read(&chan_2_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_2_read);
|
||||
|
||||
/* Signal to server that reads are done */
|
||||
backchannel_sync_send();
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_db_hash_read_no_eatt(void)
|
||||
{
|
||||
test_main_common(false);
|
||||
|
||||
/* Read the DB hash to become change-aware */
|
||||
db_hash_read.func = gatt_read_expect_success_cb;
|
||||
gatt_read(&db_hash_read);
|
||||
WAIT_FOR_FLAG(flag_db_hash_read);
|
||||
|
||||
/* Read shall now succeed */
|
||||
chan_1_read.func = gatt_read_expect_success_cb;
|
||||
UNSET_FLAG(flag_chan_1_read);
|
||||
gatt_read(&chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
|
||||
/* Signal to server that reads are done */
|
||||
backchannel_sync_send();
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_out_of_sync_no_eatt(void)
|
||||
{
|
||||
test_main_common(false);
|
||||
|
||||
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
|
||||
gatt_read(&chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
|
||||
/* Read the DB hash to become change-aware */
|
||||
db_hash_read.func = gatt_read_expect_success_cb;
|
||||
gatt_read(&db_hash_read);
|
||||
WAIT_FOR_FLAG(flag_db_hash_read);
|
||||
|
||||
/* Read shall now succeed */
|
||||
chan_1_read.func = gatt_read_expect_success_cb;
|
||||
UNSET_FLAG(flag_chan_1_read);
|
||||
gatt_read(&chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
|
||||
/* Signal to server that reads are done */
|
||||
backchannel_sync_send();
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_retry_reads_no_eatt(void)
|
||||
{
|
||||
test_main_common(false);
|
||||
|
||||
chan_1_read.func = gatt_read_expect_err_out_of_sync_cb;
|
||||
gatt_read(&chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
|
||||
/* Read again to become change-aware */
|
||||
chan_1_read.func = gatt_read_expect_success_cb;
|
||||
UNSET_FLAG(flag_chan_1_read);
|
||||
gatt_read(&chan_1_read);
|
||||
WAIT_FOR_FLAG(flag_chan_1_read);
|
||||
|
||||
/* Signal to server that reads are done */
|
||||
backchannel_sync_send();
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_vcs[] = {
|
||||
{
|
||||
.test_id = "gatt_client_db_hash_read_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_db_hash_read_eatt,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_out_of_sync_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_out_of_sync_eatt,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_retry_reads_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_retry_reads_eatt,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_db_hash_read_no_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_db_hash_read_no_eatt,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_out_of_sync_no_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_out_of_sync_no_eatt,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_retry_reads_no_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_retry_reads_no_eatt,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_vcs);
|
||||
}
|
173
tests/bsim/bluetooth/host/gatt/caching/src/gatt_server_test.c
Normal file
173
tests/bsim/bluetooth/host/gatt/caching/src/gatt_server_test.c
Normal file
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_is_encrypted);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
g_conn = bt_conn_ref(conn);
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void security_changed(struct bt_conn *conn, bt_security_t level,
|
||||
enum bt_security_err security_err)
|
||||
{
|
||||
if (security_err == BT_SECURITY_ERR_SUCCESS && level > BT_SECURITY_L1) {
|
||||
SET_FLAG(flag_is_encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed,
|
||||
};
|
||||
|
||||
#define ARRAY_ITEM(i, _) i
|
||||
static const uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
|
||||
static ssize_t read_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset)
|
||||
{
|
||||
printk("Characteristic read\n");
|
||||
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, (const void *)chrc_data, CHRC_SIZE);
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(test_svc, BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
|
||||
BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID, BT_GATT_CHRC_READ, BT_GATT_PERM_READ,
|
||||
read_test_chrc, NULL, NULL));
|
||||
|
||||
static struct bt_gatt_attr additional_attributes[] = {
|
||||
BT_GATT_CHARACTERISTIC(TEST_ADDITIONAL_CHRC_UUID, 0, BT_GATT_PERM_NONE, NULL, NULL, NULL),
|
||||
};
|
||||
|
||||
static struct bt_gatt_service additional_gatt_service = BT_GATT_SERVICE(additional_attributes);
|
||||
|
||||
static void test_main_common(bool connect_eatt)
|
||||
{
|
||||
int err;
|
||||
const struct bt_data ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS,
|
||||
(BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)) };
|
||||
|
||||
backchannel_init();
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth init failed (err %d)\n", err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err != 0) {
|
||||
FAIL("Advertising failed to start (err %d)\n", err);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Advertising successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
if (connect_eatt) {
|
||||
WAIT_FOR_FLAG(flag_is_encrypted);
|
||||
|
||||
err = bt_eatt_connect(g_conn, CONFIG_BT_EATT_MAX);
|
||||
if (err) {
|
||||
FAIL("Failed to connect EATT channels (err %d)\n", err);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for client to do discovery and configuration */
|
||||
backchannel_sync_wait();
|
||||
|
||||
printk("Registering additional service\n");
|
||||
err = bt_gatt_service_register(&additional_gatt_service);
|
||||
if (err < 0) {
|
||||
FAIL("Registering additional service failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
/* Signal to client that additional service is registered */
|
||||
backchannel_sync_send();
|
||||
|
||||
/* Wait for client to be done reading */
|
||||
backchannel_sync_wait();
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static void test_main_eatt(void)
|
||||
{
|
||||
test_main_common(true);
|
||||
}
|
||||
|
||||
static void test_main_no_eatt(void)
|
||||
{
|
||||
test_main_common(false);
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_gatt_server[] = {
|
||||
{
|
||||
.test_id = "gatt_server_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_eatt,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_server_no_eatt",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_no_eatt,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_gatt_server);
|
||||
}
|
21
tests/bsim/bluetooth/host/gatt/caching/src/main.c
Normal file
21
tests/bsim/bluetooth/host/gatt/caching/src/main.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bstests.h"
|
||||
|
||||
extern struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests);
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_gatt_server_install,
|
||||
test_gatt_client_install,
|
||||
NULL
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
38
tests/bsim/bluetooth/host/gatt/caching/test_scripts/_run_test.sh
Executable file
38
tests/bsim/bluetooth/host/gatt/caching/test_scripts/_run_test.sh
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
verbosity_level=2
|
||||
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 120 $@ &
|
||||
process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_caching_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=${client_id}
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_caching_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=${server_id}
|
||||
|
||||
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
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_caching_db_hash_read_eatt" \
|
||||
client_id="gatt_client_db_hash_read_eatt" \
|
||||
server_id="gatt_server_eatt" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_caching_db_hash_read_no_eatt" \
|
||||
client_id="gatt_client_db_hash_read_no_eatt" \
|
||||
server_id="gatt_server_no_eatt" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_caching_out_of_sync_eatt" \
|
||||
client_id="gatt_client_out_of_sync_eatt" \
|
||||
server_id="gatt_server_eatt" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_caching_out_of_sync_no_eatt" \
|
||||
client_id="gatt_client_out_of_sync_no_eatt" \
|
||||
server_id="gatt_server_no_eatt" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_caching_retry_reads_eatt" \
|
||||
client_id="gatt_client_retry_reads_eatt" \
|
||||
server_id="gatt_server_eatt" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
|
@ -0,0 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_caching_retry_reads_no_eatt" \
|
||||
client_id="gatt_client_retry_reads_no_eatt" \
|
||||
server_id="gatt_server_no_eatt" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
21
tests/bsim/bluetooth/host/gatt/general/CMakeLists.txt
Normal file
21
tests/bsim/bluetooth/host/gatt/general/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
# 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 REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_gatt)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources} )
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
6
tests/bsim/bluetooth/host/gatt/general/prj.conf
Normal file
6
tests/bsim/bluetooth/host/gatt/general/prj.conf
Normal file
|
@ -0,0 +1,6 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_DEVICE_NAME="GATT tester"
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
CONFIG_BT_ATT_PREPARE_COUNT=3
|
20
tests/bsim/bluetooth/host/gatt/general/src/common.c
Normal file
20
tests/bsim/bluetooth/host/gatt/general/src/common.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void test_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
if (bst_result != Passed) {
|
||||
FAIL("test failed (not passed after %i seconds)\n", WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
void test_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME);
|
||||
bst_result = In_progress;
|
||||
}
|
66
tests/bsim/bluetooth/host/gatt/general/src/common.h
Normal file
66
tests/bsim/bluetooth/host/gatt/general/src/common.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/**
|
||||
* Common functions and helpers for BSIM GATT tests
|
||||
*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
#define WAIT_TIME (30 * 1e6) /*seconds*/
|
||||
|
||||
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
|
||||
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
|
||||
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
|
||||
#define WAIT_FOR_FLAG(flag) \
|
||||
while (!(bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
#define CHRC_SIZE 10
|
||||
#define LONG_CHRC_SIZE 40
|
||||
|
||||
#define TEST_SERVICE_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00)
|
||||
|
||||
#define TEST_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0x00)
|
||||
|
||||
#define TEST_LONG_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, \
|
||||
0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0x11)
|
||||
|
||||
void test_tick(bs_time_t HW_device_time);
|
||||
void test_init(void);
|
304
tests/bsim/bluetooth/host/gatt/general/src/gatt_client_test.c
Normal file
304
tests/bsim/bluetooth/host/gatt/general/src/gatt_client_test.c
Normal file
|
@ -0,0 +1,304 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_discover_complete);
|
||||
CREATE_FLAG(flag_write_complete);
|
||||
CREATE_FLAG(flag_read_complete);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
static uint16_t chrc_handle;
|
||||
static uint16_t long_chrc_handle;
|
||||
static struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
|
||||
|
||||
#define ARRAY_ITEM(i, _) i
|
||||
static uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
static uint8_t long_chrc_data[] = { LISTIFY(LONG_CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
g_conn = conn;
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
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 (g_conn != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_HCI_ADV_IND && type != BT_HCI_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);
|
||||
|
||||
printk("Stopping scan\n");
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
FAIL("Could not stop scan: %d");
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
|
||||
BT_LE_CONN_PARAM_DEFAULT, &g_conn);
|
||||
if (err != 0) {
|
||||
FAIL("Could not connect to peer: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
if (chrc_handle == 0 || long_chrc_handle == 0) {
|
||||
FAIL("Did not discover chrc (%x) or long_chrc (%x)",
|
||||
chrc_handle, long_chrc_handle);
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_discover_complete);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("[ATTRIBUTE] handle %u\n", attr->handle);
|
||||
|
||||
if (params->type == BT_GATT_DISCOVER_PRIMARY &&
|
||||
bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
|
||||
printk("Found test service\n");
|
||||
params->uuid = NULL;
|
||||
params->start_handle = attr->handle + 1;
|
||||
params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
|
||||
struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
|
||||
|
||||
if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
|
||||
printk("Found chrc\n");
|
||||
chrc_handle = chrc->value_handle;
|
||||
} else if (bt_uuid_cmp(chrc->uuid, TEST_LONG_CHRC_UUID) == 0) {
|
||||
printk("Found long_chrc\n");
|
||||
long_chrc_handle = chrc->value_handle;
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void gatt_discover(void)
|
||||
{
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
int err;
|
||||
|
||||
printk("Discovering services and characteristics\n");
|
||||
|
||||
discover_params.uuid = test_svc_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(g_conn, &discover_params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed(err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_discover_complete);
|
||||
printk("Discover complete\n");
|
||||
}
|
||||
|
||||
static void gatt_write_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_write_params *params)
|
||||
{
|
||||
if (err != BT_ATT_ERR_SUCCESS) {
|
||||
FAIL("Write failed: 0x%02X\n", err);
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_write_complete);
|
||||
}
|
||||
|
||||
static void gatt_write(uint16_t handle)
|
||||
{
|
||||
static struct bt_gatt_write_params write_params;
|
||||
int err;
|
||||
|
||||
if (handle == chrc_handle) {
|
||||
printk("Writing to chrc\n");
|
||||
write_params.data = chrc_data;
|
||||
write_params.length = sizeof(chrc_data);
|
||||
} else if (handle) {
|
||||
printk("Writing to long_chrc\n");
|
||||
write_params.data = long_chrc_data;
|
||||
write_params.length = sizeof(long_chrc_data);
|
||||
}
|
||||
|
||||
write_params.func = gatt_write_cb;
|
||||
write_params.handle = handle;
|
||||
|
||||
UNSET_FLAG(flag_write_complete);
|
||||
|
||||
err = bt_gatt_write(g_conn, &write_params);
|
||||
if (err != 0) {
|
||||
FAIL("bt_gatt_write failed: %d\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_write_complete);
|
||||
printk("success\n");
|
||||
}
|
||||
|
||||
static uint8_t gatt_read_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
if (err != BT_ATT_ERR_SUCCESS) {
|
||||
FAIL("Read failed: 0x%02X\n", err);
|
||||
}
|
||||
|
||||
if (params->single.handle == chrc_handle) {
|
||||
if (length != CHRC_SIZE ||
|
||||
memcmp(data, chrc_data, length) != 0) {
|
||||
FAIL("chrc data different than expected", err);
|
||||
}
|
||||
} else if (params->single.handle == chrc_handle) {
|
||||
if (length != LONG_CHRC_SIZE ||
|
||||
memcmp(data, long_chrc_data, length) != 0) {
|
||||
FAIL("long_chrc data different than expected", err);
|
||||
}
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_read_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gatt_read(uint16_t handle)
|
||||
{
|
||||
static struct bt_gatt_read_params read_params;
|
||||
int err;
|
||||
|
||||
printk("Reading chrc\n");
|
||||
|
||||
read_params.func = gatt_read_cb;
|
||||
read_params.handle_count = 1;
|
||||
read_params.single.handle = chrc_handle;
|
||||
read_params.single.offset = 0;
|
||||
|
||||
UNSET_FLAG(flag_read_complete);
|
||||
|
||||
err = bt_gatt_read(g_conn, &read_params);
|
||||
if (err != 0) {
|
||||
FAIL("bt_gatt_read failed: %d\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_read_complete);
|
||||
printk("success\n");
|
||||
}
|
||||
|
||||
static void test_main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
|
||||
if (err != 0) {
|
||||
FAIL("Scanning failed to start (err %d)\n", err);
|
||||
}
|
||||
|
||||
printk("Scanning successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
gatt_discover();
|
||||
|
||||
/* Write and read a few times to ensure stateless behavior */
|
||||
for (size_t i = 0; i < 3; i++) {
|
||||
gatt_write(chrc_handle);
|
||||
gatt_read(chrc_handle);
|
||||
gatt_write(long_chrc_handle);
|
||||
gatt_read(long_chrc_handle);
|
||||
}
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_vcs[] = {
|
||||
{
|
||||
.test_id = "gatt_client",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main
|
||||
},
|
||||
BSTEST_END_MARKER
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_vcs);
|
||||
}
|
180
tests/bsim/bluetooth/host/gatt/general/src/gatt_server_test.c
Normal file
180
tests/bsim/bluetooth/host/gatt/general/src/gatt_server_test.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
g_conn = bt_conn_ref(conn);
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static uint8_t chrc_data[CHRC_SIZE];
|
||||
static uint8_t long_chrc_data[LONG_CHRC_SIZE];
|
||||
|
||||
static ssize_t read_test_chrc(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset)
|
||||
{
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
||||
(void *)chrc_data, sizeof(chrc_data));
|
||||
}
|
||||
|
||||
static ssize_t write_test_chrc(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len,
|
||||
uint16_t offset, uint8_t flags)
|
||||
{
|
||||
printk("chrc len %u offset %u\n", len, offset);
|
||||
|
||||
if (len > sizeof(chrc_data)) {
|
||||
printk("Invalid chrc length\n");
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
} else if (offset + len > sizeof(chrc_data)) {
|
||||
printk("Invalid chrc offset and length\n");
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
if (flags != 0) {
|
||||
FAIL("Invalid flags %u\n", flags);
|
||||
return BT_GATT_ERR(BT_ATT_ERR_UNLIKELY);
|
||||
}
|
||||
|
||||
(void)memcpy(chrc_data + offset, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t read_long_test_chrc(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset)
|
||||
{
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset,
|
||||
(void *)long_chrc_data, sizeof(long_chrc_data));
|
||||
}
|
||||
|
||||
static ssize_t write_long_test_chrc(struct bt_conn *conn,
|
||||
const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len,
|
||||
uint16_t offset, uint8_t flags)
|
||||
{
|
||||
static uint8_t prepare_count;
|
||||
|
||||
printk("long_chrc len %u offset %u\n", len, offset);
|
||||
|
||||
if (len > sizeof(long_chrc_data)) {
|
||||
printk("Invalid long_chrc length\n");
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
||||
} else if (offset + len > sizeof(long_chrc_data)) {
|
||||
printk("Invalid long_chrc offset and length\n");
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
if (flags & BT_GATT_WRITE_FLAG_PREPARE) {
|
||||
printk("prepare_count %u\n", prepare_count++);
|
||||
return BT_GATT_ERR(BT_ATT_ERR_SUCCESS);
|
||||
}
|
||||
|
||||
(void)memcpy(long_chrc_data + offset, buf, len);
|
||||
prepare_count = 0;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(test_svc,
|
||||
BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
|
||||
BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID,
|
||||
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_WRITE | BT_GATT_PERM_READ,
|
||||
read_test_chrc, write_test_chrc, NULL),
|
||||
BT_GATT_CHARACTERISTIC(TEST_LONG_CHRC_UUID,
|
||||
BT_GATT_CHRC_WRITE | BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_WRITE | BT_GATT_PERM_READ | BT_GATT_PERM_PREPARE_WRITE,
|
||||
read_long_test_chrc, write_long_test_chrc, NULL),
|
||||
);
|
||||
|
||||
static void test_main(void)
|
||||
{
|
||||
int err;
|
||||
const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR))
|
||||
};
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err != 0) {
|
||||
FAIL("Advertising failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Advertising successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_gatt_server[] = {
|
||||
{
|
||||
.test_id = "gatt_server",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main
|
||||
},
|
||||
BSTEST_END_MARKER
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_gatt_server);
|
||||
}
|
21
tests/bsim/bluetooth/host/gatt/general/src/main.c
Normal file
21
tests/bsim/bluetooth/host/gatt/general/src/main.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bstests.h"
|
||||
|
||||
extern struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests);
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_gatt_server_install,
|
||||
test_gatt_client_install,
|
||||
NULL
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
40
tests/bsim/bluetooth/host/gatt/general/test_scripts/gatt.sh
Executable file
40
tests/bsim/bluetooth/host/gatt/general/test_scripts/gatt.sh
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Basic GATT test: A central acting as a GATT client scans for and connects
|
||||
# to a peripheral acting as a GATT server. The GATT client will then attempt
|
||||
# to write and read to and from a few GATT characteristics.
|
||||
simulation_id="gatt"
|
||||
verbosity_level=2
|
||||
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 120 $@ & process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_general_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=gatt_client
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_general_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=gatt_server
|
||||
|
||||
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
|
21
tests/bsim/bluetooth/host/gatt/notify/CMakeLists.txt
Normal file
21
tests/bsim/bluetooth/host/gatt/notify/CMakeLists.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
# 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 REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_gatt)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources} )
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
17
tests/bsim/bluetooth/host/gatt/notify/prj.conf
Normal file
17
tests/bsim/bluetooth/host/gatt/notify/prj.conf
Normal file
|
@ -0,0 +1,17 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_DEVICE_NAME="GATT tester"
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
|
||||
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
||||
CONFIG_BT_L2CAP_ECRED=y
|
||||
CONFIG_BT_EATT=y
|
||||
CONFIG_BT_EATT_MAX=5
|
||||
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_BT_TESTING=y
|
||||
CONFIG_BT_DEBUG_LOG=y
|
20
tests/bsim/bluetooth/host/gatt/notify/src/common.c
Normal file
20
tests/bsim/bluetooth/host/gatt/notify/src/common.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void test_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
if (bst_result != Passed) {
|
||||
FAIL("test failed (not passed after %i seconds)\n", WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
void test_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME);
|
||||
bst_result = In_progress;
|
||||
}
|
69
tests/bsim/bluetooth/host/gatt/notify/src/common.h
Normal file
69
tests/bsim/bluetooth/host/gatt/notify/src/common.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* Common functions and helpers for BSIM GATT tests
|
||||
*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
#define WAIT_TIME (60 * 1e6) /*seconds*/
|
||||
|
||||
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
|
||||
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
|
||||
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
|
||||
#define WAIT_FOR_FLAG(flag) \
|
||||
while (!(bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
#define CHRC_SIZE 10
|
||||
#define LONG_CHRC_SIZE 40
|
||||
|
||||
#define TEST_SERVICE_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, \
|
||||
0x07, 0x08, 0x09, 0x00, 0x00)
|
||||
|
||||
#define TEST_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, \
|
||||
0x07, 0x08, 0x09, 0xFF, 0x00)
|
||||
|
||||
#define TEST_LONG_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, \
|
||||
0x07, 0x08, 0x09, 0xFF, 0x11)
|
||||
|
||||
void test_tick(bs_time_t HW_device_time);
|
||||
void test_init(void);
|
||||
|
||||
#define NOTIFICATION_COUNT 10
|
||||
BUILD_ASSERT(NOTIFICATION_COUNT % 2 == 0);
|
469
tests/bsim/bluetooth/host/gatt/notify/src/gatt_client_test.c
Normal file
469
tests/bsim/bluetooth/host/gatt/notify/src/gatt_client_test.c
Normal file
|
@ -0,0 +1,469 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_is_encrypted);
|
||||
CREATE_FLAG(flag_discover_complete);
|
||||
CREATE_FLAG(flag_short_subscribed);
|
||||
CREATE_FLAG(flag_long_subscribed);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
static uint16_t chrc_handle;
|
||||
static uint16_t long_chrc_handle;
|
||||
static struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
|
||||
{
|
||||
if (err) {
|
||||
FAIL("Encryption failer (%d)\n", err);
|
||||
} else if (level < BT_SECURITY_L2) {
|
||||
FAIL("Insufficient sec level (%d)\n", level);
|
||||
} else {
|
||||
SET_FLAG(flag_is_encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed,
|
||||
};
|
||||
|
||||
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 (g_conn != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_HCI_ADV_IND && type != BT_HCI_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);
|
||||
|
||||
printk("Stopping scan\n");
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
FAIL("Could not stop scan: %d");
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
|
||||
if (err != 0) {
|
||||
FAIL("Could not connect to peer: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
if (chrc_handle == 0 || long_chrc_handle == 0) {
|
||||
FAIL("Did not discover chrc (%x) or long_chrc (%x)", chrc_handle,
|
||||
long_chrc_handle);
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_discover_complete);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("[ATTRIBUTE] handle %u\n", attr->handle);
|
||||
|
||||
if (params->type == BT_GATT_DISCOVER_PRIMARY &&
|
||||
bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
|
||||
printk("Found test service\n");
|
||||
params->uuid = NULL;
|
||||
params->start_handle = attr->handle + 1;
|
||||
params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
|
||||
const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
|
||||
|
||||
if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
|
||||
printk("Found chrc\n");
|
||||
chrc_handle = chrc->value_handle;
|
||||
} else if (bt_uuid_cmp(chrc->uuid, TEST_LONG_CHRC_UUID) == 0) {
|
||||
printk("Found long_chrc\n");
|
||||
long_chrc_handle = chrc->value_handle;
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void gatt_discover(enum bt_att_chan_opt opt)
|
||||
{
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
int err;
|
||||
|
||||
printk("Discovering services and characteristics\n");
|
||||
|
||||
discover_params.uuid = test_svc_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;
|
||||
discover_params.chan_opt = opt;
|
||||
|
||||
err = bt_gatt_discover(g_conn, &discover_params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed(err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_discover_complete);
|
||||
printk("Discover complete\n");
|
||||
}
|
||||
|
||||
static void test_short_subscribed(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_write_params *params)
|
||||
{
|
||||
if (err) {
|
||||
FAIL("Subscribe failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
SET_FLAG(flag_short_subscribed);
|
||||
|
||||
if (!params) {
|
||||
printk("params NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params->handle == chrc_handle) {
|
||||
printk("Subscribed to short characteristic\n");
|
||||
} else {
|
||||
FAIL("Unknown handle %d\n", params->handle);
|
||||
}
|
||||
}
|
||||
|
||||
static void test_long_subscribed(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_write_params *params)
|
||||
{
|
||||
if (err) {
|
||||
FAIL("Subscribe failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
SET_FLAG(flag_long_subscribed);
|
||||
|
||||
if (!params) {
|
||||
printk("params NULL\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params->handle == long_chrc_handle) {
|
||||
printk("Subscribed to long characteristic\n");
|
||||
} else {
|
||||
FAIL("Unknown handle %d\n", params->handle);
|
||||
}
|
||||
}
|
||||
|
||||
static volatile size_t num_notifications;
|
||||
uint8_t test_notify(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
printk("Received notification #%u with length %d\n", num_notifications++, length);
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static struct bt_gatt_discover_params disc_params_short;
|
||||
static struct bt_gatt_subscribe_params sub_params_short = {
|
||||
.notify = test_notify,
|
||||
.write = test_short_subscribed,
|
||||
.ccc_handle = 0, /* Auto-discover CCC*/
|
||||
.disc_params = &disc_params_short, /* Auto-discover CCC */
|
||||
.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
.value = BT_GATT_CCC_NOTIFY,
|
||||
};
|
||||
static struct bt_gatt_discover_params disc_params_long;
|
||||
static struct bt_gatt_subscribe_params sub_params_long = {
|
||||
.notify = test_notify,
|
||||
.write = test_long_subscribed,
|
||||
.ccc_handle = 0, /* Auto-discover CCC*/
|
||||
.disc_params = &disc_params_long, /* Auto-discover CCC */
|
||||
.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
.value = BT_GATT_CCC_NOTIFY,
|
||||
};
|
||||
|
||||
static void gatt_subscribe_short(enum bt_att_chan_opt opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
sub_params_short.value_handle = chrc_handle;
|
||||
sub_params_short.chan_opt = opt;
|
||||
err = bt_gatt_subscribe(g_conn, &sub_params_short);
|
||||
if (err < 0) {
|
||||
FAIL("Failed to subscribe\n");
|
||||
} else {
|
||||
printk("Subscribe request sent\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void gatt_unsubscribe_short(enum bt_att_chan_opt opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
sub_params_short.value_handle = chrc_handle;
|
||||
sub_params_short.chan_opt = opt;
|
||||
err = bt_gatt_unsubscribe(g_conn, &sub_params_short);
|
||||
if (err < 0) {
|
||||
FAIL("Failed to unsubscribe\n");
|
||||
} else {
|
||||
printk("Unsubscribe request sent\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void gatt_subscribe_long(enum bt_att_chan_opt opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
UNSET_FLAG(flag_long_subscribed);
|
||||
sub_params_long.value_handle = long_chrc_handle;
|
||||
sub_params_long.chan_opt = opt;
|
||||
err = bt_gatt_subscribe(g_conn, &sub_params_long);
|
||||
if (err < 0) {
|
||||
FAIL("Failed to subscribe\n");
|
||||
} else {
|
||||
printk("Subscribe request sent\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void gatt_unsubscribe_long(enum bt_att_chan_opt opt)
|
||||
{
|
||||
int err;
|
||||
|
||||
UNSET_FLAG(flag_long_subscribed);
|
||||
sub_params_long.value_handle = long_chrc_handle;
|
||||
sub_params_long.chan_opt = opt;
|
||||
err = bt_gatt_unsubscribe(g_conn, &sub_params_long);
|
||||
if (err < 0) {
|
||||
FAIL("Failed to unsubscribe\n");
|
||||
} else {
|
||||
printk("Unsubscribe request sent\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
|
||||
if (err != 0) {
|
||||
FAIL("Scanning failed to start (err %d)\n", err);
|
||||
}
|
||||
|
||||
printk("Scanning successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
|
||||
if (err) {
|
||||
FAIL("Starting encryption procedure failed (%d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_encrypted);
|
||||
|
||||
while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
|
||||
k_sleep(K_MSEC(10));
|
||||
}
|
||||
|
||||
printk("EATT connected\n");
|
||||
}
|
||||
|
||||
static void test_main_none(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
gatt_discover(BT_ATT_CHAN_OPT_NONE);
|
||||
gatt_subscribe_short(BT_ATT_CHAN_OPT_NONE);
|
||||
gatt_subscribe_long(BT_ATT_CHAN_OPT_NONE);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
printk("Subscribed\n");
|
||||
|
||||
while (num_notifications < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
gatt_unsubscribe_short(BT_ATT_CHAN_OPT_NONE);
|
||||
gatt_unsubscribe_long(BT_ATT_CHAN_OPT_NONE);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Unsubscribed\n");
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_unenhanced(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
gatt_discover(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
gatt_subscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
gatt_subscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Subscribed\n");
|
||||
|
||||
while (num_notifications < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
gatt_unsubscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
gatt_unsubscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Unsubscribed\n");
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_enhanced(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
gatt_discover(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
gatt_subscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
gatt_subscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Subscribed\n");
|
||||
|
||||
while (num_notifications < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
gatt_unsubscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
gatt_unsubscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Unsubscribed\n");
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static void test_main_mixed(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
gatt_discover(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
gatt_subscribe_short(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
gatt_subscribe_long(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Subscribed\n");
|
||||
|
||||
while (num_notifications < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
gatt_unsubscribe_short(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
gatt_unsubscribe_long(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
WAIT_FOR_FLAG(flag_short_subscribed);
|
||||
WAIT_FOR_FLAG(flag_long_subscribed);
|
||||
|
||||
printk("Unsubscribed\n");
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_vcs[] = {
|
||||
{
|
||||
.test_id = "gatt_client_none",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_none,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_unenhanced",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_unenhanced,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_enhanced",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_enhanced,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_client_mixed",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_mixed,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_vcs);
|
||||
}
|
300
tests/bsim/bluetooth/host/gatt/notify/src/gatt_server_test.c
Normal file
300
tests/bsim/bluetooth/host/gatt/notify/src/gatt_server_test.c
Normal file
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_short_subscribe);
|
||||
CREATE_FLAG(flag_long_subscribe);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
|
||||
#define ARRAY_ITEM(i, _) i
|
||||
const uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
const uint8_t long_chrc_data[] = { LISTIFY(LONG_CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
g_conn = bt_conn_ref(conn);
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static ssize_t read_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset)
|
||||
{
|
||||
printk("Read short\n");
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, (void *)chrc_data,
|
||||
sizeof(chrc_data));
|
||||
}
|
||||
|
||||
static ssize_t read_long_test_chrc(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf,
|
||||
uint16_t len, uint16_t offset)
|
||||
{
|
||||
printk("Read long\n");
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, (void *)long_chrc_data,
|
||||
sizeof(long_chrc_data));
|
||||
}
|
||||
|
||||
static void short_subscribe(const struct bt_gatt_attr *attr, uint16_t value)
|
||||
{
|
||||
const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||
|
||||
if (notif_enabled) {
|
||||
SET_FLAG(flag_short_subscribe);
|
||||
}
|
||||
|
||||
printk("Short notifications %s\n", notif_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void long_subscribe(const struct bt_gatt_attr *attr, uint16_t value)
|
||||
{
|
||||
const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||
|
||||
if (notif_enabled) {
|
||||
SET_FLAG(flag_long_subscribe);
|
||||
}
|
||||
|
||||
printk("Long notifications %s\n", notif_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(test_svc, BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
|
||||
BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID,
|
||||
BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_READ, read_test_chrc, NULL, NULL),
|
||||
BT_GATT_CUD("Short test_svc format description", BT_GATT_PERM_READ),
|
||||
BT_GATT_CCC(short_subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
|
||||
BT_GATT_CHARACTERISTIC(TEST_LONG_CHRC_UUID,
|
||||
BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_READ, read_long_test_chrc, NULL, NULL),
|
||||
BT_GATT_CCC(long_subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
|
||||
|
||||
static volatile size_t num_notifications_sent;
|
||||
|
||||
static void notification_sent(struct bt_conn *conn, void *user_data)
|
||||
{
|
||||
size_t *length = user_data;
|
||||
|
||||
printk("Sent notification #%u with length %d\n", num_notifications_sent++, *length);
|
||||
}
|
||||
|
||||
static void short_notify(enum bt_att_chan_opt opt)
|
||||
{
|
||||
static size_t length = CHRC_SIZE;
|
||||
static struct bt_gatt_notify_params params = {
|
||||
.attr = &attr_test_svc[1],
|
||||
.data = chrc_data,
|
||||
.len = CHRC_SIZE,
|
||||
.func = notification_sent,
|
||||
.user_data = &length,
|
||||
.uuid = NULL,
|
||||
};
|
||||
int err;
|
||||
|
||||
params.chan_opt = opt;
|
||||
|
||||
do {
|
||||
err = bt_gatt_notify_cb(g_conn, ¶ms);
|
||||
|
||||
if (err == -ENOMEM) {
|
||||
k_sleep(K_MSEC(10));
|
||||
} else if (err) {
|
||||
FAIL("Short notify failed (err %d)\n", err);
|
||||
}
|
||||
} while (err);
|
||||
}
|
||||
|
||||
static void long_notify(enum bt_att_chan_opt opt)
|
||||
{
|
||||
static size_t length = LONG_CHRC_SIZE;
|
||||
static struct bt_gatt_notify_params params = {
|
||||
.attr = &attr_test_svc[5],
|
||||
.data = long_chrc_data,
|
||||
.len = LONG_CHRC_SIZE,
|
||||
.func = notification_sent,
|
||||
.user_data = &length,
|
||||
.uuid = NULL,
|
||||
};
|
||||
int err;
|
||||
|
||||
params.chan_opt = opt;
|
||||
|
||||
do {
|
||||
err = bt_gatt_notify_cb(g_conn, ¶ms);
|
||||
|
||||
if (err == -ENOMEM) {
|
||||
k_sleep(K_MSEC(10));
|
||||
} else if (err) {
|
||||
FAIL("Long notify failed (err %d)\n", err);
|
||||
}
|
||||
} while (err);
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
int err;
|
||||
const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
};
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err != 0) {
|
||||
FAIL("Advertising failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Advertising successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
printk("EATT connected\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_short_subscribe);
|
||||
WAIT_FOR_FLAG(flag_long_subscribe);
|
||||
}
|
||||
|
||||
static void test_main_none(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
|
||||
short_notify(BT_ATT_CHAN_OPT_NONE);
|
||||
long_notify(BT_ATT_CHAN_OPT_NONE);
|
||||
}
|
||||
|
||||
while (num_notifications_sent < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static void test_main_enhanced(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
|
||||
short_notify(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
long_notify(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
}
|
||||
|
||||
while (num_notifications_sent < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static void test_main_unenhanced(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
|
||||
short_notify(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
long_notify(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
}
|
||||
|
||||
while (num_notifications_sent < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static void test_main_mixed(void)
|
||||
{
|
||||
setup();
|
||||
|
||||
for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
|
||||
short_notify(BT_ATT_CHAN_OPT_UNENHANCED_ONLY);
|
||||
long_notify(BT_ATT_CHAN_OPT_ENHANCED_ONLY);
|
||||
}
|
||||
|
||||
while (num_notifications_sent < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_gatt_server[] = {
|
||||
{
|
||||
.test_id = "gatt_server_none",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_none,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_server_unenhanced",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_unenhanced,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_server_enhanced",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_enhanced,
|
||||
},
|
||||
{
|
||||
.test_id = "gatt_server_mixed",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main_mixed,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_gatt_server);
|
||||
}
|
21
tests/bsim/bluetooth/host/gatt/notify/src/main.c
Normal file
21
tests/bsim/bluetooth/host/gatt/notify/src/main.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bstests.h"
|
||||
|
||||
extern struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests);
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_gatt_server_install,
|
||||
test_gatt_client_install,
|
||||
NULL
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
38
tests/bsim/bluetooth/host/gatt/notify/test_scripts/_run_test.sh
Executable file
38
tests/bsim/bluetooth/host/gatt/notify/test_scripts/_run_test.sh
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
verbosity_level=2
|
||||
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}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_notify_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=${client_id}
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_notify_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=${server_id}
|
||||
|
||||
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
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_enhanced_enhanced" \
|
||||
server_id="gatt_server_enhanced" \
|
||||
client_id="gatt_client_enhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_enhanced_mixed" \
|
||||
server_id="gatt_server_enhanced" \
|
||||
client_id="gatt_client_mixed" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_enhanced_none" \
|
||||
server_id="gatt_server_enhanced" \
|
||||
client_id="gatt_client_none" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_enhanced_unenhanced" \
|
||||
server_id="gatt_server_enhanced" \
|
||||
client_id="gatt_client_unenhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_mixed_enhanced" \
|
||||
server_id="gatt_server_mixed" \
|
||||
client_id="gatt_client_enhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_mixed_mixed" \
|
||||
server_id="gatt_server_mixed" \
|
||||
client_id="gatt_client_mixed" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
10
tests/bsim/bluetooth/host/gatt/notify/test_scripts/gatt_notify_mixed_none.sh
Executable file
10
tests/bsim/bluetooth/host/gatt/notify/test_scripts/gatt_notify_mixed_none.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_mixed_none" \
|
||||
server_id="gatt_server_mixed" \
|
||||
client_id="gatt_client_none" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_mixed_unenhanced" \
|
||||
server_id="gatt_server_mixed" \
|
||||
client_id="gatt_client_unenhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_none_enhanced" \
|
||||
server_id="gatt_server_none" \
|
||||
client_id="gatt_client_enhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
10
tests/bsim/bluetooth/host/gatt/notify/test_scripts/gatt_notify_none_mixed.sh
Executable file
10
tests/bsim/bluetooth/host/gatt/notify/test_scripts/gatt_notify_none_mixed.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_none_mixed" \
|
||||
server_id="gatt_server_none" \
|
||||
client_id="gatt_client_mixed" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
10
tests/bsim/bluetooth/host/gatt/notify/test_scripts/gatt_notify_none_none.sh
Executable file
10
tests/bsim/bluetooth/host/gatt/notify/test_scripts/gatt_notify_none_none.sh
Executable file
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_none_none" \
|
||||
server_id="gatt_server_none" \
|
||||
client_id="gatt_client_none" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_none_unenhanced" \
|
||||
server_id="gatt_server_none" \
|
||||
client_id="gatt_client_unenhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_unenhanced_enhanced" \
|
||||
server_id="gatt_server_unenhanced" \
|
||||
client_id="gatt_client_enhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_unenhanced_mixed" \
|
||||
server_id="gatt_server_unenhanced" \
|
||||
client_id="gatt_client_mixed" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_unenhanced_none" \
|
||||
server_id="gatt_server_unenhanced" \
|
||||
client_id="gatt_client_none" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
|
||||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="gatt_notify_unenhanced_unenhanced" \
|
||||
server_id="gatt_server_unenhanced" \
|
||||
client_id="gatt_client_unenhanced" \
|
||||
$(dirname "${BASH_SOURCE[0]}")/_run_test.sh
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
# 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 REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_gatt)
|
||||
|
||||
FILE(GLOB app_sources src/*.c)
|
||||
target_sources(app PRIVATE ${app_sources} )
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
18
tests/bsim/bluetooth/host/gatt/notify_multiple/prj.conf
Normal file
18
tests/bsim/bluetooth/host/gatt/notify_multiple/prj.conf
Normal file
|
@ -0,0 +1,18 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_DEVICE_NAME="GATT tester"
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
|
||||
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_L2CAP_DYNAMIC_CHANNEL=y
|
||||
CONFIG_BT_L2CAP_ECRED=y
|
||||
CONFIG_BT_EATT=y
|
||||
CONFIG_BT_EATT_MAX=5
|
||||
CONFIG_BT_GATT_NOTIFY_MULTIPLE=y
|
||||
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_BT_TESTING=y
|
||||
CONFIG_BT_DEBUG_LOG=y
|
20
tests/bsim/bluetooth/host/gatt/notify_multiple/src/common.c
Normal file
20
tests/bsim/bluetooth/host/gatt/notify_multiple/src/common.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
void test_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
if (bst_result != Passed) {
|
||||
FAIL("test failed (not passed after %i seconds)\n", WAIT_TIME);
|
||||
}
|
||||
}
|
||||
|
||||
void test_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(WAIT_TIME);
|
||||
bst_result = In_progress;
|
||||
}
|
74
tests/bsim/bluetooth/host/gatt/notify_multiple/src/common.h
Normal file
74
tests/bsim/bluetooth/host/gatt/notify_multiple/src/common.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
/**
|
||||
* Common functions and helpers for BSIM GATT tests
|
||||
*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/bluetooth/uuid.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
#define WAIT_TIME (60 * 1e6) /*seconds*/
|
||||
|
||||
#define CREATE_FLAG(flag) static atomic_t flag = (atomic_t)false
|
||||
#define FORCE_FLAG(flag, val) (void)atomic_set(&flag, (atomic_t)val)
|
||||
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)true)
|
||||
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t)false)
|
||||
#define WAIT_FOR_FLAG(flag) \
|
||||
while (!(bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
#define WAIT_FOR_FLAG_UNSET(flag) \
|
||||
while ((bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
#define CHRC_SIZE 10
|
||||
#define LONG_CHRC_SIZE 40
|
||||
|
||||
#define TEST_SERVICE_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, \
|
||||
0x07, 0x08, 0x09, 0x00, 0x00)
|
||||
|
||||
#define TEST_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, \
|
||||
0x07, 0x08, 0x09, 0xFF, 0x00)
|
||||
|
||||
#define TEST_LONG_CHRC_UUID \
|
||||
BT_UUID_DECLARE_128(0x01, 0x23, 0x45, 0x67, 0x89, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, \
|
||||
0x07, 0x08, 0x09, 0xFF, 0x11)
|
||||
|
||||
void test_tick(bs_time_t HW_device_time);
|
||||
void test_init(void);
|
||||
|
||||
#define NOTIFICATION_COUNT 10
|
||||
BUILD_ASSERT(NOTIFICATION_COUNT % 2 == 0);
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_is_encrypted);
|
||||
CREATE_FLAG(flag_discover_complete);
|
||||
CREATE_FLAG(flag_write_complete);
|
||||
CREATE_FLAG(flag_subscribed_short);
|
||||
CREATE_FLAG(flag_subscribed_long);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
static uint16_t chrc_handle;
|
||||
static uint16_t long_chrc_handle;
|
||||
static uint16_t csf_handle;
|
||||
static struct bt_uuid *test_svc_uuid = TEST_SERVICE_UUID;
|
||||
|
||||
static void exchange_func(struct bt_conn *conn, uint8_t err, struct bt_gatt_exchange_params *params)
|
||||
{
|
||||
if (!err) {
|
||||
printk("MTU exchange done\n");
|
||||
} else {
|
||||
printk("MTU exchange failed (err %" PRIu8 ")\n", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
static struct bt_gatt_exchange_params exchange_params;
|
||||
|
||||
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
|
||||
|
||||
if (err != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
SET_FLAG(flag_is_connected);
|
||||
|
||||
exchange_params.func = exchange_func;
|
||||
err = bt_gatt_exchange_mtu(conn, &exchange_params);
|
||||
if (err) {
|
||||
printk("MTU exchange failed (err %d)", err);
|
||||
}
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
void security_changed(struct bt_conn *conn, bt_security_t level, enum bt_security_err err)
|
||||
{
|
||||
if (err) {
|
||||
FAIL("Encryption failer (%d)\n", err);
|
||||
} else if (level < BT_SECURITY_L2) {
|
||||
FAIL("Insufficient sec level (%d)\n", level);
|
||||
} else {
|
||||
SET_FLAG(flag_is_encrypted);
|
||||
}
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed,
|
||||
};
|
||||
|
||||
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 (g_conn != NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_HCI_ADV_IND && type != BT_HCI_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);
|
||||
|
||||
printk("Stopping scan\n");
|
||||
err = bt_le_scan_stop();
|
||||
if (err != 0) {
|
||||
FAIL("Could not stop scan: %d");
|
||||
return;
|
||||
}
|
||||
|
||||
err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT, &g_conn);
|
||||
if (err != 0) {
|
||||
FAIL("Could not connect to peer: %d", err);
|
||||
}
|
||||
}
|
||||
|
||||
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 == NULL) {
|
||||
if (chrc_handle == 0 || long_chrc_handle == 0) {
|
||||
FAIL("Did not discover chrc (%x) or long_chrc (%x)", chrc_handle,
|
||||
long_chrc_handle);
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_discover_complete);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("[ATTRIBUTE] handle %u\n", attr->handle);
|
||||
|
||||
if (params->type == BT_GATT_DISCOVER_PRIMARY &&
|
||||
bt_uuid_cmp(params->uuid, TEST_SERVICE_UUID) == 0) {
|
||||
printk("Found test service\n");
|
||||
params->uuid = NULL;
|
||||
params->start_handle = attr->handle + 1;
|
||||
params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
|
||||
const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
|
||||
|
||||
if (bt_uuid_cmp(chrc->uuid, TEST_CHRC_UUID) == 0) {
|
||||
printk("Found chrc\n");
|
||||
chrc_handle = chrc->value_handle;
|
||||
} else if (bt_uuid_cmp(chrc->uuid, TEST_LONG_CHRC_UUID) == 0) {
|
||||
printk("Found long_chrc\n");
|
||||
long_chrc_handle = chrc->value_handle;
|
||||
} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_CLIENT_FEATURES) == 0) {
|
||||
printk("Found csf\n");
|
||||
csf_handle = chrc->value_handle;
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static void gatt_discover(const struct bt_uuid *uuid, uint8_t type)
|
||||
{
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
int err;
|
||||
|
||||
printk("Discovering services and characteristics\n");
|
||||
|
||||
discover_params.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 = type;
|
||||
|
||||
UNSET_FLAG(flag_discover_complete);
|
||||
|
||||
err = bt_gatt_discover(g_conn, &discover_params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed(err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_discover_complete);
|
||||
printk("Discover complete\n");
|
||||
}
|
||||
|
||||
static void test_subscribed(struct bt_conn *conn,
|
||||
uint8_t err,
|
||||
struct bt_gatt_subscribe_params *params)
|
||||
{
|
||||
if (err) {
|
||||
FAIL("Subscribe failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
if (!params) {
|
||||
FAIL("params NULL\n");
|
||||
}
|
||||
|
||||
if (params->value_handle == chrc_handle) {
|
||||
FORCE_FLAG(flag_subscribed_short, (bool)params->value);
|
||||
printk("Subscribed to short characteristic\n");
|
||||
} else if (params->value_handle == long_chrc_handle) {
|
||||
FORCE_FLAG(flag_subscribed_long, (bool)params->value);
|
||||
printk("Subscribed to long characteristic\n");
|
||||
} else {
|
||||
FAIL("Unknown handle %d\n", params->value_handle);
|
||||
}
|
||||
}
|
||||
|
||||
static volatile size_t num_notifications;
|
||||
uint8_t test_notify(struct bt_conn *conn, struct bt_gatt_subscribe_params *params, const void *data,
|
||||
uint16_t length)
|
||||
{
|
||||
printk("Received notification #%u with length %d\n", num_notifications++, length);
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
static struct bt_gatt_discover_params disc_params_short;
|
||||
static struct bt_gatt_subscribe_params sub_params_short = {
|
||||
.notify = test_notify,
|
||||
.subscribe = test_subscribed,
|
||||
.ccc_handle = 0, /* Auto-discover CCC*/
|
||||
.disc_params = &disc_params_short, /* Auto-discover CCC */
|
||||
.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
.value = BT_GATT_CCC_NOTIFY,
|
||||
};
|
||||
static struct bt_gatt_discover_params disc_params_long;
|
||||
static struct bt_gatt_subscribe_params sub_params_long = {
|
||||
.notify = test_notify,
|
||||
.subscribe = test_subscribed,
|
||||
.ccc_handle = 0, /* Auto-discover CCC*/
|
||||
.disc_params = &disc_params_long, /* Auto-discover CCC */
|
||||
.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE,
|
||||
.value = BT_GATT_CCC_NOTIFY,
|
||||
};
|
||||
|
||||
static void write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
|
||||
{
|
||||
if (err != BT_ATT_ERR_SUCCESS) {
|
||||
FAIL("Write failed: 0x%02X\n", err);
|
||||
}
|
||||
|
||||
SET_FLAG(flag_write_complete);
|
||||
}
|
||||
|
||||
static void write_csf(void)
|
||||
{
|
||||
/* Client Supported Features Characteristic Value
|
||||
* Bit 0: Robust Caching
|
||||
* Bit 1: EATT
|
||||
* Bit 2: Multiple Handle Value Notifications
|
||||
*/
|
||||
static const uint8_t csf[] = { BIT(2) };
|
||||
static struct bt_gatt_write_params write_params = {
|
||||
.func = write_cb,
|
||||
.offset = 0,
|
||||
.data = csf,
|
||||
.length = sizeof(csf),
|
||||
};
|
||||
int err;
|
||||
|
||||
printk("Writing to Client Supported Features Characteristic\n");
|
||||
|
||||
write_params.handle = csf_handle;
|
||||
UNSET_FLAG(flag_write_complete);
|
||||
|
||||
err = bt_gatt_write(g_conn, &write_params);
|
||||
if (err) {
|
||||
FAIL("bt_gatt_write failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_write_complete);
|
||||
printk("Success\n");
|
||||
}
|
||||
|
||||
static void subscribe(struct bt_gatt_subscribe_params *params, bool subscribe)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (subscribe) {
|
||||
err = bt_gatt_subscribe(g_conn, params);
|
||||
} else {
|
||||
err = bt_gatt_unsubscribe(g_conn, params);
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
FAIL("Failed to %ssubscribe (err %d)\n", subscribe ? "un":"", err);
|
||||
} else {
|
||||
printk("%ssubscribe request sent\n", subscribe ? "un":"");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void test_main(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE, device_found);
|
||||
if (err != 0) {
|
||||
FAIL("Scanning failed to start (err %d)\n", err);
|
||||
}
|
||||
|
||||
printk("Scanning successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
|
||||
err = bt_conn_set_security(g_conn, BT_SECURITY_L2);
|
||||
if (err) {
|
||||
FAIL("Starting encryption procedure failed (%d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_encrypted);
|
||||
|
||||
while (bt_eatt_count(g_conn) < CONFIG_BT_EATT_MAX) {
|
||||
k_sleep(K_MSEC(10));
|
||||
}
|
||||
|
||||
printk("EATT connected\n");
|
||||
|
||||
gatt_discover(test_svc_uuid, BT_GATT_DISCOVER_PRIMARY);
|
||||
gatt_discover(BT_UUID_GATT_CLIENT_FEATURES, BT_GATT_DISCOVER_CHARACTERISTIC);
|
||||
write_csf();
|
||||
|
||||
sub_params_short.value_handle = chrc_handle;
|
||||
sub_params_long.value_handle = long_chrc_handle;
|
||||
|
||||
subscribe(&sub_params_short, true);
|
||||
subscribe(&sub_params_long, true);
|
||||
WAIT_FOR_FLAG(flag_subscribed_short);
|
||||
WAIT_FOR_FLAG(flag_subscribed_long);
|
||||
|
||||
printk("Subscribed\n");
|
||||
|
||||
while (num_notifications < NOTIFICATION_COUNT) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
subscribe(&sub_params_short, false);
|
||||
subscribe(&sub_params_long, false);
|
||||
WAIT_FOR_FLAG_UNSET(flag_subscribed_short);
|
||||
WAIT_FOR_FLAG_UNSET(flag_subscribed_long);
|
||||
|
||||
|
||||
printk("Unsubscribed\n");
|
||||
|
||||
PASS("GATT client Passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_vcs[] = {
|
||||
{
|
||||
.test_id = "gatt_client",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_vcs);
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
CREATE_FLAG(flag_is_connected);
|
||||
CREATE_FLAG(flag_short_subscribe);
|
||||
CREATE_FLAG(flag_long_subscribe);
|
||||
|
||||
static struct bt_conn *g_conn;
|
||||
|
||||
#define ARRAY_ITEM(i, _) i
|
||||
const uint8_t chrc_data[] = { LISTIFY(CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
const uint8_t long_chrc_data[] = { LISTIFY(LONG_CHRC_SIZE, ARRAY_ITEM, (,)) }; /* 1, 2, 3 ... */
|
||||
|
||||
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 != 0) {
|
||||
FAIL("Failed to connect to %s (%u)\n", addr, err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Connected to %s\n", addr);
|
||||
|
||||
g_conn = bt_conn_ref(conn);
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
char addr[BT_ADDR_LE_STR_LEN];
|
||||
|
||||
if (conn != g_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(g_conn);
|
||||
|
||||
g_conn = NULL;
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
};
|
||||
|
||||
static void short_subscribe(const struct bt_gatt_attr *attr, uint16_t value)
|
||||
{
|
||||
const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||
|
||||
if (notif_enabled) {
|
||||
SET_FLAG(flag_short_subscribe);
|
||||
}
|
||||
|
||||
printk("Short notifications %s\n", notif_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
static void long_subscribe(const struct bt_gatt_attr *attr, uint16_t value)
|
||||
{
|
||||
const bool notif_enabled = (value == BT_GATT_CCC_NOTIFY);
|
||||
|
||||
if (notif_enabled) {
|
||||
SET_FLAG(flag_long_subscribe);
|
||||
}
|
||||
|
||||
printk("Long notifications %s\n", notif_enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
BT_GATT_SERVICE_DEFINE(test_svc, BT_GATT_PRIMARY_SERVICE(TEST_SERVICE_UUID),
|
||||
BT_GATT_CHARACTERISTIC(TEST_CHRC_UUID,
|
||||
BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_READ, NULL, NULL, NULL),
|
||||
BT_GATT_CUD("Short test_svc format description", BT_GATT_PERM_READ),
|
||||
BT_GATT_CCC(short_subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
|
||||
BT_GATT_CHARACTERISTIC(TEST_LONG_CHRC_UUID,
|
||||
BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_READ,
|
||||
BT_GATT_PERM_READ, NULL, NULL, NULL),
|
||||
BT_GATT_CCC(long_subscribe, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE));
|
||||
|
||||
static volatile size_t num_notifications_sent;
|
||||
|
||||
static void notification_sent(struct bt_conn *conn, void *user_data)
|
||||
{
|
||||
printk("Sent notification #%u\n", num_notifications_sent++);
|
||||
}
|
||||
|
||||
static inline void multiple_notify(const struct bt_gatt_attr *attrs[2])
|
||||
{
|
||||
int err;
|
||||
static struct bt_gatt_notify_params params[] = {
|
||||
{
|
||||
.data = long_chrc_data,
|
||||
.len = LONG_CHRC_SIZE,
|
||||
.func = notification_sent,
|
||||
.uuid = NULL,
|
||||
},
|
||||
{
|
||||
.data = chrc_data,
|
||||
.len = CHRC_SIZE,
|
||||
.func = notification_sent,
|
||||
.uuid = NULL,
|
||||
},
|
||||
};
|
||||
params[0].attr = attrs[0];
|
||||
params[1].attr = attrs[1];
|
||||
|
||||
do {
|
||||
err = bt_gatt_notify_multiple(g_conn, ARRAY_SIZE(params), params);
|
||||
|
||||
if (err == -ENOMEM) {
|
||||
k_sleep(K_MSEC(10));
|
||||
} else if (err) {
|
||||
FAIL("multiple notify failed (err %d)\n", err);
|
||||
}
|
||||
} while (err);
|
||||
}
|
||||
|
||||
static void test_main(void)
|
||||
{
|
||||
int err;
|
||||
const struct bt_gatt_attr *attrs[2];
|
||||
const struct bt_data ad[] = {
|
||||
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
|
||||
};
|
||||
|
||||
err = bt_enable(NULL);
|
||||
if (err != 0) {
|
||||
FAIL("Bluetooth init failed (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Bluetooth initialized\n");
|
||||
|
||||
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
|
||||
if (err != 0) {
|
||||
FAIL("Advertising failed to start (err %d)\n", err);
|
||||
return;
|
||||
}
|
||||
|
||||
printk("Advertising successfully started\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
WAIT_FOR_FLAG(flag_short_subscribe);
|
||||
WAIT_FOR_FLAG(flag_long_subscribe);
|
||||
|
||||
/* Long characteristic [attr=value] */
|
||||
attrs[0] = bt_gatt_find_by_uuid(NULL, 0, TEST_LONG_CHRC_UUID);
|
||||
/* Short characteristic [attr=descriptor] */
|
||||
attrs[1] = &attr_test_svc[1];
|
||||
|
||||
for (int i = 0; i < NOTIFICATION_COUNT / 2; i++) {
|
||||
multiple_notify(attrs);
|
||||
}
|
||||
|
||||
while (num_notifications_sent < NOTIFICATION_COUNT / 2) {
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(1000));
|
||||
|
||||
if (num_notifications_sent != NOTIFICATION_COUNT) {
|
||||
FAIL("Unexpected notification callback value\n");
|
||||
}
|
||||
|
||||
PASS("GATT server passed\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_gatt_server[] = {
|
||||
{
|
||||
.test_id = "gatt_server",
|
||||
.test_post_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = test_main,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_gatt_server);
|
||||
}
|
21
tests/bsim/bluetooth/host/gatt/notify_multiple/src/main.c
Normal file
21
tests/bsim/bluetooth/host/gatt/notify_multiple/src/main.c
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bstests.h"
|
||||
|
||||
extern struct bst_test_list *test_gatt_server_install(struct bst_test_list *tests);
|
||||
extern struct bst_test_list *test_gatt_client_install(struct bst_test_list *tests);
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_gatt_server_install,
|
||||
test_gatt_client_install,
|
||||
NULL
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
42
tests/bsim/bluetooth/host/gatt/notify_multiple/test_scripts/_notify-debug.sh
Executable file
42
tests/bsim/bluetooth/host/gatt/notify_multiple/test_scripts/_notify-debug.sh
Executable file
|
@ -0,0 +1,42 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Usage:
|
||||
# one script instance per device, e.g. to run gdb on the client:
|
||||
# `_notify-debug.sh client debug`
|
||||
# `_notify-debug.sh server`
|
||||
# `_notify-debug.sh`
|
||||
#
|
||||
# GDB can be run on the two devices at the same time without issues, just append
|
||||
# `debug` when running the script.
|
||||
|
||||
|
||||
simulation_id="notify_multiple"
|
||||
verbosity_level=2
|
||||
process_ids=""; exit_code=0
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
if [[ $2 == "debug" ]]; then
|
||||
GDB_P="gdb --args "
|
||||
fi
|
||||
|
||||
if [[ $1 == "client" ]]; then
|
||||
$GDB_P ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_notify_multiple_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=gatt_client
|
||||
|
||||
elif [[ $1 == "server" ]]; then
|
||||
$GDB_P ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_notify_multiple_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=gatt_server
|
||||
|
||||
else
|
||||
./bs_2G4_phy_v1 -v=${verbosity_level} -s=${simulation_id} \
|
||||
-D=2 -sim_length=60e6 $@
|
||||
|
||||
fi
|
37
tests/bsim/bluetooth/host/gatt/notify_multiple/test_scripts/notify.sh
Executable file
37
tests/bsim/bluetooth/host/gatt/notify_multiple/test_scripts/notify.sh
Executable file
|
@ -0,0 +1,37 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
simulation_id="notify_multiple"
|
||||
verbosity_level=2
|
||||
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 120 $@ & process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_notify_multiple_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=gatt_client
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_notify_multiple_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=gatt_server
|
||||
|
||||
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
|
27
tests/bsim/bluetooth/host/gatt/settings/CMakeLists.txt
Normal file
27
tests/bsim/bluetooth/host/gatt/settings/CMakeLists.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
# 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 REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_gatt_settings)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/server.c
|
||||
src/client.c
|
||||
src/utils.c
|
||||
src/gatt_utils.c
|
||||
src/settings.c
|
||||
src/main.c
|
||||
)
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
26
tests/bsim/bluetooth/host/gatt/settings/prj.conf
Normal file
26
tests/bsim/bluetooth/host/gatt/settings/prj.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_DEVICE_NAME="GATT settings"
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_BT_TESTING=y
|
||||
|
||||
CONFIG_BT_AUTO_PHY_UPDATE=n
|
||||
CONFIG_BT_AUTO_DATA_LEN_UPDATE=n
|
||||
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
CONFIG_BT_GATT_DYNAMIC_DB=y
|
||||
CONFIG_BT_GATT_CACHING=y
|
||||
|
||||
CONFIG_SETTINGS=y
|
||||
CONFIG_SETTINGS_CUSTOM=y
|
||||
CONFIG_BT_SETTINGS=y
|
||||
|
||||
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
|
||||
CONFIG_BT_GATT_AUTO_RESUBSCRIBE=n
|
||||
|
||||
CONFIG_BT_PRIVACY=n
|
26
tests/bsim/bluetooth/host/gatt/settings/prj_2.conf
Normal file
26
tests/bsim/bluetooth/host/gatt/settings/prj_2.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_DEVICE_NAME="GATT settings"
|
||||
|
||||
CONFIG_LOG=y
|
||||
CONFIG_ASSERT=y
|
||||
CONFIG_BT_TESTING=y
|
||||
|
||||
CONFIG_BT_AUTO_PHY_UPDATE=n
|
||||
CONFIG_BT_AUTO_DATA_LEN_UPDATE=n
|
||||
CONFIG_BT_GAP_AUTO_UPDATE_CONN_PARAMS=n
|
||||
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
CONFIG_BT_GATT_DYNAMIC_DB=y
|
||||
CONFIG_BT_GATT_CACHING=y
|
||||
|
||||
CONFIG_SETTINGS=y
|
||||
CONFIG_SETTINGS_CUSTOM=y
|
||||
CONFIG_BT_SETTINGS=y
|
||||
|
||||
CONFIG_BT_GATT_AUTO_DISCOVER_CCC=y
|
||||
CONFIG_BT_GATT_AUTO_RESUBSCRIBE=n
|
||||
|
||||
CONFIG_BT_PRIVACY=y
|
172
tests/bsim/bluetooth/host/gatt/settings/src/client.c
Normal file
172
tests/bsim/bluetooth/host/gatt/settings/src/client.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "gatt_utils.h"
|
||||
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/toolchain/gcc.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void client_round_0(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
printk("start round 0...........\n");
|
||||
|
||||
conn = connect_as_peripheral();
|
||||
printk("connected: conn %p\n", conn);
|
||||
|
||||
gatt_discover();
|
||||
activate_robust_caching();
|
||||
/* subscribe to the SC indication, so we don't have to ATT read to
|
||||
* become change-aware.
|
||||
*/
|
||||
gatt_subscribe_to_service_changed(true);
|
||||
read_test_char(true);
|
||||
|
||||
/* We should normally wait until we are bonded to write the CCC / CF
|
||||
* characteristics, but here we bond after the fact on purpose, to
|
||||
* simulate a client that has this exact behavior.
|
||||
* The CCC and CF should still persist on reboot.
|
||||
*/
|
||||
wait_bonded();
|
||||
|
||||
disconnect(conn);
|
||||
}
|
||||
|
||||
void client_round_1(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
printk("start round 1...........\n");
|
||||
|
||||
conn = connect_as_peripheral();
|
||||
printk("connected: conn %p\n", conn);
|
||||
wait_secured();
|
||||
|
||||
/* server should remember we are change-aware */
|
||||
read_test_char(true);
|
||||
|
||||
disconnect(conn);
|
||||
}
|
||||
|
||||
void client_round_2(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
printk("start round 2...........\n");
|
||||
|
||||
conn = connect_as_peripheral();
|
||||
printk("connected: conn %p\n", conn);
|
||||
wait_secured();
|
||||
|
||||
/* We are change-unaware. wait until the Service Changed indication is
|
||||
* received, that should then make us change-aware.
|
||||
*/
|
||||
wait_for_sc_indication();
|
||||
read_test_char(true);
|
||||
|
||||
/* We sleep just enough so that the server's `delayed store` work item
|
||||
* is executed. We still trigger a disconnect, even though the server
|
||||
* device will be unresponsive for this round.
|
||||
*/
|
||||
k_sleep(K_MSEC(CONFIG_BT_SETTINGS_DELAYED_STORE_MS));
|
||||
|
||||
disconnect(conn);
|
||||
}
|
||||
|
||||
void client_round_3(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
printk("start round 3...........\n");
|
||||
|
||||
conn = connect_as_peripheral();
|
||||
printk("connected: conn %p\n", conn);
|
||||
wait_secured();
|
||||
|
||||
/* server should remember we are change-aware */
|
||||
read_test_char(true);
|
||||
|
||||
/* Unsubscribe from the SC indication.
|
||||
*
|
||||
* In the next round, we will be change-unaware, so the first ATT read
|
||||
* will fail, but the second one will succeed and we will be marked as
|
||||
* change-aware again.
|
||||
*/
|
||||
gatt_subscribe_to_service_changed(false);
|
||||
|
||||
disconnect(conn);
|
||||
}
|
||||
|
||||
void client_round_4(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
printk("start round 4...........\n");
|
||||
|
||||
conn = connect_as_peripheral();
|
||||
printk("connected: conn %p\n", conn);
|
||||
wait_secured();
|
||||
|
||||
/* GATT DB has changed again.
|
||||
* Before disc: svc1
|
||||
* After disc: svc1 + svc2
|
||||
* At boot: svc1
|
||||
* Expect a failure on the first read of the same GATT handle.
|
||||
*/
|
||||
read_test_char(false);
|
||||
read_test_char(true);
|
||||
|
||||
disconnect(conn);
|
||||
}
|
||||
|
||||
void client_round_5(void)
|
||||
{
|
||||
printk("start round 5...........\n");
|
||||
printk("don't need to do anything, central will "
|
||||
"not connect to us\n");
|
||||
}
|
||||
|
||||
void client_round_6(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
printk("start round 6...........\n");
|
||||
conn = connect_as_peripheral();
|
||||
printk("connected: conn %p\n", conn);
|
||||
wait_secured();
|
||||
|
||||
/* GATT DB has changed again.
|
||||
* Expect a failure on the first read of the same GATT handle.
|
||||
*/
|
||||
read_test_char(false);
|
||||
read_test_char(true);
|
||||
|
||||
disconnect(conn);
|
||||
}
|
||||
|
||||
void client_procedure(void)
|
||||
{
|
||||
bt_enable(NULL);
|
||||
settings_load();
|
||||
|
||||
client_round_0();
|
||||
client_round_1();
|
||||
client_round_2();
|
||||
client_round_3();
|
||||
client_round_4();
|
||||
client_round_5();
|
||||
client_round_6();
|
||||
|
||||
PASS("PASS\n");
|
||||
}
|
373
tests/bsim/bluetooth/host/gatt/settings/src/gatt_utils.c
Normal file
373
tests/bsim/bluetooth/host/gatt/settings/src/gatt_utils.c
Normal file
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "argparse.h"
|
||||
#include "bs_pc_backchannel.h"
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/gatt.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
|
||||
/* Custom Service Variables */
|
||||
static struct bt_uuid_128 test_svc_uuid = BT_UUID_INIT_128(
|
||||
0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
||||
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
||||
|
||||
static struct bt_uuid_128 test_svc_uuid_2 = BT_UUID_INIT_128(
|
||||
0xf1, 0xdd, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
||||
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
||||
|
||||
static struct bt_uuid_128 test_chrc_uuid = BT_UUID_INIT_128(
|
||||
0xf2, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12,
|
||||
0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12);
|
||||
|
||||
static uint8_t test_value[] = { 'T', 'e', 's', 't', '\0' };
|
||||
|
||||
DEFINE_FLAG(flag_client_read);
|
||||
|
||||
static ssize_t read_test(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
void *buf, uint16_t len, uint16_t offset)
|
||||
{
|
||||
const char *value = attr->user_data;
|
||||
|
||||
printk("Client has read from test char\n");
|
||||
SET_FLAG(flag_client_read);
|
||||
|
||||
return bt_gatt_attr_read(conn, attr, buf, len, offset, value,
|
||||
strlen(value));
|
||||
}
|
||||
|
||||
void wait_for_client_read(void)
|
||||
{
|
||||
WAIT_FOR_FLAG(flag_client_read);
|
||||
}
|
||||
|
||||
static ssize_t write_test(struct bt_conn *conn, const struct bt_gatt_attr *attr,
|
||||
const void *buf, uint16_t len, uint16_t offset,
|
||||
uint8_t flags)
|
||||
{
|
||||
uint8_t *value = attr->user_data;
|
||||
|
||||
printk("Client has written to test char\n");
|
||||
|
||||
if (offset + len > sizeof(test_value)) {
|
||||
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
||||
}
|
||||
|
||||
memcpy(value + offset, buf, len);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static struct bt_gatt_attr test_attrs[] = {
|
||||
/* Vendor Primary Service Declaration */
|
||||
BT_GATT_PRIMARY_SERVICE(&test_svc_uuid),
|
||||
|
||||
BT_GATT_CHARACTERISTIC(&test_chrc_uuid.uuid,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
||||
BT_GATT_PERM_READ |
|
||||
BT_GATT_PERM_WRITE,
|
||||
read_test, write_test, test_value),
|
||||
};
|
||||
|
||||
static struct bt_gatt_attr test_attrs_2[] = {
|
||||
/* Vendor Primary Service Declaration */
|
||||
BT_GATT_PRIMARY_SERVICE(&test_svc_uuid_2),
|
||||
|
||||
BT_GATT_CHARACTERISTIC(&test_chrc_uuid.uuid,
|
||||
BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
|
||||
BT_GATT_PERM_READ_ENCRYPT |
|
||||
BT_GATT_PERM_WRITE_ENCRYPT,
|
||||
read_test, write_test, test_value),
|
||||
};
|
||||
|
||||
static struct bt_gatt_service test_svc = BT_GATT_SERVICE(test_attrs);
|
||||
static struct bt_gatt_service test_svc_2 = BT_GATT_SERVICE(test_attrs_2);
|
||||
|
||||
void gatt_register_service_1(void)
|
||||
{
|
||||
int err = bt_gatt_service_register(&test_svc);
|
||||
|
||||
__ASSERT(!err, "Failed to register GATT service (err %d)\n", err);
|
||||
}
|
||||
|
||||
void gatt_register_service_2(void)
|
||||
{
|
||||
/* This service is only used to trigger a GATT DB change.
|
||||
* No reads or writes will be attempted.
|
||||
*/
|
||||
int err = bt_gatt_service_register(&test_svc_2);
|
||||
|
||||
__ASSERT(!err, "Failed to register GATT service (err %d)\n", err);
|
||||
}
|
||||
|
||||
/* We need to discover:
|
||||
* - Dynamic service
|
||||
* - Client Features (to set robust caching)
|
||||
* - Service Changed (to sub to indications)
|
||||
*/
|
||||
enum GATT_HANDLES {
|
||||
CLIENT_FEATURES,
|
||||
SERVICE_CHANGED,
|
||||
TEST_CHAR,
|
||||
NUM_HANDLES,
|
||||
};
|
||||
|
||||
uint16_t gatt_handles[NUM_HANDLES] = {0};
|
||||
|
||||
DEFINE_FLAG(flag_discovered);
|
||||
|
||||
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 == NULL) {
|
||||
for (int i = 0; i < ARRAY_SIZE(gatt_handles); i++) {
|
||||
printk("handle[%d] = 0x%x\n", i, gatt_handles[i]);
|
||||
|
||||
if (gatt_handles[i] == 0) {
|
||||
FAIL("Did not discover all characteristics\n");
|
||||
}
|
||||
}
|
||||
|
||||
(void)memset(params, 0, sizeof(*params));
|
||||
|
||||
SET_FLAG(flag_discovered);
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
if (params->type == BT_GATT_DISCOVER_PRIMARY &&
|
||||
bt_uuid_cmp(params->uuid, &test_svc_uuid.uuid) == 0) {
|
||||
printk("Found test service\n");
|
||||
params->uuid = NULL;
|
||||
params->start_handle = attr->handle + 1;
|
||||
params->type = BT_GATT_DISCOVER_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(conn, params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed (err %d)\n", err);
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_STOP;
|
||||
} else if (params->type == BT_GATT_DISCOVER_CHARACTERISTIC) {
|
||||
const struct bt_gatt_chrc *chrc = (struct bt_gatt_chrc *)attr->user_data;
|
||||
|
||||
if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_CLIENT_FEATURES) == 0) {
|
||||
printk("Found client supported features\n");
|
||||
gatt_handles[CLIENT_FEATURES] = chrc->value_handle;
|
||||
|
||||
} else if (bt_uuid_cmp(chrc->uuid, BT_UUID_GATT_SC) == 0) {
|
||||
printk("Found service changed\n");
|
||||
gatt_handles[SERVICE_CHANGED] = chrc->value_handle;
|
||||
|
||||
} else if (bt_uuid_cmp(chrc->uuid, &test_chrc_uuid.uuid) == 0) {
|
||||
printk("Found test characteristic\n");
|
||||
gatt_handles[TEST_CHAR] = chrc->value_handle;
|
||||
}
|
||||
}
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
DEFINE_FLAG(flag_sc_indicated);
|
||||
static uint8_t sc_indicated(struct bt_conn *conn,
|
||||
struct bt_gatt_subscribe_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
if (!data) {
|
||||
params->value_handle = 0U;
|
||||
return BT_GATT_ITER_STOP;
|
||||
}
|
||||
|
||||
printk("SC received\n");
|
||||
SET_FLAG(flag_sc_indicated);
|
||||
|
||||
return BT_GATT_ITER_CONTINUE;
|
||||
}
|
||||
|
||||
void wait_for_sc_indication(void)
|
||||
{
|
||||
WAIT_FOR_FLAG(flag_sc_indicated);
|
||||
}
|
||||
|
||||
DEFINE_FLAG(flag_sc_subscribed);
|
||||
static void sc_subscribed(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_subscribe_params *params)
|
||||
{
|
||||
if (params->value) {
|
||||
printk("SC subscribed\n");
|
||||
SET_FLAG(flag_sc_subscribed);
|
||||
} else {
|
||||
printk("SC unsubscribed\n");
|
||||
UNSET_FLAG(flag_sc_subscribed);
|
||||
}
|
||||
}
|
||||
|
||||
static struct bt_gatt_discover_params disc_params;
|
||||
static struct bt_gatt_subscribe_params subscribe_params;
|
||||
void gatt_subscribe_to_service_changed(bool subscribe)
|
||||
{
|
||||
int err;
|
||||
|
||||
subscribe_params.value_handle = gatt_handles[SERVICE_CHANGED];
|
||||
subscribe_params.notify = sc_indicated;
|
||||
subscribe_params.subscribe = sc_subscribed;
|
||||
|
||||
if (subscribe) {
|
||||
/* Use the BT_GATT_AUTO_DISCOVER_CCC feature */
|
||||
subscribe_params.ccc_handle = 0;
|
||||
subscribe_params.disc_params = &disc_params,
|
||||
subscribe_params.value = BT_GATT_CCC_INDICATE;
|
||||
subscribe_params.end_handle = BT_ATT_LAST_ATTRIBUTE_HANDLE;
|
||||
|
||||
err = bt_gatt_subscribe(get_conn(), &subscribe_params);
|
||||
WAIT_FOR_FLAG(flag_sc_subscribed);
|
||||
} else {
|
||||
/* Params are already set to the correct values by the previous
|
||||
* call of this fn.
|
||||
*/
|
||||
err = bt_gatt_unsubscribe(get_conn(), &subscribe_params);
|
||||
WAIT_FOR_FLAG_UNSET(flag_sc_subscribed);
|
||||
}
|
||||
|
||||
if (err != 0) {
|
||||
FAIL("Subscription failed(err %d)\n", err);
|
||||
} else {
|
||||
printk("%subscribed %s SC indications\n",
|
||||
subscribe ? "S" : "Uns",
|
||||
subscribe ? "to" : "from");
|
||||
}
|
||||
}
|
||||
|
||||
void gatt_discover(void)
|
||||
{
|
||||
static struct bt_gatt_discover_params discover_params;
|
||||
int err;
|
||||
|
||||
printk("Discovering services and characteristics\n");
|
||||
UNSET_FLAG(flag_discovered);
|
||||
|
||||
discover_params.uuid = NULL;
|
||||
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_CHARACTERISTIC;
|
||||
|
||||
err = bt_gatt_discover(get_conn(), &discover_params);
|
||||
if (err != 0) {
|
||||
FAIL("Discover failed(err %d)\n", err);
|
||||
}
|
||||
|
||||
WAIT_FOR_FLAG(flag_discovered);
|
||||
printk("Discover complete\n");
|
||||
}
|
||||
|
||||
DEFINE_FLAG(flag_written);
|
||||
|
||||
static void write_cb(struct bt_conn *conn, uint8_t err, struct bt_gatt_write_params *params)
|
||||
{
|
||||
if (err != BT_ATT_ERR_SUCCESS) {
|
||||
FAIL("Write failed: 0x%02X\n", err);
|
||||
}
|
||||
|
||||
SET_FLAG(flag_written);
|
||||
}
|
||||
|
||||
#define CF_BIT_ROBUST_CACHING 0
|
||||
void activate_robust_caching(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
static const uint8_t csf = BIT(CF_BIT_ROBUST_CACHING);
|
||||
static struct bt_gatt_write_params write_params = {
|
||||
.func = write_cb,
|
||||
.offset = 0,
|
||||
.data = &csf,
|
||||
.length = sizeof(csf),
|
||||
};
|
||||
|
||||
write_params.handle = gatt_handles[CLIENT_FEATURES];
|
||||
|
||||
UNSET_FLAG(flag_written);
|
||||
err = bt_gatt_write(get_conn(), &write_params);
|
||||
|
||||
__ASSERT(!err, "Failed to enable robust caching\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_written);
|
||||
printk("Robust caching enabled\n");
|
||||
}
|
||||
|
||||
DEFINE_FLAG(flag_read);
|
||||
|
||||
static uint8_t _expect_success(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
/* printk("GATT read cb: err 0x%02X\n", err); */
|
||||
__ASSERT(err == 0, "Failed to read: err 0x%x\n", err);
|
||||
|
||||
SET_FLAG(flag_read);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t _expect_out_of_sync_cb(struct bt_conn *conn, uint8_t err,
|
||||
struct bt_gatt_read_params *params,
|
||||
const void *data, uint16_t length)
|
||||
{
|
||||
/* printk("GATT read cb: err 0x%02X\n", err); */
|
||||
__ASSERT(err == BT_ATT_ERR_DB_OUT_OF_SYNC,
|
||||
"Didn't get expected error code: err 0x%x\n", err);
|
||||
|
||||
SET_FLAG(flag_read);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_char(uint16_t handle, bool expect_success)
|
||||
{
|
||||
int err;
|
||||
|
||||
struct bt_gatt_read_params read_params = {
|
||||
.handle_count = 1,
|
||||
.single = {
|
||||
.handle = handle,
|
||||
.offset = 0,
|
||||
},
|
||||
};
|
||||
|
||||
if (expect_success) {
|
||||
read_params.func = _expect_success;
|
||||
} else {
|
||||
read_params.func = _expect_out_of_sync_cb;
|
||||
}
|
||||
|
||||
UNSET_FLAG(flag_read);
|
||||
|
||||
err = bt_gatt_read(get_conn(), &read_params);
|
||||
__ASSERT(!err, "Failed to read char\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_read);
|
||||
}
|
||||
|
||||
void read_test_char(bool expect_success)
|
||||
{
|
||||
read_char(gatt_handles[TEST_CHAR], expect_success);
|
||||
}
|
||||
|
||||
void gatt_clear_flags(void)
|
||||
{
|
||||
UNSET_FLAG(flag_client_read);
|
||||
UNSET_FLAG(flag_discovered);
|
||||
UNSET_FLAG(flag_sc_indicated);
|
||||
UNSET_FLAG(flag_sc_subscribed);
|
||||
UNSET_FLAG(flag_written);
|
||||
UNSET_FLAG(flag_read);
|
||||
}
|
17
tests/bsim/bluetooth/host/gatt/settings/src/gatt_utils.h
Normal file
17
tests/bsim/bluetooth/host/gatt/settings/src/gatt_utils.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
void gatt_register_service_1(void);
|
||||
void gatt_register_service_2(void);
|
||||
void gatt_discover(void);
|
||||
void activate_robust_caching(void);
|
||||
void read_test_char(bool expect_success);
|
||||
void wait_for_client_read(void);
|
||||
void gatt_subscribe_to_service_changed(bool subscribe);
|
||||
void wait_for_sc_indication(void);
|
||||
void gatt_clear_flags(void);
|
211
tests/bsim/bluetooth/host/gatt/settings/src/main.c
Normal file
211
tests/bsim/bluetooth/host/gatt/settings/src/main.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "main.h"
|
||||
#include "argparse.h"
|
||||
#include "bs_pc_backchannel.h"
|
||||
#include "bstests.h"
|
||||
|
||||
#include <zephyr/sys/__assert.h>
|
||||
|
||||
void server_procedure(void);
|
||||
void client_procedure(void);
|
||||
|
||||
#define BS_SECONDS(dur_sec) ((bs_time_t)dur_sec * USEC_PER_SEC)
|
||||
#define TEST_TIMEOUT_SIMULATED BS_SECONDS(30)
|
||||
|
||||
static int test_round;
|
||||
static int final_round;
|
||||
static char *settings_file;
|
||||
|
||||
int get_test_round(void)
|
||||
{
|
||||
return test_round;
|
||||
}
|
||||
|
||||
bool is_final_round(void)
|
||||
{
|
||||
return test_round == final_round;
|
||||
}
|
||||
|
||||
char *get_settings_file(void)
|
||||
{
|
||||
return settings_file;
|
||||
}
|
||||
|
||||
static void test_args(int argc, char **argv)
|
||||
{
|
||||
__ASSERT(argc == 3, "Please specify only 3 test arguments\n");
|
||||
|
||||
test_round = atol(argv[0]);
|
||||
final_round = atol(argv[1]);
|
||||
settings_file = argv[2];
|
||||
|
||||
bs_trace_raw(0, "Test round %u\n", test_round);
|
||||
bs_trace_raw(0, "Final round %u\n", final_round);
|
||||
}
|
||||
|
||||
void test_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
bs_trace_debug_time(0, "Simulation ends now.\n");
|
||||
if (bst_result != Passed) {
|
||||
bst_result = Failed;
|
||||
bs_trace_error("Test did not pass before simulation ended.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void test_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(TEST_TIMEOUT_SIMULATED);
|
||||
bst_result = In_progress;
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_to_add[] = {
|
||||
{
|
||||
.test_id = "server",
|
||||
.test_pre_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = server_procedure,
|
||||
.test_args_f = test_args,
|
||||
},
|
||||
{
|
||||
.test_id = "client",
|
||||
.test_pre_init_f = test_init,
|
||||
.test_tick_f = test_tick,
|
||||
.test_main_f = client_procedure,
|
||||
.test_args_f = test_args,
|
||||
},
|
||||
BSTEST_END_MARKER,
|
||||
};
|
||||
|
||||
static struct bst_test_list *install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_to_add);
|
||||
};
|
||||
|
||||
bst_test_install_t test_installers[] = { install, NULL };
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
||||
|
||||
|
||||
void backchannel_init(void)
|
||||
{
|
||||
uint device_number = get_device_nbr();
|
||||
uint channel_numbers[2] = { 0, 0, };
|
||||
uint device_numbers[2];
|
||||
uint num_ch;
|
||||
uint *ch;
|
||||
|
||||
/* No backchannels to next/prev device if only device */
|
||||
if (get_test_round() == 0 && is_final_round()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Each `server` round/instance gets a connection to the previous and to
|
||||
* the next instance in the chain. It waits until it is signalled by the
|
||||
* previous instance, then runs its test procedure and finally signals
|
||||
* the next instance in the chain.
|
||||
*
|
||||
* The two ends of the chain get only one channel, hence the difference
|
||||
* in handling.
|
||||
*/
|
||||
|
||||
if (get_test_round() == 0) {
|
||||
/* send only */
|
||||
device_numbers[0] = get_device_nbr() + 1;
|
||||
num_ch = 1;
|
||||
|
||||
} else if (is_final_round()) {
|
||||
/* receive only */
|
||||
device_numbers[0] = get_device_nbr() - 1;
|
||||
num_ch = 1;
|
||||
|
||||
} else {
|
||||
/* send signal */
|
||||
device_numbers[0] = get_device_nbr() + 1;
|
||||
/* receive signal */
|
||||
device_numbers[1] = get_device_nbr() - 1;
|
||||
num_ch = 2;
|
||||
}
|
||||
|
||||
printk("Opening backchannels\n");
|
||||
ch = bs_open_back_channel(device_number, device_numbers,
|
||||
channel_numbers, num_ch);
|
||||
if (!ch) {
|
||||
FAIL("Unable to open backchannel\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define MSG_SIZE 1
|
||||
|
||||
void backchannel_sync_send(uint channel)
|
||||
{
|
||||
uint8_t sync_msg[MSG_SIZE] = { get_device_nbr() };
|
||||
|
||||
printk("Sending sync\n");
|
||||
bs_bc_send_msg(channel, sync_msg, ARRAY_SIZE(sync_msg));
|
||||
}
|
||||
|
||||
void backchannel_sync_wait(uint channel)
|
||||
{
|
||||
uint8_t sync_msg[MSG_SIZE];
|
||||
|
||||
while (true) {
|
||||
if (bs_bc_is_msg_received(channel) > 0) {
|
||||
bs_bc_receive_msg(channel, sync_msg,
|
||||
ARRAY_SIZE(sync_msg));
|
||||
if (sync_msg[0] != get_device_nbr()) {
|
||||
/* Received a message from another device, exit */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
k_sleep(K_MSEC(1));
|
||||
}
|
||||
|
||||
printk("Sync received\n");
|
||||
}
|
||||
|
||||
/* We can't really kill the device/process without borking the bsim
|
||||
* backchannels, so the next best thing is stopping all threads from processing,
|
||||
* thus stopping the Bluetooth host from processing the disconnect event (or any
|
||||
* event, really) coming from the link-layer.
|
||||
*/
|
||||
static void stop_all_threads(void)
|
||||
{
|
||||
/* promote to highest priority */
|
||||
k_thread_priority_set(k_current_get(), K_HIGHEST_THREAD_PRIO);
|
||||
/* busy-wait loop */
|
||||
for (;;) {
|
||||
k_busy_wait(1000);
|
||||
k_yield();
|
||||
}
|
||||
}
|
||||
|
||||
void signal_next_test_round(void)
|
||||
{
|
||||
if (!is_final_round()) {
|
||||
backchannel_sync_send(0);
|
||||
}
|
||||
|
||||
PASS("round %d over\n", get_test_round());
|
||||
stop_all_threads();
|
||||
}
|
||||
|
||||
void wait_for_round_start(void)
|
||||
{
|
||||
backchannel_init();
|
||||
|
||||
if (is_final_round()) {
|
||||
backchannel_sync_wait(0);
|
||||
} else if (get_test_round() != 0) {
|
||||
backchannel_sync_wait(1);
|
||||
}
|
||||
}
|
14
tests/bsim/bluetooth/host/gatt/settings/src/main.h
Normal file
14
tests/bsim/bluetooth/host/gatt/settings/src/main.h
Normal file
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
char *get_settings_file(void);
|
||||
int get_test_round(void);
|
||||
bool is_final_round(void);
|
||||
|
||||
void signal_next_test_round(void);
|
||||
void wait_for_round_start(void);
|
208
tests/bsim/bluetooth/host/gatt/settings/src/server.c
Normal file
208
tests/bsim/bluetooth/host/gatt/settings/src/server.c
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "main.h"
|
||||
#include "gatt_utils.h"
|
||||
|
||||
#include <zephyr/bluetooth/addr.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include <zephyr/toolchain/gcc.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
void set_public_addr(void)
|
||||
{
|
||||
bt_addr_le_t addr = {BT_ADDR_LE_RANDOM,
|
||||
{{0x0A, 0x89, 0x67, 0x45, 0x23, 0xC1}}};
|
||||
bt_id_create(&addr, NULL);
|
||||
}
|
||||
|
||||
void server_round_0(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
conn = connect_as_central();
|
||||
wait_for_client_read();
|
||||
|
||||
printk("bonding\n");
|
||||
bond(conn);
|
||||
}
|
||||
|
||||
void server_round_1(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
/* Wait for GATT DB hash to complete. */
|
||||
k_sleep(K_SECONDS(2));
|
||||
conn = connect_as_central();
|
||||
printk("encrypting\n");
|
||||
set_security(conn, BT_SECURITY_L2);
|
||||
|
||||
wait_for_client_read();
|
||||
wait_disconnected();
|
||||
|
||||
printk("register second service, peer will be change-unaware\n");
|
||||
gatt_register_service_2();
|
||||
/* on-disk hash will be different when round 2 (and round 4)
|
||||
* start, the peer will be marked as change-unaware
|
||||
*/
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
void server_round_2(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
conn = connect_as_central();
|
||||
printk("encrypting\n");
|
||||
set_security(conn, BT_SECURITY_L2);
|
||||
|
||||
wait_for_client_read();
|
||||
|
||||
/* Kill the power before the graceful disconnect, to make sure
|
||||
* that the change-aware status has been written correctly to
|
||||
* NVS. We still have to wait for the delayed work to be run.
|
||||
*/
|
||||
k_sleep(K_MSEC(CONFIG_BT_SETTINGS_DELAYED_STORE_MS));
|
||||
}
|
||||
|
||||
void server_round_3(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
conn = connect_as_central();
|
||||
printk("encrypting\n");
|
||||
set_security(conn, BT_SECURITY_L2);
|
||||
|
||||
wait_for_client_read();
|
||||
wait_disconnected();
|
||||
|
||||
printk("register second service, peer will be change-unaware\n");
|
||||
gatt_register_service_2();
|
||||
/* on-disk hash will be different when round 2 (and round 4)
|
||||
* start, the peer will be marked as change-unaware
|
||||
*/
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
void server_round_4(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
conn = connect_as_central();
|
||||
printk("encrypting\n");
|
||||
set_security(conn, BT_SECURITY_L2);
|
||||
|
||||
wait_for_client_read();
|
||||
wait_disconnected();
|
||||
}
|
||||
|
||||
void server_round_5(void)
|
||||
{
|
||||
gatt_register_service_2();
|
||||
|
||||
/* sleep long enough to ensure the DB hash is stored to disk, but short
|
||||
* enough to make sure the delayed storage work item is not executed.
|
||||
*/
|
||||
k_sleep(K_MSEC(100));
|
||||
}
|
||||
|
||||
void server_round_6(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
gatt_register_service_2();
|
||||
|
||||
conn = connect_as_central();
|
||||
printk("encrypting\n");
|
||||
set_security(conn, BT_SECURITY_L2);
|
||||
|
||||
wait_for_client_read();
|
||||
wait_disconnected();
|
||||
}
|
||||
|
||||
/* What is being tested: since this deals with settings it's not the rounds
|
||||
* themselves, but rather the transitions that test expected behavior.
|
||||
*
|
||||
* Round 0 -> 1: test CCC / CF values written before bonding are stored to NVS
|
||||
* if the server reboots before disconnecting.
|
||||
*
|
||||
* Round 1 -> 2: test change-awareness is updated if GATT DB changes _after_ the
|
||||
* peer has disconnected. In round 2 we also make sure we receive the Service
|
||||
* Changed indication.
|
||||
*
|
||||
* Round 2 -> 3: tests `CONFIG_BT_SETTINGS_CF_STORE_ON_WRITE` does its job, and
|
||||
* writes the change-awareness before we get disconnected. Basically, this
|
||||
* transition simulates a user yanking the power of the device before it has the
|
||||
* chance to disconnect.
|
||||
*
|
||||
* Round 3 -> 4: same as (1->2), except this time we won't get the SC indication
|
||||
* (as we have unsubscribed from it). We should instead get the
|
||||
* `BT_ATT_ERR_DB_OUT_OF_SYNC` error on the first attribute read. This also
|
||||
* tests that robust GATT caching is enforced.
|
||||
*
|
||||
* Round 4 -> 5: tests change-awareness status is still written on disconnect.
|
||||
* This is a non-regression test to make sure we didn't break the previous
|
||||
* behavior.
|
||||
*
|
||||
* Round 5 -> 6: tests DFU corner case: in this case, we are on the first boot
|
||||
* of an updated firmware, that will register new services. But for some unknown
|
||||
* reason, we decide to reboot before the delayed store work item has had the
|
||||
* time to execute and store that the peers are now change-unaware. Round 6 then
|
||||
* makes sure that we are indeed change-unaware.
|
||||
*/
|
||||
void server_procedure(void)
|
||||
{
|
||||
uint8_t round = get_test_round();
|
||||
|
||||
wait_for_round_start();
|
||||
|
||||
printk("Start test round: %d\n", get_test_round());
|
||||
|
||||
/* Use the same public address for all instances of the central. If we
|
||||
* don't do that, encryption (using the bond stored in NVS) will
|
||||
* fail.
|
||||
*/
|
||||
set_public_addr();
|
||||
|
||||
gatt_register_service_1();
|
||||
|
||||
bt_enable(NULL);
|
||||
settings_load();
|
||||
|
||||
switch (round) {
|
||||
case 0:
|
||||
server_round_0();
|
||||
break;
|
||||
case 1:
|
||||
server_round_1();
|
||||
break;
|
||||
case 2:
|
||||
server_round_2();
|
||||
break;
|
||||
case 3:
|
||||
server_round_3();
|
||||
break;
|
||||
case 4:
|
||||
server_round_4();
|
||||
break;
|
||||
case 5:
|
||||
server_round_5();
|
||||
break;
|
||||
case 6:
|
||||
server_round_6();
|
||||
break;
|
||||
default:
|
||||
FAIL("Round %d doesn't exist\n", round);
|
||||
break;
|
||||
}
|
||||
|
||||
signal_next_test_round();
|
||||
}
|
233
tests/bsim/bluetooth/host/gatt/settings/src/settings.c
Normal file
233
tests/bsim/bluetooth/host/gatt/settings/src/settings.c
Normal file
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "main.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#include "zephyr/types.h"
|
||||
#include "errno.h"
|
||||
#include "argparse.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(settings_backend, 3);
|
||||
|
||||
#define SETTINGS_FILE setting_file
|
||||
#define SETTINGS_FILE_TMP setting_file_tmp
|
||||
|
||||
#define ENTRY_LEN_SIZE (4)
|
||||
#define ENTRY_NAME_MAX_LEN (SETTINGS_MAX_NAME_LEN + SETTINGS_EXTRA_LEN)
|
||||
#define ENTRY_VAL_MAX_LEN (SETTINGS_MAX_VAL_LEN * 2)
|
||||
#define READ_LEN_MAX (ENTRY_VAL_MAX_LEN + ENTRY_NAME_MAX_LEN + 2)
|
||||
|
||||
struct line_read_ctx {
|
||||
int len;
|
||||
const uint8_t *val;
|
||||
};
|
||||
|
||||
static char setting_file[50];
|
||||
static char setting_file_tmp[sizeof(setting_file) + 1];
|
||||
|
||||
static int entry_check_and_copy(FILE *fin, FILE *fout, const char *name)
|
||||
{
|
||||
char line[READ_LEN_MAX + 1];
|
||||
char name_tmp[strlen(name) + 2];
|
||||
|
||||
snprintk(name_tmp, sizeof(name_tmp), "%s=", name);
|
||||
|
||||
while (fgets(line, sizeof(line), fin) == line) {
|
||||
if (strstr(line, name_tmp) != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (fputs(line, fout) < 0) {
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t settings_line_read_cb(void *cb_arg, void *data, size_t len)
|
||||
{
|
||||
struct line_read_ctx *valctx = (struct line_read_ctx *)cb_arg;
|
||||
|
||||
if ((valctx->len / 2) > len) {
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
uint8_t *n = (uint8_t *) data;
|
||||
|
||||
len = valctx->len / 2;
|
||||
|
||||
for (int i = 0; i < len; i++, n++) {
|
||||
if (sscanf(&valctx->val[i * 2], "%2hhx", n) != 1) {
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static int settings_custom_load(struct settings_store *cs, const struct settings_load_arg *arg)
|
||||
{
|
||||
FILE *fp = fopen(SETTINGS_FILE, "r+");
|
||||
|
||||
if (fp == NULL) {
|
||||
LOG_INF("Settings file doesn't exist, will create it");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fseek(fp, 0, SEEK_SET) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int vallen;
|
||||
char line[READ_LEN_MAX + 1];
|
||||
|
||||
while (fgets(line, sizeof(line), fp) == line) {
|
||||
/* check for matching subtree */
|
||||
if (arg->subtree != NULL && !strstr(line, arg->subtree)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char *pos = strchr(line, '=');
|
||||
|
||||
if (pos <= line) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
vallen = strlen(line) - (pos - line) - 2;
|
||||
LOG_DBG("loading entry: %s", line);
|
||||
|
||||
struct line_read_ctx valctx;
|
||||
|
||||
valctx.len = vallen;
|
||||
valctx.val = pos + 1;
|
||||
int err = settings_call_set_handler(line, vallen / 2, settings_line_read_cb,
|
||||
&valctx, arg);
|
||||
|
||||
if (err < 0) {
|
||||
return err;
|
||||
}
|
||||
};
|
||||
|
||||
return fclose(fp);
|
||||
}
|
||||
|
||||
/* Entries are saved to optimize readability of the settings file for test development and
|
||||
* debugging purposes. Format:
|
||||
* <entry-key>=<entry-value-hex-str>\n
|
||||
*/
|
||||
static int settings_custom_save(struct settings_store *cs, const char *name,
|
||||
const char *value, size_t val_len)
|
||||
{
|
||||
FILE *fcur = fopen(SETTINGS_FILE, "r+");
|
||||
FILE *fnew = NULL;
|
||||
|
||||
if (fcur == NULL) {
|
||||
fcur = fopen(SETTINGS_FILE, "w");
|
||||
} else {
|
||||
fnew = fopen(SETTINGS_FILE_TMP, "w");
|
||||
if (fnew == NULL) {
|
||||
LOG_ERR("Failed to create temporary file %s", SETTINGS_FILE_TMP);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fcur == NULL) {
|
||||
LOG_ERR("Failed to create settings file: %s", SETTINGS_FILE);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strlen(name) > ENTRY_NAME_MAX_LEN || val_len > SETTINGS_MAX_VAL_LEN) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (fnew != NULL) {
|
||||
if (entry_check_and_copy(fcur, fnew, name) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (val_len) {
|
||||
char bufvname[ENTRY_NAME_MAX_LEN + ENTRY_LEN_SIZE + 3];
|
||||
|
||||
snprintk(bufvname, sizeof(bufvname), "%s=", name);
|
||||
if (fputs(bufvname, fnew != NULL ? fnew : fcur) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
char bufval[ENTRY_VAL_MAX_LEN + 2] = {};
|
||||
size_t valcnt = 0;
|
||||
|
||||
while (valcnt < (val_len * 2)) {
|
||||
valcnt += snprintk(&bufval[valcnt], 3, "%02x",
|
||||
(uint8_t)value[valcnt / 2]);
|
||||
};
|
||||
|
||||
/* helps in making settings file readable */
|
||||
bufval[valcnt++] = '\n';
|
||||
bufval[valcnt] = 0;
|
||||
|
||||
LOG_DBG("writing to disk");
|
||||
|
||||
if (fputs(bufval, fnew != NULL ? fnew : fcur) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (fnew != NULL) {
|
||||
entry_check_and_copy(fcur, fnew, name);
|
||||
}
|
||||
|
||||
fclose(fcur);
|
||||
|
||||
if (fnew != NULL) {
|
||||
fclose(fnew);
|
||||
|
||||
remove(SETTINGS_FILE);
|
||||
rename(SETTINGS_FILE_TMP, SETTINGS_FILE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* custom backend interface */
|
||||
static struct settings_store_itf settings_custom_itf = {
|
||||
.csi_load = settings_custom_load,
|
||||
.csi_save = settings_custom_save,
|
||||
};
|
||||
|
||||
/* custom backend node */
|
||||
static struct settings_store settings_custom_store = {
|
||||
.cs_itf = &settings_custom_itf
|
||||
};
|
||||
|
||||
int settings_backend_init(void)
|
||||
{
|
||||
snprintf(setting_file, sizeof(setting_file), "%s_%s.log", get_simid(), get_settings_file());
|
||||
snprintf(setting_file_tmp, sizeof(setting_file_tmp), "~%s", setting_file);
|
||||
|
||||
LOG_INF("file path: %s", SETTINGS_FILE);
|
||||
|
||||
/* register custom backend */
|
||||
settings_dst_register(&settings_custom_store);
|
||||
settings_src_register(&settings_custom_store);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void settings_test_backend_clear(void)
|
||||
{
|
||||
snprintf(setting_file, sizeof(setting_file), "%s_%s.log", get_simid(), get_settings_file());
|
||||
|
||||
if (remove(setting_file)) {
|
||||
LOG_INF("error deleting file: %s", setting_file);
|
||||
}
|
||||
}
|
223
tests/bsim/bluetooth/host/gatt/settings/src/utils.c
Normal file
223
tests/bsim/bluetooth/host/gatt/settings/src/utils.c
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "utils.h"
|
||||
#include "gatt_utils.h"
|
||||
#include <zephyr/sys/__assert.h>
|
||||
#include <zephyr/bluetooth/hci.h>
|
||||
|
||||
DEFINE_FLAG(flag_is_connected);
|
||||
DEFINE_FLAG(flag_test_end);
|
||||
|
||||
void wait_connected(void)
|
||||
{
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
WAIT_FOR_FLAG(flag_is_connected);
|
||||
printk("connected\n");
|
||||
|
||||
}
|
||||
|
||||
void wait_disconnected(void)
|
||||
{
|
||||
SET_FLAG(flag_is_connected);
|
||||
WAIT_FOR_FLAG_UNSET(flag_is_connected);
|
||||
printk("disconnected\n");
|
||||
}
|
||||
|
||||
static void disconnected(struct bt_conn *conn, uint8_t reason)
|
||||
{
|
||||
bt_conn_unref(conn);
|
||||
UNSET_FLAG(flag_is_connected);
|
||||
gatt_clear_flags();
|
||||
}
|
||||
|
||||
static void connected(struct bt_conn *conn, uint8_t err)
|
||||
{
|
||||
if (err != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
bt_conn_ref(conn);
|
||||
SET_FLAG(flag_is_connected);
|
||||
}
|
||||
|
||||
DEFINE_FLAG(flag_encrypted);
|
||||
|
||||
void security_changed(struct bt_conn *conn, bt_security_t level,
|
||||
enum bt_security_err err)
|
||||
{
|
||||
__ASSERT(err == 0, "Error setting security (err %u)\n", err);
|
||||
|
||||
printk("Encrypted\n");
|
||||
SET_FLAG(flag_encrypted);
|
||||
}
|
||||
|
||||
BT_CONN_CB_DEFINE(conn_callbacks) = {
|
||||
.connected = connected,
|
||||
.disconnected = disconnected,
|
||||
.security_changed = security_changed,
|
||||
};
|
||||
|
||||
static void scan_connect_to_first_result_device_found(const bt_addr_le_t *addr, int8_t rssi,
|
||||
uint8_t type, struct net_buf_simple *ad)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
char addr_str[BT_ADDR_LE_STR_LEN];
|
||||
int err;
|
||||
|
||||
/* We're only interested in connectable events */
|
||||
if (type != BT_HCI_ADV_IND && type != BT_HCI_ADV_DIRECT_IND) {
|
||||
FAIL("Unexpected advertisement type.");
|
||||
}
|
||||
|
||||
bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));
|
||||
printk("Got scan result, connecting.. dst %s, RSSI %d\n",
|
||||
addr_str, rssi);
|
||||
|
||||
err = bt_le_scan_stop();
|
||||
__ASSERT(!err, "Err bt_le_scan_stop %d", err);
|
||||
|
||||
err = bt_conn_le_create(addr,
|
||||
BT_CONN_LE_CREATE_CONN, BT_LE_CONN_PARAM_DEFAULT,
|
||||
&conn);
|
||||
__ASSERT(!err, "Err bt_conn_le_create %d", err);
|
||||
}
|
||||
|
||||
void scan_connect_to_first_result(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
printk("start scanner\n");
|
||||
err = bt_le_scan_start(BT_LE_SCAN_PASSIVE,
|
||||
scan_connect_to_first_result_device_found);
|
||||
__ASSERT(!err, "Err bt_le_scan_start %d", err);
|
||||
}
|
||||
|
||||
void advertise_connectable(void)
|
||||
{
|
||||
printk("start advertiser\n");
|
||||
int err;
|
||||
struct bt_le_adv_param param = {};
|
||||
|
||||
param.interval_min = 0x0020;
|
||||
param.interval_max = 0x4000;
|
||||
param.options |= BT_LE_ADV_OPT_ONE_TIME;
|
||||
param.options |= BT_LE_ADV_OPT_CONNECTABLE;
|
||||
|
||||
err = bt_le_adv_start(¶m, NULL, 0, NULL, 0);
|
||||
__ASSERT(err == 0, "Advertising failed to start (err %d)\n", err);
|
||||
}
|
||||
|
||||
void disconnect(struct bt_conn *conn)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
|
||||
__ASSERT(!err, "Failed to initate disconnection (err %d)", err);
|
||||
|
||||
printk("Waiting for disconnection...\n");
|
||||
WAIT_FOR_FLAG_UNSET(flag_is_connected);
|
||||
}
|
||||
|
||||
static void get_active_conn_cb(struct bt_conn *src, void *dst)
|
||||
{
|
||||
*(struct bt_conn **)dst = src;
|
||||
}
|
||||
|
||||
struct bt_conn *get_conn(void)
|
||||
{
|
||||
struct bt_conn *ret;
|
||||
|
||||
bt_conn_foreach(BT_CONN_TYPE_LE, get_active_conn_cb, &ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DEFINE_FLAG(flag_pairing_complete);
|
||||
|
||||
static void pairing_failed(struct bt_conn *conn, enum bt_security_err reason)
|
||||
{
|
||||
FAIL("Pairing failed (unexpected): reason %u", reason);
|
||||
}
|
||||
|
||||
static void pairing_complete(struct bt_conn *conn, bool bonded)
|
||||
{
|
||||
__ASSERT(bonded, "Bonding failed\n");
|
||||
|
||||
printk("Paired\n");
|
||||
SET_FLAG(flag_pairing_complete);
|
||||
}
|
||||
|
||||
static struct bt_conn_auth_info_cb bt_conn_auth_info_cb = {
|
||||
.pairing_failed = pairing_failed,
|
||||
.pairing_complete = pairing_complete,
|
||||
};
|
||||
|
||||
void set_security(struct bt_conn *conn, bt_security_t sec)
|
||||
{
|
||||
int err;
|
||||
|
||||
UNSET_FLAG(flag_encrypted);
|
||||
|
||||
err = bt_conn_set_security(conn, sec);
|
||||
__ASSERT(!err, "Err bt_conn_set_security %d", err);
|
||||
|
||||
WAIT_FOR_FLAG(flag_encrypted);
|
||||
}
|
||||
|
||||
void wait_secured(void)
|
||||
{
|
||||
UNSET_FLAG(flag_encrypted);
|
||||
WAIT_FOR_FLAG(flag_encrypted);
|
||||
}
|
||||
|
||||
void bond(struct bt_conn *conn)
|
||||
{
|
||||
UNSET_FLAG(flag_pairing_complete);
|
||||
|
||||
int err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb);
|
||||
|
||||
__ASSERT(!err, "bt_conn_auth_info_cb_register failed.\n");
|
||||
|
||||
set_security(conn, BT_SECURITY_L2);
|
||||
|
||||
WAIT_FOR_FLAG(flag_pairing_complete);
|
||||
}
|
||||
|
||||
void wait_bonded(void)
|
||||
{
|
||||
UNSET_FLAG(flag_encrypted);
|
||||
UNSET_FLAG(flag_pairing_complete);
|
||||
|
||||
int err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb);
|
||||
|
||||
__ASSERT(!err, "bt_conn_auth_info_cb_register failed.\n");
|
||||
|
||||
WAIT_FOR_FLAG(flag_encrypted);
|
||||
WAIT_FOR_FLAG(flag_pairing_complete);
|
||||
}
|
||||
|
||||
struct bt_conn *connect_as_central(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
scan_connect_to_first_result();
|
||||
wait_connected();
|
||||
conn = get_conn();
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
struct bt_conn *connect_as_peripheral(void)
|
||||
{
|
||||
struct bt_conn *conn;
|
||||
|
||||
advertise_connectable();
|
||||
wait_connected();
|
||||
conn = get_conn();
|
||||
|
||||
return conn;
|
||||
}
|
67
tests/bsim/bluetooth/host/gatt/settings/src/utils.h
Normal file
67
tests/bsim/bluetooth/host/gatt/settings/src/utils.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright (c) 2023 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "bs_tracing.h"
|
||||
#include "bs_types.h"
|
||||
#include "bstests.h"
|
||||
#include "time_machine.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <zephyr/bluetooth/conn.h>
|
||||
|
||||
extern enum bst_result_t bst_result;
|
||||
|
||||
#define DECLARE_FLAG(flag) extern atomic_t flag
|
||||
#define DEFINE_FLAG(flag) atomic_t flag = (atomic_t) false
|
||||
#define SET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) true)
|
||||
#define UNSET_FLAG(flag) (void)atomic_set(&flag, (atomic_t) false)
|
||||
#define WAIT_FOR_FLAG(flag) \
|
||||
while (!(bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
#define WAIT_FOR_FLAG_UNSET(flag) \
|
||||
while ((bool)atomic_get(&flag)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
#define TAKE_FLAG(flag) \
|
||||
while (!(bool)atomic_cas(&flag, true, false)) { \
|
||||
(void)k_sleep(K_MSEC(1)); \
|
||||
}
|
||||
|
||||
#define ASSERT(expr, ...) \
|
||||
do { \
|
||||
if (!(expr)) { \
|
||||
FAIL(__VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
DECLARE_FLAG(flag_test_end);
|
||||
|
||||
#define FAIL(...) \
|
||||
SET_FLAG(flag_test_end); \
|
||||
do { \
|
||||
bst_result = Failed; \
|
||||
bs_trace_error_time_line(__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define PASS(...) \
|
||||
SET_FLAG(flag_test_end); \
|
||||
do { \
|
||||
bst_result = Passed; \
|
||||
bs_trace_info_time(1, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
void disconnect(struct bt_conn *conn);
|
||||
void wait_disconnected(void);
|
||||
struct bt_conn *get_conn(void);
|
||||
struct bt_conn *connect_as_central(void);
|
||||
struct bt_conn *connect_as_peripheral(void);
|
||||
|
||||
void set_security(struct bt_conn *conn, bt_security_t sec);
|
||||
void wait_secured(void);
|
||||
void bond(struct bt_conn *conn);
|
||||
void wait_bonded(void);
|
16
tests/bsim/bluetooth/host/gatt/settings/test_scripts/_compile.sh
Executable file
16
tests/bsim/bluetooth/host/gatt/settings/test_scripts/_compile.sh
Executable file
|
@ -0,0 +1,16 @@
|
|||
#!/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"
|
||||
|
||||
# Place yourself in the test's root (i.e. ./../)
|
||||
west build -b nrf52_bsim -d build_test && \
|
||||
cp build_test/zephyr/zephyr.exe "${test_exe}"
|
||||
|
||||
west build -b nrf52_bsim -d build_test_2 -- -DCONF_FILE=prj_2.conf && \
|
||||
cp build_test_2/zephyr/zephyr.exe "${test_2_exe}"
|
13
tests/bsim/bluetooth/host/gatt/settings/test_scripts/_env.sh
Executable file
13
tests/bsim/bluetooth/host/gatt/settings/test_scripts/_env.sh
Executable file
|
@ -0,0 +1,13 @@
|
|||
#!/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"
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
test_exe="${bsim_bin}/bs_${BOARD}_tests_bsim_bluetooth_host_gatt_settings_prj_conf"
|
||||
test_2_exe="${bsim_bin}/bs_${BOARD}_tests_bsim_bluetooth_host_gatt_settings_prj_2_conf"
|
63
tests/bsim/bluetooth/host/gatt/settings/test_scripts/run_gatt_settings.sh
Executable file
63
tests/bsim/bluetooth/host/gatt/settings/test_scripts/run_gatt_settings.sh
Executable file
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -eu
|
||||
bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
|
||||
|
||||
source "${bash_source_dir}/_env.sh"
|
||||
simulation_id="${test_name}"
|
||||
verbosity_level=2
|
||||
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
|
||||
$@ &
|
||||
process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
# Remove the files used by the custom SETTINGS backend
|
||||
TO_DELETE="${simulation_id}_server.log ${simulation_id}_client.log"
|
||||
echo "remove settings files ${TO_DELETE}"
|
||||
rm ${TO_DELETE} || true
|
||||
|
||||
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s="${simulation_id}" -D=8 -sim_length=30e6 $@
|
||||
|
||||
# Only one `server` device is really running at a time. This is a necessary hack
|
||||
# because bsim doesn't support plugging devices in and out of a running
|
||||
# simulation, but this test needs a way to power-cycle the `server` device a few
|
||||
# times.
|
||||
#
|
||||
# Each device will wait until the previous instance (called 'test round') has
|
||||
# finished executing before starting up.
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=0 -testid=server -RealEncryption=1 -argstest 0 6 "server"
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=1 -testid=server -RealEncryption=1 -argstest 1 6 "server"
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=2 -testid=server -RealEncryption=1 -argstest 2 6 "server"
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=3 -testid=server -RealEncryption=1 -argstest 3 6 "server"
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=4 -testid=server -RealEncryption=1 -argstest 4 6 "server"
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=5 -testid=server -RealEncryption=1 -argstest 5 6 "server"
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=6 -testid=server -RealEncryption=1 -argstest 6 6 "server"
|
||||
|
||||
Execute "$test_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=7 -testid=client -RealEncryption=1 -argstest 0 0 "client"
|
||||
|
||||
for process_id in $process_ids; do
|
||||
wait $process_id || let "exit_code=$?"
|
||||
done
|
||||
exit $exit_code #the last exit code != 0
|
63
tests/bsim/bluetooth/host/gatt/settings/test_scripts/run_gatt_settings_2.sh
Executable file
63
tests/bsim/bluetooth/host/gatt/settings/test_scripts/run_gatt_settings_2.sh
Executable file
|
@ -0,0 +1,63 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2022 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -eu
|
||||
bash_source_dir="$(realpath "$(dirname "${BASH_SOURCE[0]}")")"
|
||||
|
||||
source "${bash_source_dir}/_env.sh"
|
||||
simulation_id="${test_name}_2"
|
||||
verbosity_level=2
|
||||
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
|
||||
$@ &
|
||||
process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
# Remove the files used by the custom SETTINGS backend
|
||||
TO_DELETE="${simulation_id}_server_2.log ${simulation_id}_client_2.log"
|
||||
echo "remove settings files ${TO_DELETE}"
|
||||
rm ${TO_DELETE} || true
|
||||
|
||||
Execute ./bs_2G4_phy_v1 -v=${verbosity_level} -s="${simulation_id}" -D=8 -sim_length=30e6 $@
|
||||
|
||||
# Only one `server` device is really running at a time. This is a necessary hack
|
||||
# because bsim doesn't support plugging devices in and out of a running
|
||||
# simulation, but this test needs a way to power-cycle the `server` device a few
|
||||
# times.
|
||||
#
|
||||
# Each device will wait until the previous instance (called 'test round') has
|
||||
# finished executing before starting up.
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=0 -testid=server -RealEncryption=1 -argstest 0 6 "server_2"
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=1 -testid=server -RealEncryption=1 -argstest 1 6 "server_2"
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=2 -testid=server -RealEncryption=1 -argstest 2 6 "server_2"
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=3 -testid=server -RealEncryption=1 -argstest 3 6 "server_2"
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=4 -testid=server -RealEncryption=1 -argstest 4 6 "server_2"
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=5 -testid=server -RealEncryption=1 -argstest 5 6 "server_2"
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=6 -testid=server -RealEncryption=1 -argstest 6 6 "server_2"
|
||||
|
||||
Execute "$test_2_exe" -v=${verbosity_level} \
|
||||
-s="${simulation_id}" -d=7 -testid=client -RealEncryption=1 -argstest 0 0 "client_2"
|
||||
|
||||
for process_id in $process_ids; do
|
||||
wait $process_id || let "exit_code=$?"
|
||||
done
|
||||
exit $exit_code #the last exit code != 0
|
25
tests/bsim/bluetooth/host/gatt/write/CMakeLists.txt
Normal file
25
tests/bsim/bluetooth/host/gatt/write/CMakeLists.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
# 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 REQUIRED HINTS $ENV{ZEPHYR_BASE})
|
||||
project(bsim_test_gatt_write)
|
||||
|
||||
target_sources(app PRIVATE
|
||||
src/main.c
|
||||
${ZEPHYR_BASE}/samples/bluetooth/central_gatt_write/src/gatt_write_common.c
|
||||
${ZEPHYR_BASE}/samples/bluetooth/central_gatt_write/src/central_gatt_write.c
|
||||
${ZEPHYR_BASE}/samples/bluetooth/peripheral_gatt_write/src/peripheral_gatt_write.c
|
||||
)
|
||||
|
||||
zephyr_include_directories(
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libUtilv1/src/
|
||||
$ENV{BSIM_COMPONENTS_PATH}/libPhyComv1/src/
|
||||
)
|
14
tests/bsim/bluetooth/host/gatt/write/prj.conf
Normal file
14
tests/bsim/bluetooth/host/gatt/write/prj.conf
Normal file
|
@ -0,0 +1,14 @@
|
|||
CONFIG_BT=y
|
||||
CONFIG_BT_CENTRAL=y
|
||||
CONFIG_BT_PERIPHERAL=y
|
||||
CONFIG_BT_SMP=y
|
||||
CONFIG_BT_GATT_CLIENT=y
|
||||
|
||||
CONFIG_BT_BUF_ACL_RX_SIZE=255
|
||||
CONFIG_BT_BUF_ACL_TX_SIZE=251
|
||||
CONFIG_BT_BUF_CMD_TX_SIZE=255
|
||||
CONFIG_BT_BUF_EVT_DISCARDABLE_SIZE=255
|
||||
|
||||
CONFIG_BT_L2CAP_TX_MTU=247
|
||||
|
||||
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
|
131
tests/bsim/bluetooth/host/gatt/write/src/main.c
Normal file
131
tests/bsim/bluetooth/host/gatt/write/src/main.c
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2022 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#include <zephyr/sys/printk.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
#include "bs_types.h"
|
||||
#include "bs_tracing.h"
|
||||
#include "time_machine.h"
|
||||
#include "bstests.h"
|
||||
|
||||
#define COUNT 5000 /* Arbitrary GATT Write Cmd iterations used */
|
||||
|
||||
/* Write Throughput calculation:
|
||||
* Measure interval = 1 s
|
||||
* Connection interval = 50 ms
|
||||
* No. of connection intervals = 20
|
||||
* Single Tx time, 2M PHY = 1064 us
|
||||
* tIFS = 150 us
|
||||
* Single Tx duration = 1214 us
|
||||
* Full duplex Tx-Rx duration = 2428 us
|
||||
* Implementation dependent event overhead = 340 us
|
||||
* Max. incomplete PDU time = 1064 us
|
||||
* Max. radio idle time per 1 second = (1064 + 340) * 20 = 28080 us
|
||||
* Packets per 1 second = (1000000 - 28080) / 2428 = 400.297
|
||||
* GATT Write data length = 244 bytes
|
||||
* Throughput = 400 * 244 * 8 = 780800 bps
|
||||
*/
|
||||
#define WRITE_RATE 780800 /* GATT Write bps recorded in this test */
|
||||
|
||||
extern uint32_t central_gatt_write(uint32_t count);
|
||||
extern uint32_t peripheral_gatt_write(uint32_t count);
|
||||
|
||||
#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;
|
||||
|
||||
static void test_central_main(void)
|
||||
{
|
||||
uint32_t write_rate;
|
||||
|
||||
write_rate = central_gatt_write(COUNT);
|
||||
|
||||
printk("%s: Write Rate = %u bps\n", __func__, write_rate);
|
||||
if (write_rate == WRITE_RATE) {
|
||||
PASS("Central tests passed\n");
|
||||
} else {
|
||||
FAIL("Central tests failed\n");
|
||||
}
|
||||
|
||||
/* Give extra time for peripheral side to finish its iterations */
|
||||
k_sleep(K_SECONDS(1));
|
||||
|
||||
bs_trace_silent_exit(0);
|
||||
}
|
||||
|
||||
static void test_peripheral_main(void)
|
||||
{
|
||||
uint32_t write_rate;
|
||||
|
||||
write_rate = peripheral_gatt_write(COUNT);
|
||||
|
||||
printk("%s: Write Rate = %u bps\n", __func__, write_rate);
|
||||
if (write_rate == WRITE_RATE) {
|
||||
PASS("Peripheral tests passed\n");
|
||||
} else {
|
||||
FAIL("Peripheral tests failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void test_gatt_write_init(void)
|
||||
{
|
||||
bst_ticker_set_next_tick_absolute(60e6);
|
||||
bst_result = In_progress;
|
||||
}
|
||||
|
||||
static void test_gatt_write_tick(bs_time_t HW_device_time)
|
||||
{
|
||||
bst_result = Failed;
|
||||
bs_trace_error_line("Test GATT Write finished.\n");
|
||||
}
|
||||
|
||||
static const struct bst_test_instance test_def[] = {
|
||||
{
|
||||
.test_id = "central",
|
||||
.test_descr = "Central GATT Write",
|
||||
.test_post_init_f = test_gatt_write_init,
|
||||
.test_tick_f = test_gatt_write_tick,
|
||||
.test_main_f = test_central_main
|
||||
},
|
||||
{
|
||||
.test_id = "peripheral",
|
||||
.test_descr = "Peripheral GATT Write",
|
||||
.test_post_init_f = test_gatt_write_init,
|
||||
.test_tick_f = test_gatt_write_tick,
|
||||
.test_main_f = test_peripheral_main
|
||||
},
|
||||
BSTEST_END_MARKER
|
||||
};
|
||||
|
||||
struct bst_test_list *test_gatt_write_install(struct bst_test_list *tests)
|
||||
{
|
||||
return bst_add_tests(tests, test_def);
|
||||
}
|
||||
|
||||
bst_test_install_t test_installers[] = {
|
||||
test_gatt_write_install,
|
||||
NULL
|
||||
};
|
||||
|
||||
void main(void)
|
||||
{
|
||||
bst_main();
|
||||
}
|
38
tests/bsim/bluetooth/host/gatt/write/tests_scripts/gatt_write.sh
Executable file
38
tests/bsim/bluetooth/host/gatt/write/tests_scripts/gatt_write.sh
Executable file
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env bash
|
||||
# Copyright 2018 Oticon A/S
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# Multiple connection between two devices with multiple peripheral identity
|
||||
simulation_id="gatt_write"
|
||||
verbosity_level=2
|
||||
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 60 $@ & process_ids="$process_ids $!"
|
||||
}
|
||||
|
||||
: "${BSIM_OUT_PATH:?BSIM_OUT_PATH must be defined}"
|
||||
|
||||
#Give a default value to BOARD if it does not have one yet:
|
||||
BOARD="${BOARD:-nrf52_bsim}"
|
||||
|
||||
cd ${BSIM_OUT_PATH}/bin
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_write_prj_conf \
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=0 -testid=central
|
||||
|
||||
Execute ./bs_${BOARD}_tests_bsim_bluetooth_host_gatt_write_prj_conf\
|
||||
-v=${verbosity_level} -s=${simulation_id} -d=1 -testid=peripheral
|
||||
|
||||
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
|
Loading…
Add table
Add a link
Reference in a new issue