The host-based adv auto-resume function has both a problematic implementation and disagreement in the community around how it should behave. See the issue linked resolved below for details. This patch makes the deprecation visible to the user. The user will be better served by a auto-resume tailored their applications use case, based on more primitive host API like `conn_cb.recycled`, which has obvious behavior that is unlikely to change. Resolves: https://github.com/zephyrproject-rtos/zephyr/issues/72567 Signed-off-by: Aleksander Wasaznik <aleksander.wasaznik@nordicsemi.no>
262 lines
6.5 KiB
C
262 lines
6.5 KiB
C
/*
|
|
* Copyright (c) 2023 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/gatt.h>
|
|
#include <zephyr/bluetooth/uuid.h>
|
|
#include <zephyr/bluetooth/hci.h>
|
|
#include <zephyr/sys/util.h>
|
|
|
|
#define NAME_LEN 30
|
|
|
|
static K_SEM_DEFINE(sem_per_adv, 0, 1);
|
|
static K_SEM_DEFINE(sem_per_sync, 0, 1);
|
|
static K_SEM_DEFINE(sem_per_sync_lost, 0, 1);
|
|
|
|
static struct bt_conn *default_conn;
|
|
static struct bt_le_per_adv_sync *default_sync;
|
|
static struct __packed {
|
|
uint8_t subevent;
|
|
uint8_t response_slot;
|
|
|
|
} pawr_timing;
|
|
|
|
static void sync_cb(struct bt_le_per_adv_sync *sync, struct bt_le_per_adv_sync_synced_info *info)
|
|
{
|
|
struct bt_le_per_adv_sync_subevent_params params;
|
|
uint8_t subevents[1];
|
|
char le_addr[BT_ADDR_LE_STR_LEN];
|
|
int err;
|
|
|
|
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
|
|
printk("Synced to %s with %d subevents\n", le_addr, info->num_subevents);
|
|
|
|
default_sync = sync;
|
|
|
|
params.properties = 0;
|
|
params.num_subevents = 1;
|
|
params.subevents = subevents;
|
|
subevents[0] = pawr_timing.subevent;
|
|
|
|
err = bt_le_per_adv_sync_subevent(sync, ¶ms);
|
|
if (err) {
|
|
printk("Failed to set subevents to sync to (err %d)\n", err);
|
|
} else {
|
|
printk("Changed sync to subevent %d\n", subevents[0]);
|
|
}
|
|
|
|
k_sem_give(&sem_per_sync);
|
|
}
|
|
|
|
static void term_cb(struct bt_le_per_adv_sync *sync,
|
|
const struct bt_le_per_adv_sync_term_info *info)
|
|
{
|
|
char le_addr[BT_ADDR_LE_STR_LEN];
|
|
|
|
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
|
|
|
|
printk("Sync terminated (reason %d)\n", info->reason);
|
|
|
|
default_sync = NULL;
|
|
|
|
k_sem_give(&sem_per_sync_lost);
|
|
}
|
|
|
|
static bool print_ad_field(struct bt_data *data, void *user_data)
|
|
{
|
|
ARG_UNUSED(user_data);
|
|
|
|
printk(" 0x%02X: ", data->type);
|
|
for (size_t i = 0; i < data->data_len; i++) {
|
|
printk("%02X", data->data[i]);
|
|
}
|
|
|
|
printk("\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
int bt_le_per_adv_set_response_data(struct bt_le_per_adv_sync *per_adv_sync,
|
|
const struct bt_le_per_adv_response_params *params,
|
|
const struct net_buf_simple *data);
|
|
|
|
static struct bt_le_per_adv_response_params rsp_params;
|
|
|
|
NET_BUF_SIMPLE_DEFINE_STATIC(rsp_buf, 247);
|
|
|
|
static void recv_cb(struct bt_le_per_adv_sync *sync,
|
|
const struct bt_le_per_adv_sync_recv_info *info, struct net_buf_simple *buf)
|
|
{
|
|
int err;
|
|
|
|
if (buf && buf->len) {
|
|
/* Echo the data back to the advertiser */
|
|
net_buf_simple_reset(&rsp_buf);
|
|
net_buf_simple_add_mem(&rsp_buf, buf->data, buf->len);
|
|
|
|
rsp_params.request_event = info->periodic_event_counter;
|
|
rsp_params.request_subevent = info->subevent;
|
|
/* Respond in current subevent and assigned response slot */
|
|
rsp_params.response_subevent = info->subevent;
|
|
rsp_params.response_slot = pawr_timing.response_slot;
|
|
|
|
printk("Indication: subevent %d, responding in slot %d\n", info->subevent,
|
|
pawr_timing.response_slot);
|
|
bt_data_parse(buf, print_ad_field, NULL);
|
|
|
|
err = bt_le_per_adv_set_response_data(sync, &rsp_params, &rsp_buf);
|
|
if (err) {
|
|
printk("Failed to send response (err %d)\n", err);
|
|
}
|
|
} else if (buf) {
|
|
printk("Received empty indication: subevent %d\n", info->subevent);
|
|
} else {
|
|
printk("Failed to receive indication: subevent %d\n", info->subevent);
|
|
}
|
|
}
|
|
|
|
static struct bt_le_per_adv_sync_cb sync_callbacks = {
|
|
.synced = sync_cb,
|
|
.term = term_cb,
|
|
.recv = recv_cb,
|
|
};
|
|
|
|
static const struct bt_uuid_128 pawr_svc_uuid =
|
|
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0));
|
|
static const struct bt_uuid_128 pawr_char_uuid =
|
|
BT_UUID_INIT_128(BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef1));
|
|
|
|
static ssize_t write_timing(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf,
|
|
uint16_t len, uint16_t offset, uint8_t flags)
|
|
{
|
|
if (offset) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET);
|
|
}
|
|
|
|
if (len != sizeof(pawr_timing)) {
|
|
return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
|
|
}
|
|
|
|
memcpy(&pawr_timing, buf, len);
|
|
|
|
printk("New timing: subevent %d, response slot %d\n", pawr_timing.subevent,
|
|
pawr_timing.response_slot);
|
|
|
|
struct bt_le_per_adv_sync_subevent_params params;
|
|
uint8_t subevents[1];
|
|
int err;
|
|
|
|
params.properties = 0;
|
|
params.num_subevents = 1;
|
|
params.subevents = subevents;
|
|
subevents[0] = pawr_timing.subevent;
|
|
|
|
if (default_sync) {
|
|
err = bt_le_per_adv_sync_subevent(default_sync, ¶ms);
|
|
if (err) {
|
|
printk("Failed to set subevents to sync to (err %d)\n", err);
|
|
} else {
|
|
printk("Changed sync to subevent %d\n", subevents[0]);
|
|
}
|
|
} else {
|
|
printk("Not synced yet\n");
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
BT_GATT_SERVICE_DEFINE(pawr_svc, BT_GATT_PRIMARY_SERVICE(&pawr_svc_uuid.uuid),
|
|
BT_GATT_CHARACTERISTIC(&pawr_char_uuid.uuid, BT_GATT_CHRC_WRITE,
|
|
BT_GATT_PERM_WRITE, NULL, write_timing,
|
|
&pawr_timing));
|
|
|
|
void connected(struct bt_conn *conn, uint8_t err)
|
|
{
|
|
printk("Connected, err 0x%02X %s\n", err, bt_hci_err_to_str(err));
|
|
|
|
if (err) {
|
|
default_conn = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
default_conn = bt_conn_ref(conn);
|
|
}
|
|
|
|
void disconnected(struct bt_conn *conn, uint8_t reason)
|
|
{
|
|
bt_conn_unref(default_conn);
|
|
default_conn = NULL;
|
|
|
|
printk("Disconnected, reason 0x%02X %s\n", reason, bt_hci_err_to_str(reason));
|
|
}
|
|
|
|
BT_CONN_CB_DEFINE(conn_cb) = {
|
|
.connected = connected,
|
|
.disconnected = disconnected,
|
|
};
|
|
|
|
static const struct bt_data ad[] = {
|
|
BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME, sizeof(CONFIG_BT_DEVICE_NAME) - 1),
|
|
};
|
|
|
|
int main(void)
|
|
{
|
|
struct bt_le_per_adv_sync_transfer_param past_param;
|
|
int err;
|
|
|
|
printk("Starting Periodic Advertising with Responses Synchronization Demo\n");
|
|
|
|
err = bt_enable(NULL);
|
|
if (err) {
|
|
printk("Bluetooth init failed (err %d)\n", err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
bt_le_per_adv_sync_cb_register(&sync_callbacks);
|
|
|
|
past_param.skip = 1;
|
|
past_param.timeout = 1000; /* 10 seconds */
|
|
past_param.options = BT_LE_PER_ADV_SYNC_TRANSFER_OPT_NONE;
|
|
err = bt_le_per_adv_sync_transfer_subscribe(NULL, &past_param);
|
|
if (err) {
|
|
printk("PAST subscribe failed (err %d)\n", err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
do {
|
|
err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_1, ad, ARRAY_SIZE(ad), NULL, 0);
|
|
if (err && err != -EALREADY) {
|
|
printk("Advertising failed to start (err %d)\n", err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
printk("Waiting for periodic sync...\n");
|
|
err = k_sem_take(&sem_per_sync, K_SECONDS(10));
|
|
if (err) {
|
|
printk("Timed out while synchronizing\n");
|
|
|
|
continue;
|
|
}
|
|
|
|
printk("Periodic sync established.\n");
|
|
|
|
err = k_sem_take(&sem_per_sync_lost, K_FOREVER);
|
|
if (err) {
|
|
printk("failed (err %d)\n", err);
|
|
|
|
return 0;
|
|
}
|
|
|
|
printk("Periodic sync lost.\n");
|
|
} while (true);
|
|
|
|
return 0;
|
|
}
|