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:
parent
262a0501de
commit
aea6afe3fa
8 changed files with 660 additions and 8 deletions
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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, ¶m->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, ¶m->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) &&
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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, ¶m);
|
||||
if (err) {
|
||||
shell_error(shell, "Failed to set periodic advertisement "
|
||||
shell_error(shell, "Failed to set periodic advertising "
|
||||
"parameters (%d)", err);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue