From aea6afe3fabe7ea5d87183e2f32de412e51a9e6e Mon Sep 17 00:00:00 2001 From: Emil Gydesen Date: Wed, 6 May 2020 22:38:23 +0200 Subject: [PATCH] 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 --- include/bluetooth/bluetooth.h | 187 ++++++++++++++- include/bluetooth/gap.h | 17 ++ include/bluetooth/hci.h | 18 +- subsys/bluetooth/Kconfig | 18 ++ subsys/bluetooth/host/hci_core.c | 385 ++++++++++++++++++++++++++++++- subsys/bluetooth/host/hci_core.h | 40 ++++ subsys/bluetooth/shell/bt.c | 2 +- tests/bluetooth/shell/prj.conf | 1 + 8 files changed, 660 insertions(+), 8 deletions(-) diff --git a/include/bluetooth/bluetooth.h b/include/bluetooth/bluetooth.h index f7792b22453..6fa31ff813a 100644 --- a/include/bluetooth/bluetooth.h +++ b/include/bluetooth/bluetooth.h @@ -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; diff --git a/include/bluetooth/gap.h b/include/bluetooth/gap.h index 10f73a66a92..c350c04e9ed 100644 --- a/include/bluetooth/gap.h +++ b/include/bluetooth/gap.h @@ -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, +}; + /** * @} */ diff --git a/include/bluetooth/hci.h b/include/bluetooth/hci.h index 5eaf2a39d28..67c6264ec03 100644 --- a/include/bluetooth/hci.h +++ b/include/bluetooth/hci.h @@ -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]; diff --git a/subsys/bluetooth/Kconfig b/subsys/bluetooth/Kconfig index ffc9683d27f..cecf28e4990 100644 --- a/subsys/bluetooth/Kconfig +++ b/subsys/bluetooth/Kconfig @@ -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" diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index f87b973581d..14042fd9cbd 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -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) && diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index 40bf66e2b5e..a3308e78932 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -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]; diff --git a/subsys/bluetooth/shell/bt.c b/subsys/bluetooth/shell/bt.c index 69ba7aca698..65c46a4652f 100644 --- a/subsys/bluetooth/shell/bt.c +++ b/subsys/bluetooth/shell/bt.c @@ -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; } diff --git a/tests/bluetooth/shell/prj.conf b/tests/bluetooth/shell/prj.conf index 1d0a88c4493..0e6f83d3fe2 100644 --- a/tests/bluetooth/shell/prj.conf +++ b/tests/bluetooth/shell/prj.conf @@ -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