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. */
|
||||
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 {
|
||||
|
@ -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);
|
||||
|
||||
/** 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 {
|
||||
/** Convenience value when no options are specified. */
|
||||
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_RX_AOA 22
|
||||
#define BT_LE_FEAT_BIT_RX_CTE 23
|
||||
#define BT_LE_FEAT_BIT_PERIODIC_SYNC_XFER_SEND 24
|
||||
#define BT_LE_FEAT_BIT_PERIODIC_SYNC_XFER_RECV 25
|
||||
#define BT_LE_FEAT_BIT_PAST_SEND 24
|
||||
#define BT_LE_FEAT_BIT_PAST_RECV 25
|
||||
#define BT_LE_FEAT_BIT_SCA_UPDATE 26
|
||||
#define BT_LE_FEAT_BIT_REMOTE_PUB_KEY_VALIDATE 27
|
||||
#define BT_LE_FEAT_BIT_CIS_MASTER 28
|
||||
|
@ -185,6 +185,10 @@ struct bt_hci_cmd_hdr {
|
|||
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)
|
||||
#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, \
|
||||
BT_LE_FEAT_BIT_CIS_MASTER)
|
||||
#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;
|
||||
} __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)
|
||||
struct bt_hci_rp_le_read_buffer_size_v2 {
|
||||
uint8_t status;
|
||||
|
@ -2000,6 +2064,19 @@ struct bt_hci_evt_le_chan_sel_algo {
|
|||
uint8_t chan_sel_algo;
|
||||
} __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
|
||||
struct bt_hci_evt_le_cis_established {
|
||||
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_SCAN_REQ_RECEIVED BT_EVT_BIT(18)
|
||||
#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_REQ BT_EVT_BIT(25)
|
||||
#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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
pending_per_adv_sync->phy = evt->phy;
|
||||
|
||||
memset(&sync_info, 0, sizeof(sync_info));
|
||||
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;
|
||||
|
@ -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_EXT_ADV) */
|
||||
|
||||
|
@ -5027,6 +5102,8 @@ static const struct event_handler meta_events[] = {
|
|||
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)),
|
||||
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_EXT_ADV) */
|
||||
#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_ADVERTISING_REPORT;
|
||||
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;
|
||||
}
|
||||
|
||||
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,
|
||||
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);
|
||||
}
|
||||
|
||||
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) */
|
||||
|
||||
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)
|
||||
{
|
||||
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));
|
||||
|
||||
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, "
|
||||
"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,
|
||||
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(
|
||||
|
@ -1564,6 +1582,107 @@ static int cmd_per_adv_sync_delete(const struct shell *shell, size_t argc,
|
|||
|
||||
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 */
|
||||
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
|
@ -2817,6 +2936,11 @@ SHELL_STATIC_SUBCMD_SET_CREATE(bt_cmds,
|
|||
cmd_per_adv_sync_create, 4, 6),
|
||||
SHELL_CMD_ARG(per-adv-sync-delete, NULL, "[<index>]",
|
||||
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) */
|
||||
#if defined(CONFIG_BT_CONN)
|
||||
#if defined(CONFIG_BT_CENTRAL)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue