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:
Emil Gydesen 2020-09-23 22:39:29 +02:00 committed by Carles Cufí
commit 1dada244c1
4 changed files with 585 additions and 26 deletions

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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, &param);
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)