Bluetooth: host: Perodic advertisement synchronization

Added support for syncing to periodic advetisements.
The API and usage is heavily inspired by the extended advertisement
and connection APIs.

Signed-off-by: Emil Gydesen <emil_gydesen@bose.com>
This commit is contained in:
Emil Gydesen 2020-05-06 22:38:23 +02:00 committed by Carles Cufí
commit aea6afe3fa
8 changed files with 660 additions and 8 deletions

View file

@ -48,6 +48,9 @@ extern "C" {
/** Opaque type representing an advertiser. */
struct bt_le_ext_adv;
/** Opaque type representing an periodic advertising sync. */
struct bt_le_per_adv_sync;
/* Don't require everyone to include conn.h */
struct bt_conn;
@ -846,7 +849,7 @@ typedef void bt_le_scan_cb_t(const bt_addr_le_t *addr, int8_t rssi,
int bt_le_per_adv_set_param(struct bt_le_ext_adv *adv,
const struct bt_le_per_adv_param *param);
/** @brief Set or update the periodic advertisement data.
/** @brief Set or update the periodic advertising data.
*
* The periodic advertisement data can only be set or updated on an
* extended advertisement set which is neither scannable, connectable nor
@ -891,6 +894,186 @@ int bt_le_per_adv_start(struct bt_le_ext_adv *adv);
*/
int bt_le_per_adv_stop(struct bt_le_ext_adv *adv);
struct bt_le_per_adv_sync_synced_info {
/** Advertiser LE address and type. */
const bt_addr_le_t *addr;
/** Advertiser SID */
uint8_t sid;
/** Periodic advertising interval (N * 1.25 ms) */
uint16_t interval;
/** Advertiser PHY */
uint8_t phy;
};
struct bt_le_per_adv_sync_term_info {
/** Advertiser LE address and type. */
const bt_addr_le_t *addr;
/** Advertiser SID */
uint8_t sid;
};
struct bt_le_per_adv_sync_recv_info {
/** Advertiser LE address and type. */
const bt_addr_le_t *addr;
/** Advertiser SID */
uint8_t sid;
/** The TX power of the advertisement. */
int8_t tx_power;
/** The RSSI of the advertisement excluding any CTE. */
int8_t rssi;
/** The Constant Tone Extension (CTE) of the advertisement */
uint8_t cte_type;
};
struct bt_le_per_adv_sync_cb {
/** @brief The periodic advertising has been successfully synced.
*
* This callback notifies the application that the periodic advertising
* set has been successfully synced, and will now start to
* receive periodic advertising reports.
*
* @param sync The periodic advertising sync object.
* @param info Information about the sync event.
*/
void (*synced)(struct bt_le_per_adv_sync *sync,
struct bt_le_per_adv_sync_synced_info *info);
/** @brief The periodic advertising sync has been terminated.
*
* This callback notifies the application that the periodic advertising
* sync has been terminated, either by local request, remote request or
* because due to missing data, e.g. by being out of range or sync.
*
* @param sync The periodic advertising sync object.
*/
void (*term)(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_term_info *info);
/** @brief Periodic advertising data received.
*
* This callback notifies the application of an periodic advertising
* report.
*
* @param sync The advertising set object.
* @param info Information about the periodic advertising event.
* @param buf Buffer containing the periodic advertising data.
*/
void (*recv)(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_recv_info *info,
struct net_buf_simple *buf);
};
/** Periodic advertising sync options */
enum {
/** Convenience value when no options are specified. */
BT_LE_PER_ADV_SYNC_OPT_NONE = 0,
/** @brief Use the periodic advertising list to sync with advertiser
*
* When this option is set, the address and SID of the parameters
* are ignored.
*/
BT_LE_PER_ADV_SYNC_OPT_USE_PER_ADV_LIST = BIT(0),
/** @brief Disables periodic advertising reports
*
* No advertisement reports will be handled until enabled.
*/
BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED = BIT(1),
/** Sync with Angle of Arrival (AoA) constant tone extension */
BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOA = BIT(2),
/** Sync with Angle of Departure (AoD) 1 us constant tone extension */
BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_1US = BIT(3),
/** Sync with Angle of Departure (AoD) 2 us constant tone extension */
BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_2US = BIT(4),
/** Do not sync to packets without a constant tone extension */
BT_LE_PER_ADV_SYNC_OPT_SYNC_ONLY_CONST_TONE_EXT = BIT(5),
};
struct bt_le_per_adv_sync_param {
/** @brief Periodic Advertiser Address
*
* Only valid if not using the periodic advertising list
*/
bt_addr_le_t addr;
/** @brief Advertiser SID
*
* Only valid if not using the periodic advertising list
*/
uint8_t sid;
/** Bit-field of periodic advertising sync options. */
uint32_t options;
/** @brief Maximum event skip
*
* Maximum number of periodic advertising events that can be
* skipped after a successful receive
*/
uint16_t skip;
/** @brief Synchronization timeout (N * 10 ms)
*
* Synchronization timeout for the periodic advertising sync.
* Range 0x000A to 0x4000 (100 ms to 163840 ms)
*/
uint16_t timeout;
};
/** @brief Get array index of an periodic advertising sync object.
*
* This function is get the index of an array of periodic advertising sync
* objects. The array has CONFIG_BT_PER_ADV_SYNC_MAX elements.
*
* @param per_adv_sync The periodic advertising sync object.
*
* @return Index of the periodic advertising sync object.
* The range of the returned value is 0..CONFIG_BT_PER_ADV_SYNC_MAX-1
*/
uint8_t bt_le_per_adv_sync_get_index(struct bt_le_per_adv_sync *per_adv_sync);
/** @brief Create a periodic advertising sync object.
*
* Create a periodic advertising sync object that can try to synchronize
* to periodic advertising reports from an advertiser. Scan shall either be
* disabled or extended scan shall be enabled.
*
* @param[in] param Periodic advertising sync parameters.
* @param[in] cb Periodic advertising callbacks.
* @param[out] out_sync Periodic advertising sync object on.
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_le_per_adv_sync_create(const struct bt_le_per_adv_sync_param *param,
const struct bt_le_per_adv_sync_cb *cb,
struct bt_le_per_adv_sync **out_sync);
/** @brief Delete periodic advertising sync.
*
* Delete the periodic advertising sync object. Can be called regardless of the
* state of the sync. If the syncing is currently syncing, the syncing is
* cancelled. If the sync has been established, it is terminated. The
* periodic advertising sync object will be invalidated afterwards.
*
* @param per_adv_sync The periodic advertising sync object.
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_le_per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync);
enum {
/** Convenience value when no options are specified. */
BT_LE_SCAN_OPT_NONE = 0,
@ -990,7 +1173,7 @@ struct bt_le_scan_recv_info {
/**
* @brief Periodic advertising interval.
*
* If 0 there is no periodic advertisement.
* If 0 there is no periodic advertising.
*/
uint16_t interval;

View file

@ -143,6 +143,23 @@ enum {
#define BT_GAP_DATA_TIME_DEFAULT 0x0148 /* 328 us */
#define BT_GAP_DATA_TIME_MAX 0x4290 /* 17040 us */
#define BT_GAP_SID_MAX 0x0F
#define BT_GAP_PER_ADV_MAX_MAX_SKIP 0x01F3
#define BT_GAP_PER_ADV_MAX_MAX_TIMEOUT 0x4000
/** Constant Tone Extension (CTE) types */
enum {
/** Angle of Arrival */
BT_GAP_CTE_AOA = 0x00,
/** Angle of Departure with 1 us slots */
BT_GAP_CTE_AOD_1US = 0x01,
/** Angle of Departure with 2 us slots */
BT_GAP_CTE_AOD_2US = 0x02,
/** No extensions */
BT_GAP_CTE_NONE = 0xFF,
};
/**
* @}
*/

View file

@ -143,7 +143,8 @@ struct bt_hci_cmd_hdr {
BT_LE_FEAT_BIT_PRIVACY)
#define BT_FEAT_LE_EXT_ADV(feat) BT_LE_FEAT_TEST(feat, \
BT_LE_FEAT_BIT_EXT_ADV)
#define BT_FEAT_LE_EXT_PER_ADV(feat) BT_LE_FEAT_TEST(feat, \
BT_LE_FEAT_BIT_PER_ADV)
/* LE States */
#define BT_LE_STATES_SLAVE_CONN_ADV(states) (states & 0x0000004000000000)
@ -1199,14 +1200,23 @@ struct bt_hci_cp_le_ext_create_conn {
struct bt_hci_ext_conn_phy p[0];
} __packed;
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST BIT(0)
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED BIT(1)
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOA BIT(0)
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_1US BIT(1)
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_2US BIT(2)
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_CTE BIT(3)
#define BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_ONLY_CTE BIT(4)
#define BT_HCI_OP_LE_PER_ADV_CREATE_SYNC BT_OP(BT_OGF_LE, 0x0044)
struct bt_hci_cp_le_per_adv_create_sync {
uint8_t filter_policy;
uint8_t options;
uint8_t sid;
bt_addr_le_t addr;
uint16_t skip;
uint16_t sync_timeout;
uint8_t unused;
uint8_t cte_type;
} __packed;
#define BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL BT_OP(BT_OGF_LE, 0x0045)
@ -1658,7 +1668,7 @@ struct bt_hci_evt_le_per_advertising_report {
uint16_t handle;
int8_t tx_power;
int8_t rssi;
uint8_t unused;
uint8_t cte_type;
uint8_t data_status;
uint8_t length;
uint8_t data[0];

View file

@ -139,6 +139,24 @@ config BT_PER_ADV
intervals. Scanners can synchronize to the periodic advertisements
to periodically get the data.
config BT_PER_ADV_SYNC
bool "Periodic advertising sync support [EXPERIMENTAL]"
depends on BT_OBSERVER
help
Select this to enable Periodic Advertising Sync API support.
Syncing with a periodic advertiser allows the device to periodically
and deterministic receive data from that device in a connectionless
manner.
if BT_PER_ADV_SYNC
config BT_PER_ADV_SYNC_MAX
int "Maximum number of simultaneous periodic advertising syncs"
range 1 64
default 1
help
Maximum number of simultaneous periodic advertising syncs supported.
endif # BT_PER_ADV_SYNC
endif # BT_EXT_ADV
menu "Observer"

View file

@ -96,7 +96,12 @@ static sys_slist_t scan_cbs = SYS_SLIST_STATIC_INIT(&scan_cbs);
#if defined(CONFIG_BT_EXT_ADV)
static struct bt_le_ext_adv adv_pool[CONFIG_BT_EXT_ADV_MAX_ADV_SET];
#endif
#if defined(CONFIG_BT_PER_ADV_SYNC)
static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void);
static struct bt_le_per_adv_sync per_adv_sync_pool[CONFIG_BT_PER_ADV_SYNC_MAX];
#endif /* defined(CONFIG_BT_PER_ADV) */
#endif /* defined(CONFIG_BT_EXT_ADV) */
#if defined(CONFIG_BT_HCI_VS_EVT_USER)
static bt_hci_vnd_evt_cb_t *hci_vnd_evt_cb;
@ -4716,6 +4721,12 @@ int bt_le_scan_update(bool fast_scan)
return start_passive_scan(fast_scan);
}
#if defined(CONFIG_BT_PER_ADV_SYNC)
if (get_pending_per_adv_sync()) {
return start_passive_scan(fast_scan);
}
#endif
return 0;
}
@ -4927,6 +4938,170 @@ static void le_adv_ext_report(struct net_buf *buf)
net_buf_pull(buf, evt->length);
}
}
#if defined(CONFIG_BT_PER_ADV_SYNC)
static void per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync)
{
atomic_clear(per_adv_sync->flags);
per_adv_sync->cb = NULL; /* disable callbacks */
}
static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void)
{
for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) {
if (atomic_test_bit(per_adv_sync_pool[i].flags,
BT_PER_ADV_SYNC_SYNCING)) {
return &per_adv_sync_pool[i];
}
}
return NULL;
}
static struct bt_le_per_adv_sync *get_per_adv_sync(uint16_t handle)
{
for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) {
if (per_adv_sync_pool[i].handle == handle &&
atomic_test_bit(per_adv_sync_pool[i].flags,
BT_PER_ADV_SYNC_SYNCED)) {
return &per_adv_sync_pool[i];
}
}
return NULL;
}
static void le_per_adv_report(struct net_buf *buf)
{
struct bt_hci_evt_le_per_advertising_report *evt;
struct bt_le_per_adv_sync *per_adv_sync;
struct bt_le_per_adv_sync_recv_info info;
if (buf->len < sizeof(*evt)) {
BT_ERR("Unexpected end of buffer");
return;
}
evt = net_buf_pull_mem(buf, sizeof(*evt));
per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->handle));
if (!per_adv_sync) {
BT_ERR("Unknown handle 0x%04X for periodic advertising report",
sys_le16_to_cpu(evt->handle));
return;
}
info.tx_power = evt->tx_power;
info.rssi = evt->rssi;
info.cte_type = evt->cte_type;
info.addr = &per_adv_sync->addr;
if (per_adv_sync->cb && per_adv_sync->cb->recv) {
per_adv_sync->cb->recv(per_adv_sync, &info, &buf->b);
}
}
static int per_adv_sync_terminate(uint16_t handle)
{
struct bt_hci_cp_le_per_adv_terminate_sync *cp;
struct net_buf *buf;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC,
sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
(void)memset(cp, 0, sizeof(*cp));
cp->handle = sys_cpu_to_le16(handle);
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_TERMINATE_SYNC, buf,
NULL);
}
static void le_per_adv_sync_established(struct net_buf *buf)
{
struct bt_hci_evt_le_per_adv_sync_established *evt =
(struct bt_hci_evt_le_per_adv_sync_established *)buf->data;
struct bt_le_per_adv_sync_synced_info sync_info;
struct bt_le_per_adv_sync *pending_per_adv_sync;
int err;
err = bt_le_scan_update(false);
if (err) {
BT_ERR("Could not stop scan (%d)", err);
}
if (evt->status == BT_HCI_ERR_OP_CANCELLED_BY_HOST) {
/* Cancelled locally, don't call CB */
return;
}
pending_per_adv_sync = get_pending_per_adv_sync();
if (!pending_per_adv_sync ||
pending_per_adv_sync->sid != evt->sid ||
bt_addr_le_cmp(&pending_per_adv_sync->addr, &evt->adv_addr)) {
BT_ERR("Unexpected per adv sync established event");
per_adv_sync_terminate(sys_le16_to_cpu(evt->handle));
return;
}
atomic_clear_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING);
atomic_set_bit(pending_per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED);
pending_per_adv_sync->handle = sys_le16_to_cpu(evt->handle);
pending_per_adv_sync->interval = sys_le16_to_cpu(evt->interval);
pending_per_adv_sync->clock_accuracy =
sys_le16_to_cpu(evt->clock_accuracy);
pending_per_adv_sync->phy = evt->phy;
sync_info.interval = pending_per_adv_sync->interval;
sync_info.phy = get_phy(pending_per_adv_sync->phy);
sync_info.addr = &pending_per_adv_sync->addr;
sync_info.sid = pending_per_adv_sync->sid;
if (pending_per_adv_sync->cb && pending_per_adv_sync->cb->synced) {
pending_per_adv_sync->cb->synced(pending_per_adv_sync,
&sync_info);
}
}
static void le_per_adv_sync_lost(struct net_buf *buf)
{
struct bt_hci_evt_le_per_adv_sync_lost *evt =
(struct bt_hci_evt_le_per_adv_sync_lost *)buf->data;
struct bt_le_per_adv_sync *per_adv_sync;
struct bt_le_per_adv_sync_term_info term_info;
per_adv_sync = get_per_adv_sync(sys_le16_to_cpu(evt->handle));
if (!per_adv_sync) {
BT_ERR("Unknown handle 0x%04Xfor periodic adv sync lost",
sys_le16_to_cpu(evt->handle));
return;
}
term_info.addr = &per_adv_sync->addr;
term_info.sid = per_adv_sync->sid;
/* Clearing bit before callback, so the caller will be able to restart
* sync in the callback
*/
atomic_clear_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED);
if (per_adv_sync->cb && per_adv_sync->cb->term) {
per_adv_sync->cb->term(per_adv_sync, &term_info);
}
per_adv_sync_delete(per_adv_sync);
}
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
#endif /* defined(CONFIG_BT_EXT_ADV) */
static void le_adv_report(struct net_buf *buf)
@ -5192,6 +5367,15 @@ static const struct event_handler meta_events[] = {
EVENT_HANDLER(BT_HCI_EVT_LE_EXT_ADVERTISING_REPORT, le_adv_ext_report,
sizeof(struct bt_hci_evt_le_ext_advertising_report)),
#endif /* defined(CONFIG_BT_OBSERVER) */
#if defined(CONFIG_BT_PER_ADV_SYNC)
EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADV_SYNC_ESTABLISHED,
le_per_adv_sync_established,
sizeof(struct bt_hci_evt_le_per_adv_sync_established)),
EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADVERTISING_REPORT, le_per_adv_report,
sizeof(struct bt_hci_evt_le_per_advertising_report)),
EVENT_HANDLER(BT_HCI_EVT_LE_PER_ADV_SYNC_LOST, le_per_adv_sync_lost,
sizeof(struct bt_hci_evt_le_per_adv_sync_lost)),
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
#endif /* defined(CONFIG_BT_EXT_ADV) */
};
@ -5613,6 +5797,11 @@ static int le_set_event_mask(void)
mask |= BT_EVT_MASK_LE_SCAN_REQ_RECEIVED;
mask |= BT_EVT_MASK_LE_EXT_ADVERTISING_REPORT;
mask |= BT_EVT_MASK_LE_SCAN_TIMEOUT;
if (IS_ENABLED(CONFIG_BT_PER_ADV_SYNC)) {
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED;
mask |= BT_EVT_MASK_LE_PER_ADVERTISING_REPORT;
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_LOST;
}
}
if (IS_ENABLED(CONFIG_BT_CONN)) {
@ -7346,6 +7535,200 @@ int bt_le_per_adv_stop(struct bt_le_ext_adv *adv)
return bt_le_per_adv_enable(adv, false);
}
#if defined(CONFIG_BT_PER_ADV_SYNC)
uint8_t bt_le_per_adv_sync_get_index(struct bt_le_per_adv_sync *per_adv_sync)
{
uintptr_t index = per_adv_sync - per_adv_sync_pool;
__ASSERT(per_adv_sync >= per_adv_sync_pool &&
index < ARRAY_SIZE(per_adv_sync_pool),
"Invalid per_adv_sync pointer");
return index;
}
static struct bt_le_per_adv_sync *per_adv_sync_new(void)
{
struct bt_le_per_adv_sync *per_adv_sync = NULL;
for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) {
if (!atomic_test_bit(per_adv_sync_pool[i].flags,
BT_PER_ADV_SYNC_CREATED)) {
per_adv_sync = &per_adv_sync_pool[i];
break;
}
}
if (!per_adv_sync) {
return NULL;
}
(void)memset(per_adv_sync, 0, sizeof(*per_adv_sync));
atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_CREATED);
return per_adv_sync;
}
int bt_le_per_adv_sync_create(const struct bt_le_per_adv_sync_param *param,
const struct bt_le_per_adv_sync_cb *cb,
struct bt_le_per_adv_sync **out_sync)
{
struct bt_hci_cp_le_per_adv_create_sync *cp;
struct net_buf *buf;
struct bt_le_per_adv_sync *per_adv_sync;
int err;
if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) {
return -ENOTSUP;
}
if (get_pending_per_adv_sync()) {
return -EBUSY;
}
if (param->sid > BT_GAP_SID_MAX ||
param->skip > BT_GAP_PER_ADV_MAX_MAX_SKIP ||
param->timeout > BT_GAP_PER_ADV_MAX_MAX_TIMEOUT) {
return -EINVAL;
}
if (atomic_test_bit(bt_dev.flags, BT_DEV_EXPLICIT_SCAN)) {
return -EBUSY;
}
per_adv_sync = per_adv_sync_new();
if (!per_adv_sync) {
return -ENOMEM;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC, sizeof(*cp));
if (!buf) {
per_adv_sync_delete(per_adv_sync);
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
(void)memset(cp, 0, sizeof(*cp));
bt_addr_le_copy(&cp->addr, &param->addr);
if (param->options & BT_LE_PER_ADV_SYNC_OPT_USE_PER_ADV_LIST) {
cp->options |= BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_USE_LIST;
}
if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOA) {
cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOA;
}
if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_1US) {
cp->cte_type |=
BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_1US;
}
if (param->options & BT_LE_PER_ADV_SYNC_OPT_DONT_SYNC_AOD_2US) {
cp->cte_type |=
BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_NO_AOD_2US;
}
if (param->options & BT_LE_PER_ADV_SYNC_OPT_SYNC_ONLY_CONST_TONE_EXT) {
cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_ONLY_CTE;
}
cp->sid = param->sid;
cp->skip = sys_cpu_to_le16(param->skip);
cp->sync_timeout = sys_cpu_to_le16(param->timeout);
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC, buf, NULL);
if (err) {
per_adv_sync_delete(per_adv_sync);
return err;
}
atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCING);
/* Syncing requires that scan is enabled. If the caller doesn't enable
* scan first, we enable it here, and disable it once the sync has been
* established. We don't need to use any callbacks since we rely on
* the advertiser address in the sync params.
*/
if (!atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
err = bt_le_scan_update(false);
if (err) {
bt_le_per_adv_sync_delete(per_adv_sync);
return err;
}
}
*out_sync = per_adv_sync;
bt_addr_le_copy(&per_adv_sync->addr, &param->addr);
per_adv_sync->sid = param->sid;
per_adv_sync->cb = cb;
return 0;
}
static int bt_le_per_adv_sync_create_cancel(
struct bt_le_per_adv_sync *per_adv_sync)
{
struct net_buf *buf;
int err;
if (get_pending_per_adv_sync() != per_adv_sync) {
return -EINVAL;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL, 0);
if (!buf) {
return -ENOBUFS;
}
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_CREATE_SYNC_CANCEL, buf,
NULL);
if (err) {
return err;
}
return 0;
}
static int bt_le_per_adv_sync_terminate(struct bt_le_per_adv_sync *per_adv_sync)
{
int err;
if (!atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) {
return -EINVAL;
}
err = per_adv_sync_terminate(per_adv_sync->handle);
if (err) {
return err;
}
return 0;
}
int bt_le_per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync)
{
int err = 0;
if (atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) {
err = bt_le_per_adv_sync_terminate(per_adv_sync);
} else if (get_pending_per_adv_sync() == per_adv_sync) {
err = bt_le_per_adv_sync_create_cancel(per_adv_sync);
}
if (err) {
return err;
}
per_adv_sync_delete(per_adv_sync);
return err;
}
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
static bool valid_adv_ext_param(const struct bt_le_adv_param *param)
{
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&

View file

@ -132,6 +132,46 @@ struct bt_le_ext_adv {
#endif /* defined(CONFIG_BT_EXT_ADV) */
};
enum {
/** Periodic Advertising Sync has been created in the host. */
BT_PER_ADV_SYNC_CREATED,
/** Periodic advertising is in sync and can be terminated */
BT_PER_ADV_SYNC_SYNCED,
/** Periodic advertising is attempting sync sync */
BT_PER_ADV_SYNC_SYNCING,
BT_PER_ADV_SYNC_NUM_FLAGS,
};
struct bt_le_per_adv_sync {
/** Periodic Advertiser Address */
bt_addr_le_t addr;
/** Advertiser SID */
uint8_t sid;
/** Sync handle */
uint16_t handle;
/** Periodic advertising interval (N * 1.25MS) */
uint16_t interval;
/** Periodic advertising advertiser clock accuracy (ppm) */
uint16_t clock_accuracy;
/** Advertiser PHY */
uint8_t phy;
/** Flags */
ATOMIC_DEFINE(flags, BT_PER_ADV_SYNC_NUM_FLAGS);
/** Callbacks */
const struct bt_le_per_adv_sync_cb *cb;
};
struct bt_dev_le {
/* LE features */
uint8_t features[8];

View file

@ -1319,7 +1319,7 @@ static int cmd_per_adv_param(const struct shell *shell, size_t argc,
err = bt_le_per_adv_set_param(adv, &param);
if (err) {
shell_error(shell, "Failed to set periodic advertisement "
shell_error(shell, "Failed to set periodic advertising "
"parameters (%d)", err);
return -ENOEXEC;
}

View file

@ -38,6 +38,7 @@ CONFIG_SETTINGS=y
CONFIG_BT_EXT_ADV=y
CONFIG_BT_EXT_ADV_LEGACY_SUPPORT=y
CONFIG_BT_PER_ADV=y
CONFIG_BT_PER_ADV_SYNC=y
CONFIG_BT_USER_DATA_LEN_UPDATE=y
CONFIG_BT_AUTO_DATA_LEN_UPDATE=y