blob: a1ccc4f6996c39d3724fb32cd94f6c50639a793f [file] [log] [blame]
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "bs_bt_utils.h"
#include "utils.h"
BUILD_ASSERT(CONFIG_BT_MAX_PAIRED >= 2, "CONFIG_BT_MAX_PAIRED is too small.");
BUILD_ASSERT(CONFIG_BT_ID_MAX >= 3, "CONFIG_BT_ID_MAX is too small.");
BUILD_ASSERT(CONFIG_BT_MAX_CONN == 2, "CONFIG_BT_MAX_CONN should be equal to two.");
BUILD_ASSERT(CONFIG_BT_GATT_CLIENT, "CONFIG_BT_GATT_CLIENT is disabled.");
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;
}
DEFINE_FLAG(flag_has_new_conn);
struct bt_conn *new_conn;
DEFINE_FLAG(flag_has_disconnected);
void clear_conn(struct bt_conn *conn)
{
if (new_conn == conn) {
new_conn = NULL;
}
ASSERT(conn, "Test error: No new_conn!\n");
bt_conn_unref(conn);
}
void wait_connected(struct bt_conn **conn)
{
WAIT_FOR_FLAG(flag_has_new_conn);
UNSET_FLAG(flag_has_new_conn);
ASSERT(new_conn, "connection unpopulated.");
*conn = new_conn;
new_conn = NULL;
}
void wait_disconnected(void)
{
WAIT_FOR_FLAG(flag_has_disconnected);
UNSET_FLAG(flag_has_disconnected);
}
static void print_conn_state_transition(const char *prefix, struct bt_conn *conn)
{
int err;
struct bt_conn_info info;
char addr_str[BT_ADDR_LE_STR_LEN];
err = bt_conn_get_info(conn, &info);
ASSERT(!err, "Unexpected conn info result.");
bt_addr_le_to_str(info.le.dst, addr_str, sizeof(addr_str));
printk("%s: %s\n", prefix, addr_str);
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
print_conn_state_transition("Disonnected", conn);
SET_FLAG(flag_has_disconnected);
}
static void connected(struct bt_conn *conn, uint8_t err)
{
ASSERT((!new_conn || (conn == new_conn)), "Unexpected new connection.");
if (!new_conn) {
new_conn = bt_conn_ref(conn);
}
if (err != 0) {
clear_conn(conn);
return;
}
print_conn_state_transition("Connected", conn);
SET_FLAG(flag_has_new_conn);
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
DEFINE_FLAG(flag_pairing_completed);
static void pairing_complete(struct bt_conn *conn, bool bonded)
{
print_conn_state_transition("Paired", conn);
SET_FLAG(flag_pairing_completed);
}
static struct bt_conn_auth_info_cb bt_conn_auth_info_cb = {
.pairing_complete = pairing_complete,
};
void set_security(struct bt_conn *conn, bt_security_t sec)
{
int err;
err = bt_conn_set_security(conn, sec);
ASSERT(!err, "Err bt_conn_set_security %d", err);
}
void wait_pairing_completed(void)
{
WAIT_FOR_FLAG(flag_pairing_completed);
UNSET_FLAG(flag_pairing_completed);
}
void bs_bt_utils_setup(void)
{
int err;
err = bt_enable(NULL);
ASSERT(!err, "bt_enable failed.\n");
err = bt_conn_auth_info_cb_register(&bt_conn_auth_info_cb);
ASSERT(!err, "bt_conn_auth_info_cb_register failed.\n");
err = settings_load();
ASSERT(!err, "settings_load failed.\n");
}
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)
{
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, &new_conn);
ASSERT(!err, "Err bt_conn_le_create %d", err);
}
void scan_connect_to_first_result(void)
{
int err;
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 disconnect(struct bt_conn *conn)
{
int err;
err = bt_conn_disconnect(conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN);
ASSERT(!err, "Err bt_conn_disconnect %d", err);
}
DEFINE_FLAG(flag_bas_has_notification);
static uint8_t bas_level = 50;
static uint8_t bas_notify_func(struct bt_conn *conn,
struct bt_gatt_subscribe_params *params,
const void *data, uint16_t length)
{
const uint8_t *lvl8 = data;
if ((length == 1) && (*lvl8 == bas_level)) {
printk("BAS notification\n");
SET_FLAG(flag_bas_has_notification);
}
return BT_GATT_ITER_CONTINUE;
}
void wait_bas_notification(void)
{
WAIT_FOR_FLAG(flag_bas_has_notification);
UNSET_FLAG(flag_bas_has_notification);
}
/* Not actually used, see below why we also have this on the central */
BT_GATT_SERVICE_DEFINE(bas,
BT_GATT_PRIMARY_SERVICE(BT_UUID_BAS),
BT_GATT_CHARACTERISTIC(BT_UUID_BAS_BATTERY_LEVEL,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ, NULL, NULL, &bas_level),
BT_GATT_CCC(NULL,
BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);
void bas_subscribe(struct bt_conn *conn)
{
int err;
static struct bt_gatt_subscribe_params subscribe_params = {0};
/* This is a bit of a shortcut: to skip discovery, we assume the handles
* will be the same on the central & peripheral images.
*/
subscribe_params.ccc_handle = bt_gatt_attr_get_handle(&bas.attrs[3]);
subscribe_params.value_handle = bt_gatt_attr_get_handle(&bas.attrs[2]);
subscribe_params.value = BT_GATT_CCC_NOTIFY;
subscribe_params.notify = bas_notify_func;
err = bt_gatt_subscribe(conn, &subscribe_params);
ASSERT(!err, "bt_gatt_subscribe failed (err %d)\n", err);
}