Bluetooth: host: Periodic Advertising Sync Transfer
Adds support for the periodic advertising sync transfer (PAST) feature, which allows a synced device, or an advertiser, to transfer synchronization of a periodic advertising train to a connected device. Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
parent
635230c725
commit
1dada244c1
4 changed files with 585 additions and 26 deletions
|
@ -981,6 +981,21 @@ struct bt_le_per_adv_sync_synced_info {
|
||||||
|
|
||||||
/** True if receiving periodic advertisements, false otherwise. */
|
/** True if receiving periodic advertisements, false otherwise. */
|
||||||
bool recv_enabled;
|
bool recv_enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Service Data provided by the peer when sync is transferred
|
||||||
|
*
|
||||||
|
* Will always be 0 when the sync is locally created.
|
||||||
|
*/
|
||||||
|
uint16_t service_data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Peer that transferred the periodic advertising sync
|
||||||
|
*
|
||||||
|
* Will always be 0 when the sync is locally created.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct bt_conn *conn;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bt_le_per_adv_sync_term_info {
|
struct bt_le_per_adv_sync_term_info {
|
||||||
|
@ -1220,6 +1235,124 @@ int bt_le_per_adv_sync_recv_enable(struct bt_le_per_adv_sync *per_adv_sync);
|
||||||
*/
|
*/
|
||||||
int bt_le_per_adv_sync_recv_disable(struct bt_le_per_adv_sync *per_adv_sync);
|
int bt_le_per_adv_sync_recv_disable(struct bt_le_per_adv_sync *per_adv_sync);
|
||||||
|
|
||||||
|
/** Periodic Advertising Sync Transfer options */
|
||||||
|
enum {
|
||||||
|
/** Convenience value when no options are specified. */
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_NONE = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief No Angle of Arrival (AoA)
|
||||||
|
*
|
||||||
|
* Do not sync with Angle of Arrival (AoA) constant tone extension
|
||||||
|
**/
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOA = BIT(0),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief No Angle of Departure (AoD) 1 us
|
||||||
|
*
|
||||||
|
* Do not sync with Angle of Departure (AoD) 1 us
|
||||||
|
* constant tone extension
|
||||||
|
*/
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_1US = BIT(1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief No Angle of Departure (AoD) 2
|
||||||
|
*
|
||||||
|
* Do not sync with Angle of Departure (AoD) 2 us
|
||||||
|
* constant tone extension
|
||||||
|
*/
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_2US = BIT(2),
|
||||||
|
|
||||||
|
/** Only sync to packets with constant tone extension */
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_ONLY_CTE = BIT(3),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct bt_le_per_adv_sync_transfer_param {
|
||||||
|
/**
|
||||||
|
* @brief Maximum event skip
|
||||||
|
*
|
||||||
|
* The number of periodic advertising packets 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;
|
||||||
|
|
||||||
|
/** Periodic Advertising Sync Transfer options */
|
||||||
|
uint32_t options;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transfer the periodic advertising sync information to a peer device.
|
||||||
|
*
|
||||||
|
* This will allow another device to quickly synchronize to the same periodic
|
||||||
|
* advertising train that this device is currently synced to.
|
||||||
|
*
|
||||||
|
* @param per_adv_sync The periodic advertising sync to transfer.
|
||||||
|
* @param conn The peer device that will receive the sync information.
|
||||||
|
* @param service_data Application service data provided to the remote host.
|
||||||
|
*
|
||||||
|
* @return Zero on success or (negative) error code otherwise.
|
||||||
|
*/
|
||||||
|
int bt_le_per_adv_sync_transfer(const struct bt_le_per_adv_sync *per_adv_sync,
|
||||||
|
const struct bt_conn *conn,
|
||||||
|
uint16_t service_data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Transfer the information about a periodic advertising set.
|
||||||
|
*
|
||||||
|
* This will allow another device to quickly synchronize to periodic
|
||||||
|
* advertising set from this device.
|
||||||
|
*
|
||||||
|
* @param adv The periodic advertising set to transfer info of.
|
||||||
|
* @param conn The peer device that will receive the information.
|
||||||
|
* @param service_data Application service data provided to the remote host.
|
||||||
|
*
|
||||||
|
* @return Zero on success or (negative) error code otherwise.
|
||||||
|
*/
|
||||||
|
int bt_le_per_adv_set_info_transfer(const struct bt_le_ext_adv *adv,
|
||||||
|
const struct bt_conn *conn,
|
||||||
|
uint16_t service_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Subscribe to periodic advertising sync transfers (PASTs).
|
||||||
|
*
|
||||||
|
* Sets the parameters and allow other devices to transfer periodic advertising
|
||||||
|
* syncs.
|
||||||
|
*
|
||||||
|
* @param conn The connection to set the parameters for. If NULL default
|
||||||
|
* parameters for all connections will be set. Parameters set
|
||||||
|
* for specific connection will always have precedence.
|
||||||
|
* @param param The periodic advertising sync transfer parameters.
|
||||||
|
*
|
||||||
|
* @return Zero on success or (negative) error code otherwise.
|
||||||
|
*/
|
||||||
|
int bt_le_per_adv_sync_transfer_subscribe(
|
||||||
|
const struct bt_conn *conn,
|
||||||
|
const struct bt_le_per_adv_sync_transfer_param *param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Unsubscribe from periodic advertising sync transfers (PASTs).
|
||||||
|
*
|
||||||
|
* Remove the parameters that allow other devices to transfer periodic
|
||||||
|
* advertising syncs.
|
||||||
|
*
|
||||||
|
* @param conn The connection to remove the parameters for. If NULL default
|
||||||
|
* parameters for all connections will be removed. Unsubscribing
|
||||||
|
* for a specific device, will still allow other devices to
|
||||||
|
* transfer periodic advertising syncs.
|
||||||
|
*
|
||||||
|
* @return Zero on success or (negative) error code otherwise.
|
||||||
|
*/
|
||||||
|
int bt_le_per_adv_sync_transfer_unsubscribe(const struct bt_conn *conn);
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
/** Convenience value when no options are specified. */
|
/** Convenience value when no options are specified. */
|
||||||
BT_LE_SCAN_OPT_NONE = 0,
|
BT_LE_SCAN_OPT_NONE = 0,
|
||||||
|
|
|
@ -151,8 +151,8 @@ struct bt_hci_cmd_hdr {
|
||||||
#define BT_LE_FEAT_BIT_ANT_SWITCH_TX_AOD 21
|
#define BT_LE_FEAT_BIT_ANT_SWITCH_TX_AOD 21
|
||||||
#define BT_LE_FEAT_BIT_ANT_SWITCH_RX_AOA 22
|
#define BT_LE_FEAT_BIT_ANT_SWITCH_RX_AOA 22
|
||||||
#define BT_LE_FEAT_BIT_RX_CTE 23
|
#define BT_LE_FEAT_BIT_RX_CTE 23
|
||||||
#define BT_LE_FEAT_BIT_PERIODIC_SYNC_XFER_SEND 24
|
#define BT_LE_FEAT_BIT_PAST_SEND 24
|
||||||
#define BT_LE_FEAT_BIT_PERIODIC_SYNC_XFER_RECV 25
|
#define BT_LE_FEAT_BIT_PAST_RECV 25
|
||||||
#define BT_LE_FEAT_BIT_SCA_UPDATE 26
|
#define BT_LE_FEAT_BIT_SCA_UPDATE 26
|
||||||
#define BT_LE_FEAT_BIT_REMOTE_PUB_KEY_VALIDATE 27
|
#define BT_LE_FEAT_BIT_REMOTE_PUB_KEY_VALIDATE 27
|
||||||
#define BT_LE_FEAT_BIT_CIS_MASTER 28
|
#define BT_LE_FEAT_BIT_CIS_MASTER 28
|
||||||
|
@ -185,6 +185,10 @@ struct bt_hci_cmd_hdr {
|
||||||
BT_LE_FEAT_BIT_EXT_ADV)
|
BT_LE_FEAT_BIT_EXT_ADV)
|
||||||
#define BT_FEAT_LE_EXT_PER_ADV(feat) BT_LE_FEAT_TEST(feat, \
|
#define BT_FEAT_LE_EXT_PER_ADV(feat) BT_LE_FEAT_TEST(feat, \
|
||||||
BT_LE_FEAT_BIT_PER_ADV)
|
BT_LE_FEAT_BIT_PER_ADV)
|
||||||
|
#define BT_FEAT_LE_PAST_SEND(feat) BT_LE_FEAT_TEST(feat, \
|
||||||
|
BT_LE_FEAT_BIT_PAST_SEND)
|
||||||
|
#define BT_FEAT_LE_PAST_RECV(feat) BT_LE_FEAT_TEST(feat, \
|
||||||
|
BT_LE_FEAT_BIT_PAST_RECV)
|
||||||
#define BT_FEAT_LE_CIS_MASTER(feat) BT_LE_FEAT_TEST(feat, \
|
#define BT_FEAT_LE_CIS_MASTER(feat) BT_LE_FEAT_TEST(feat, \
|
||||||
BT_LE_FEAT_BIT_CIS_MASTER)
|
BT_LE_FEAT_BIT_CIS_MASTER)
|
||||||
#define BT_FEAT_LE_CIS_SLAVE(feat) BT_LE_FEAT_TEST(feat, \
|
#define BT_FEAT_LE_CIS_SLAVE(feat) BT_LE_FEAT_TEST(feat, \
|
||||||
|
@ -1341,6 +1345,66 @@ struct bt_hci_cp_le_set_per_adv_recv_enable {
|
||||||
uint8_t enable;
|
uint8_t enable;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER BT_OP(BT_OGF_LE, 0x005a)
|
||||||
|
struct bt_hci_cp_le_per_adv_sync_transfer {
|
||||||
|
uint16_t conn_handle;
|
||||||
|
uint16_t service_data;
|
||||||
|
uint16_t sync_handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_hci_rp_le_per_adv_sync_transfer {
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t conn_handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BT_HCI_OP_LE_PER_ADV_SET_INFO_TRANSFER BT_OP(BT_OGF_LE, 0x005b)
|
||||||
|
struct bt_hci_cp_le_per_adv_set_info_transfer {
|
||||||
|
uint16_t conn_handle;
|
||||||
|
uint16_t service_data;
|
||||||
|
uint8_t adv_handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_hci_rp_le_per_adv_set_info_transfer {
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t conn_handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BT_HCI_LE_PAST_MODE_NO_SYNC 0x00
|
||||||
|
#define BT_HCI_LE_PAST_MODE_NO_REPORTS 0x01
|
||||||
|
#define BT_HCI_LE_PAST_MODE_SYNC 0x02
|
||||||
|
|
||||||
|
#define BT_HCI_LE_PAST_CTE_TYPE_NO_AOA BIT(0)
|
||||||
|
#define BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_1US BIT(1)
|
||||||
|
#define BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_2US BIT(2)
|
||||||
|
#define BT_HCI_LE_PAST_CTE_TYPE_NO_CTE BIT(3)
|
||||||
|
#define BT_HCI_LE_PAST_CTE_TYPE_ONLY_CTE BIT(4)
|
||||||
|
|
||||||
|
#define BT_HCI_OP_LE_PAST_PARAM BT_OP(BT_OGF_LE, 0x005c)
|
||||||
|
struct bt_hci_cp_le_past_param {
|
||||||
|
uint16_t conn_handle;
|
||||||
|
uint8_t mode;
|
||||||
|
uint16_t skip;
|
||||||
|
uint16_t timeout;
|
||||||
|
uint8_t cte_type;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_hci_rp_le_past_param {
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t conn_handle;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
#define BT_HCI_OP_LE_DEFAULT_PAST_PARAM BT_OP(BT_OGF_LE, 0x005d)
|
||||||
|
struct bt_hci_cp_le_default_past_param {
|
||||||
|
uint8_t mode;
|
||||||
|
uint16_t skip;
|
||||||
|
uint16_t timeout;
|
||||||
|
uint8_t cte_type;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
|
struct bt_hci_rp_le_default_past_param {
|
||||||
|
uint8_t status;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define BT_HCI_OP_LE_READ_BUFFER_SIZE_V2 BT_OP(BT_OGF_LE, 0x0060)
|
#define BT_HCI_OP_LE_READ_BUFFER_SIZE_V2 BT_OP(BT_OGF_LE, 0x0060)
|
||||||
struct bt_hci_rp_le_read_buffer_size_v2 {
|
struct bt_hci_rp_le_read_buffer_size_v2 {
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
|
@ -2000,6 +2064,19 @@ struct bt_hci_evt_le_chan_sel_algo {
|
||||||
uint8_t chan_sel_algo;
|
uint8_t chan_sel_algo;
|
||||||
} __packed;
|
} __packed;
|
||||||
|
|
||||||
|
#define BT_HCI_EVT_LE_PAST_RECEIVED 0x18
|
||||||
|
struct bt_hci_evt_le_past_received {
|
||||||
|
uint8_t status;
|
||||||
|
uint16_t conn_handle;
|
||||||
|
uint16_t service_data;
|
||||||
|
uint16_t sync_handle;
|
||||||
|
uint8_t adv_sid;
|
||||||
|
bt_addr_le_t addr;
|
||||||
|
uint8_t phy;
|
||||||
|
uint16_t interval;
|
||||||
|
uint8_t clock_accuracy;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
#define BT_HCI_EVT_LE_CIS_ESTABLISHED 0x19
|
#define BT_HCI_EVT_LE_CIS_ESTABLISHED 0x19
|
||||||
struct bt_hci_evt_le_cis_established {
|
struct bt_hci_evt_le_cis_established {
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
|
@ -2173,6 +2250,7 @@ struct bt_hci_evt_le_biginfo_adv_report {
|
||||||
#define BT_EVT_MASK_LE_ADV_SET_TERMINATED BT_EVT_BIT(17)
|
#define BT_EVT_MASK_LE_ADV_SET_TERMINATED BT_EVT_BIT(17)
|
||||||
#define BT_EVT_MASK_LE_SCAN_REQ_RECEIVED BT_EVT_BIT(18)
|
#define BT_EVT_MASK_LE_SCAN_REQ_RECEIVED BT_EVT_BIT(18)
|
||||||
#define BT_EVT_MASK_LE_CHAN_SEL_ALGO BT_EVT_BIT(19)
|
#define BT_EVT_MASK_LE_CHAN_SEL_ALGO BT_EVT_BIT(19)
|
||||||
|
#define BT_EVT_MASK_LE_PAST_RECEIVED BT_EVT_BIT(23)
|
||||||
#define BT_EVT_MASK_LE_CIS_ESTABLISHED BT_EVT_BIT(24)
|
#define BT_EVT_MASK_LE_CIS_ESTABLISHED BT_EVT_BIT(24)
|
||||||
#define BT_EVT_MASK_LE_CIS_REQ BT_EVT_BIT(25)
|
#define BT_EVT_MASK_LE_CIS_REQ BT_EVT_BIT(25)
|
||||||
#define BT_EVT_MASK_LE_BIG_COMPLETE BT_EVT_BIT(26)
|
#define BT_EVT_MASK_LE_BIG_COMPLETE BT_EVT_BIT(26)
|
||||||
|
|
|
@ -4544,6 +4544,28 @@ static void per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync)
|
||||||
atomic_clear(per_adv_sync->flags);
|
atomic_clear(per_adv_sync->flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
static struct bt_le_per_adv_sync *get_pending_per_adv_sync(void)
|
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++) {
|
for (int i = 0; i < ARRAY_SIZE(per_adv_sync_pool); i++) {
|
||||||
|
@ -4706,6 +4728,7 @@ static void le_per_adv_sync_established(struct net_buf *buf)
|
||||||
sys_le16_to_cpu(evt->clock_accuracy);
|
sys_le16_to_cpu(evt->clock_accuracy);
|
||||||
pending_per_adv_sync->phy = evt->phy;
|
pending_per_adv_sync->phy = evt->phy;
|
||||||
|
|
||||||
|
memset(&sync_info, 0, sizeof(sync_info));
|
||||||
sync_info.interval = pending_per_adv_sync->interval;
|
sync_info.interval = pending_per_adv_sync->interval;
|
||||||
sync_info.phy = get_phy(pending_per_adv_sync->phy);
|
sync_info.phy = get_phy(pending_per_adv_sync->phy);
|
||||||
sync_info.addr = &pending_per_adv_sync->addr;
|
sync_info.addr = &pending_per_adv_sync->addr;
|
||||||
|
@ -4753,6 +4776,58 @@ static void le_per_adv_sync_lost(struct net_buf *buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void le_past_received(struct net_buf *buf)
|
||||||
|
{
|
||||||
|
struct bt_hci_evt_le_past_received *evt =
|
||||||
|
(struct bt_hci_evt_le_past_received *)buf->data;
|
||||||
|
struct bt_le_per_adv_sync_synced_info sync_info;
|
||||||
|
struct bt_le_per_adv_sync_cb *listener;
|
||||||
|
struct bt_le_per_adv_sync *per_adv_sync;
|
||||||
|
|
||||||
|
if (evt->status) {
|
||||||
|
/* No sync created, don't notify app */
|
||||||
|
BT_DBG("PAST receive failed with status 0x%02X", evt->status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_info.conn = bt_conn_lookup_handle(
|
||||||
|
sys_le16_to_cpu(evt->conn_handle));
|
||||||
|
|
||||||
|
if (!sync_info.conn) {
|
||||||
|
BT_ERR("Could not lookup connection handle from PAST");
|
||||||
|
per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
per_adv_sync = per_adv_sync_new();
|
||||||
|
if (!per_adv_sync) {
|
||||||
|
BT_WARN("Could not allocate new PA sync from PAST");
|
||||||
|
per_adv_sync_terminate(sys_le16_to_cpu(evt->sync_handle));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_set_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED);
|
||||||
|
|
||||||
|
per_adv_sync->handle = sys_le16_to_cpu(evt->sync_handle);
|
||||||
|
per_adv_sync->interval = sys_le16_to_cpu(evt->interval);
|
||||||
|
per_adv_sync->clock_accuracy = sys_le16_to_cpu(evt->clock_accuracy);
|
||||||
|
per_adv_sync->phy = evt->phy;
|
||||||
|
bt_addr_le_copy(&per_adv_sync->addr, &evt->addr);
|
||||||
|
per_adv_sync->sid = evt->adv_sid;
|
||||||
|
|
||||||
|
sync_info.interval = per_adv_sync->interval;
|
||||||
|
sync_info.phy = get_phy(per_adv_sync->phy);
|
||||||
|
sync_info.addr = &per_adv_sync->addr;
|
||||||
|
sync_info.sid = per_adv_sync->sid;
|
||||||
|
sync_info.service_data = sys_le16_to_cpu(evt->service_data);
|
||||||
|
|
||||||
|
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
|
||||||
|
if (listener->synced) {
|
||||||
|
listener->synced(per_adv_sync, &sync_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
||||||
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
||||||
|
|
||||||
|
@ -5027,6 +5102,8 @@ static const struct event_handler meta_events[] = {
|
||||||
sizeof(struct bt_hci_evt_le_per_advertising_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,
|
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)),
|
sizeof(struct bt_hci_evt_le_per_adv_sync_lost)),
|
||||||
|
EVENT_HANDLER(BT_HCI_EVT_LE_PAST_RECEIVED, le_past_received,
|
||||||
|
sizeof(struct bt_hci_evt_le_past_received)),
|
||||||
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
||||||
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
#endif /* defined(CONFIG_BT_EXT_ADV) */
|
||||||
#if defined(CONFIG_BT_ISO)
|
#if defined(CONFIG_BT_ISO)
|
||||||
|
@ -5523,6 +5600,7 @@ static int le_set_event_mask(void)
|
||||||
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED;
|
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_ESTABLISHED;
|
||||||
mask |= BT_EVT_MASK_LE_PER_ADVERTISING_REPORT;
|
mask |= BT_EVT_MASK_LE_PER_ADVERTISING_REPORT;
|
||||||
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_LOST;
|
mask |= BT_EVT_MASK_LE_PER_ADV_SYNC_LOST;
|
||||||
|
mask |= BT_EVT_MASK_LE_PAST_RECEIVED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7310,28 +7388,6 @@ uint8_t bt_le_per_adv_sync_get_index(struct bt_le_per_adv_sync *per_adv_sync)
|
||||||
return index;
|
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,
|
int bt_le_per_adv_sync_create(const struct bt_le_per_adv_sync_param *param,
|
||||||
struct bt_le_per_adv_sync **out_sync)
|
struct bt_le_per_adv_sync **out_sync)
|
||||||
{
|
{
|
||||||
|
@ -7574,6 +7630,174 @@ int bt_le_per_adv_sync_recv_disable(struct bt_le_per_adv_sync *per_adv_sync)
|
||||||
{
|
{
|
||||||
return bt_le_set_per_adv_recv_enable(per_adv_sync, false);
|
return bt_le_set_per_adv_recv_enable(per_adv_sync, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bt_le_per_adv_sync_transfer(const struct bt_le_per_adv_sync *per_adv_sync,
|
||||||
|
const struct bt_conn *conn,
|
||||||
|
uint16_t service_data)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_le_per_adv_sync_transfer *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
if (!BT_FEAT_LE_PAST_SEND(bt_dev.le.features)) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER,
|
||||||
|
sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
(void)memset(cp, 0, sizeof(*cp));
|
||||||
|
|
||||||
|
cp->conn_handle = sys_cpu_to_le16(conn->handle);
|
||||||
|
cp->sync_handle = sys_cpu_to_le16(per_adv_sync->handle);
|
||||||
|
cp->service_data = sys_cpu_to_le16(service_data);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_SYNC_TRANSFER, buf,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_le_per_adv_set_info_transfer(const struct bt_le_ext_adv *adv,
|
||||||
|
const struct bt_conn *conn,
|
||||||
|
uint16_t service_data)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_le_per_adv_set_info_transfer *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
if (!BT_FEAT_LE_PAST_SEND(bt_dev.le.features)) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_PER_ADV_SET_INFO_TRANSFER,
|
||||||
|
sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
(void)memset(cp, 0, sizeof(*cp));
|
||||||
|
|
||||||
|
cp->conn_handle = sys_cpu_to_le16(conn->handle);
|
||||||
|
cp->adv_handle = adv->handle;
|
||||||
|
cp->service_data = sys_cpu_to_le16(service_data);
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PER_ADV_SET_INFO_TRANSFER, buf,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool valid_past_param(
|
||||||
|
const struct bt_le_per_adv_sync_transfer_param *param)
|
||||||
|
{
|
||||||
|
if (param->skip > 0x01f3 ||
|
||||||
|
param->timeout < 0x000A ||
|
||||||
|
param->timeout > 0x4000) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int past_param_set(const struct bt_conn *conn, uint8_t mode,
|
||||||
|
uint16_t skip, uint16_t timeout, uint8_t cte_type)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_le_past_param *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_PAST_PARAM, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
(void)memset(cp, 0, sizeof(*cp));
|
||||||
|
|
||||||
|
cp->conn_handle = sys_cpu_to_le16(conn->handle);
|
||||||
|
cp->mode = mode;
|
||||||
|
cp->skip = sys_cpu_to_le16(skip);
|
||||||
|
cp->timeout = sys_cpu_to_le16(timeout);
|
||||||
|
cp->cte_type = cte_type;
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_PAST_PARAM, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int default_past_param_set(uint8_t mode, uint16_t skip, uint16_t timeout,
|
||||||
|
uint8_t cte_type)
|
||||||
|
{
|
||||||
|
struct bt_hci_cp_le_default_past_param *cp;
|
||||||
|
struct net_buf *buf;
|
||||||
|
|
||||||
|
buf = bt_hci_cmd_create(BT_HCI_OP_LE_DEFAULT_PAST_PARAM, sizeof(*cp));
|
||||||
|
if (!buf) {
|
||||||
|
return -ENOBUFS;
|
||||||
|
}
|
||||||
|
|
||||||
|
cp = net_buf_add(buf, sizeof(*cp));
|
||||||
|
(void)memset(cp, 0, sizeof(*cp));
|
||||||
|
|
||||||
|
cp->mode = mode;
|
||||||
|
cp->skip = sys_cpu_to_le16(skip);
|
||||||
|
cp->timeout = sys_cpu_to_le16(timeout);
|
||||||
|
cp->cte_type = cte_type;
|
||||||
|
|
||||||
|
return bt_hci_cmd_send_sync(BT_HCI_OP_LE_DEFAULT_PAST_PARAM, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_le_per_adv_sync_transfer_subscribe(
|
||||||
|
const struct bt_conn *conn,
|
||||||
|
const struct bt_le_per_adv_sync_transfer_param *param)
|
||||||
|
{
|
||||||
|
uint8_t cte_type = 0;
|
||||||
|
|
||||||
|
if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid_past_param(param)) {
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOA) {
|
||||||
|
cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_1US) {
|
||||||
|
cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_1US;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_2US) {
|
||||||
|
cte_type |= BT_HCI_LE_PAST_CTE_TYPE_NO_AOD_2US;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (param->options & BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_ONLY_CTE) {
|
||||||
|
cte_type |= BT_HCI_LE_PAST_CTE_TYPE_ONLY_CTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn) {
|
||||||
|
return past_param_set(conn, BT_HCI_LE_PAST_MODE_SYNC,
|
||||||
|
param->skip, param->timeout, cte_type);
|
||||||
|
} else {
|
||||||
|
return default_past_param_set(BT_HCI_LE_PAST_MODE_SYNC,
|
||||||
|
param->skip, param->timeout,
|
||||||
|
cte_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bt_le_per_adv_sync_transfer_unsubscribe(const struct bt_conn *conn)
|
||||||
|
{
|
||||||
|
if (!BT_FEAT_LE_PAST_RECV(bt_dev.le.features)) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn) {
|
||||||
|
return past_param_set(conn, BT_HCI_LE_PAST_MODE_NO_SYNC, 0,
|
||||||
|
0x0a, 0);
|
||||||
|
} else {
|
||||||
|
return default_past_param_set(BT_HCI_LE_PAST_MODE_NO_SYNC, 0,
|
||||||
|
0x0a, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
||||||
|
|
||||||
static bool valid_adv_ext_param(const struct bt_le_adv_param *param)
|
static bool valid_adv_ext_param(const struct bt_le_adv_param *param)
|
||||||
|
|
|
@ -434,12 +434,30 @@ static void per_adv_sync_sync_cb(struct bt_le_per_adv_sync *sync,
|
||||||
struct bt_le_per_adv_sync_synced_info *info)
|
struct bt_le_per_adv_sync_synced_info *info)
|
||||||
{
|
{
|
||||||
char le_addr[BT_ADDR_LE_STR_LEN];
|
char le_addr[BT_ADDR_LE_STR_LEN];
|
||||||
|
char past_peer[BT_ADDR_LE_STR_LEN];
|
||||||
|
|
||||||
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
|
bt_addr_le_to_str(info->addr, le_addr, sizeof(le_addr));
|
||||||
|
|
||||||
|
if (info->conn) {
|
||||||
|
conn_addr_str(info->conn, past_peer, sizeof(past_peer));
|
||||||
|
} else {
|
||||||
|
memset(past_peer, 0, sizeof(past_peer));
|
||||||
|
}
|
||||||
|
|
||||||
shell_print(ctx_shell, "PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
|
shell_print(ctx_shell, "PER_ADV_SYNC[%u]: [DEVICE]: %s synced, "
|
||||||
"Interval 0x%04x (%u ms), PHY %s",
|
"Interval 0x%04x (%u ms), PHY %s, SD 0x%04X, PAST peer %s",
|
||||||
bt_le_per_adv_sync_get_index(sync), le_addr,
|
bt_le_per_adv_sync_get_index(sync), le_addr,
|
||||||
info->interval, info->interval * 5 / 4, phy2str(info->phy));
|
info->interval, info->interval * 5 / 4, phy2str(info->phy),
|
||||||
|
info->service_data, past_peer);
|
||||||
|
|
||||||
|
if (info->conn) { /* if from PAST */
|
||||||
|
for (int i = 0; i < ARRAY_SIZE(per_adv_syncs); i++) {
|
||||||
|
if (!per_adv_syncs[i]) {
|
||||||
|
per_adv_syncs[i] = sync;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void per_adv_sync_terminated_cb(
|
static void per_adv_sync_terminated_cb(
|
||||||
|
@ -1564,6 +1582,107 @@ static int cmd_per_adv_sync_delete(const struct shell *shell, size_t argc,
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int cmd_past_subscribe(const struct shell *shell, size_t argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
struct bt_le_per_adv_sync_transfer_param param;
|
||||||
|
int err;
|
||||||
|
int i = 0;
|
||||||
|
bool global = true;
|
||||||
|
|
||||||
|
if (i == ARRAY_SIZE(per_adv_syncs)) {
|
||||||
|
shell_error(shell, "Cannot create more per adv syncs");
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Default values */
|
||||||
|
param.options = 0;
|
||||||
|
param.timeout = 1000; /* 10 seconds */
|
||||||
|
param.skip = 10;
|
||||||
|
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (!strcmp(argv[i], "aoa")) {
|
||||||
|
param.options |=
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOA;
|
||||||
|
} else if (!strcmp(argv[i], "aod_1us")) {
|
||||||
|
param.options |=
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_1US;
|
||||||
|
} else if (!strcmp(argv[i], "aod_2us")) {
|
||||||
|
param.options |=
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_NO_AOD_2US;
|
||||||
|
} else if (!strcmp(argv[i], "only_cte")) {
|
||||||
|
param.options |=
|
||||||
|
BT_LE_PER_ADV_SYNC_TRANSFER_OPT_SYNC_ONLY_CTE;
|
||||||
|
} else if (!strcmp(argv[i], "timeout")) {
|
||||||
|
if (++i == argc) {
|
||||||
|
shell_help(shell);
|
||||||
|
return SHELL_CMD_HELP_PRINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
param.timeout = strtoul(argv[i], NULL, 16);
|
||||||
|
} else if (!strcmp(argv[i], "skip")) {
|
||||||
|
if (++i == argc) {
|
||||||
|
shell_help(shell);
|
||||||
|
return SHELL_CMD_HELP_PRINTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
param.skip = strtoul(argv[i], NULL, 16);
|
||||||
|
} else if (!strcmp(argv[i], "conn")) {
|
||||||
|
if (!default_conn) {
|
||||||
|
shell_print(shell, "Not connected");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
global = false;
|
||||||
|
} else {
|
||||||
|
shell_help(shell);
|
||||||
|
return SHELL_CMD_HELP_PRINTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bt_le_per_adv_sync_cb_register(&per_adv_sync_cb);
|
||||||
|
|
||||||
|
err = bt_le_per_adv_sync_transfer_subscribe(
|
||||||
|
global ? NULL : default_conn, ¶m);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
shell_error(shell, "PAST subscribe failed (%d)", err);
|
||||||
|
} else {
|
||||||
|
shell_print(shell, "Subscribed to PAST");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_past_unsubscribe(const struct shell *shell, size_t argc,
|
||||||
|
char *argv[])
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (argc > 1) {
|
||||||
|
if (!strcmp(argv[1], "conn")) {
|
||||||
|
if (default_conn) {
|
||||||
|
err =
|
||||||
|
bt_le_per_adv_sync_transfer_unsubscribe(
|
||||||
|
default_conn);
|
||||||
|
} else {
|
||||||
|
shell_print(shell, "Not connected");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
shell_help(shell);
|
||||||
|
return SHELL_CMD_HELP_PRINTED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = bt_le_per_adv_sync_transfer_unsubscribe(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
shell_error(shell, "PAST unsubscribe failed (%d)", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
#endif /* CONFIG_BT_PER_ADV_SYNC */
|
#endif /* CONFIG_BT_PER_ADV_SYNC */
|
||||||
|
|
||||||
#if defined(CONFIG_BT_CONN)
|
#if defined(CONFIG_BT_CONN)
|
||||||
|
@ -2817,6 +2936,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds,
|
||||||
cmd_per_adv_sync_create, 4, 6),
|
cmd_per_adv_sync_create, 4, 6),
|
||||||
SHELL_CMD_ARG(per-adv-sync-delete, NULL, "[<index>]",
|
SHELL_CMD_ARG(per-adv-sync-delete, NULL, "[<index>]",
|
||||||
cmd_per_adv_sync_delete, 1, 1),
|
cmd_per_adv_sync_delete, 1, 1),
|
||||||
|
SHELL_CMD_ARG(past-subscribe, NULL, "[conn] [skip <count>] "
|
||||||
|
"[timeout <ms>] [aoa] [aod_1us] [aod_2us] [cte_only]",
|
||||||
|
cmd_past_subscribe, 1, 7),
|
||||||
|
SHELL_CMD_ARG(past-unsubscribe, NULL, "[conn]",
|
||||||
|
cmd_past_unsubscribe, 1, 1),
|
||||||
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
|
||||||
#if defined(CONFIG_BT_CONN)
|
#if defined(CONFIG_BT_CONN)
|
||||||
#if defined(CONFIG_BT_CENTRAL)
|
#if defined(CONFIG_BT_CENTRAL)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue