Bluetooth: host: PA sync receive enable/disable

Adds support for enabling/disabling PA sync receive,
which allows applications to control when to receive data
while a sync is established.

Signed-off-by: Emil Gydesen <emil.gydesen@nordicsemi.no>
This commit is contained in:
Emil Gydesen 2020-10-12 14:13:19 +02:00 committed by Carles Cufí
parent 6c67d3cb4b
commit 44f00358a6
3 changed files with 142 additions and 0 deletions

View file

@ -977,6 +977,9 @@ struct bt_le_per_adv_sync_synced_info {
/** Advertiser PHY */
uint8_t phy;
/** True if receiving periodic advertisements, false otherwise. */
bool recv_enabled;
};
struct bt_le_per_adv_sync_term_info {
@ -1004,6 +1007,12 @@ struct bt_le_per_adv_sync_recv_info {
uint8_t cte_type;
};
struct bt_le_per_adv_sync_state_info {
/** True if receiving periodic advertisements, false otherwise. */
bool recv_enabled;
};
struct bt_le_per_adv_sync_cb {
/**
* @brief The periodic advertising has been successfully synced.
@ -1044,6 +1053,20 @@ struct bt_le_per_adv_sync_cb {
const struct bt_le_per_adv_sync_recv_info *info,
struct net_buf_simple *buf);
/**
* @brief The periodic advertising sync state has changed.
*
* This callback notifies the application about changes to the sync
* state. Initialize sync and termination is handled by their individual
* callbacks, and won't be notified here.
*
* @param sync The periodic advertising sync object.
* @param info Information about the state change.
*/
void (*state_changed)(struct bt_le_per_adv_sync *sync,
const struct bt_le_per_adv_sync_state_info *info);
sys_snode_t node;
};
@ -1170,6 +1193,28 @@ int bt_le_per_adv_sync_delete(struct bt_le_per_adv_sync *per_adv_sync);
*/
void bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb);
/**
* @brief Enables receiving periodic advertising reports for a sync.
*
* If the sync is already receiving the reports, -EALREADY is returned.
*
* @param per_adv_sync The periodic advertising sync object.
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_le_per_adv_sync_recv_enable(struct bt_le_per_adv_sync *per_adv_sync);
/**
* @brief Disables receiving periodic advertising reports for a sync.
*
* If the sync report receiving is already disabled, -EALREADY is returned.
*
* @param per_adv_sync The periodic advertising sync object.
*
* @return Zero on success or (negative) error code otherwise.
*/
int bt_le_per_adv_sync_recv_disable(struct bt_le_per_adv_sync *per_adv_sync);
enum {
/** Convenience value when no options are specified. */
BT_LE_SCAN_OPT_NONE = 0,

View file

@ -4616,6 +4616,12 @@ static void le_per_adv_report(struct net_buf *buf)
return;
}
if (atomic_test_bit(per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED)) {
BT_ERR("Received PA adv report when receive disabled");
return;
}
info.tx_power = evt->tx_power;
info.rssi = evt->rssi;
info.cte_type = evt->cte_type;
@ -4723,6 +4729,10 @@ static void le_per_adv_sync_established(struct net_buf *buf)
sync_info.addr = &pending_per_adv_sync->addr;
sync_info.sid = pending_per_adv_sync->sid;
sync_info.recv_enabled =
!atomic_test_bit(pending_per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED);
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
if (listener->synced) {
listener->synced(pending_per_adv_sync, &sync_info);
@ -7401,6 +7411,15 @@ int bt_le_per_adv_sync_create(const struct bt_le_per_adv_sync_param *param,
cp->cte_type |= BT_HCI_LE_PER_ADV_CREATE_SYNC_CTE_TYPE_ONLY_CTE;
}
if (param->options &
BT_LE_PER_ADV_SYNC_OPT_REPORTING_INITIALLY_DISABLED) {
cp->options |=
BT_HCI_LE_PER_ADV_CREATE_SYNC_FP_REPORTS_DISABLED;
atomic_set_bit(per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED);
}
cp->sid = param->sid;
cp->skip = sys_cpu_to_le16(param->skip);
cp->sync_timeout = sys_cpu_to_le16(param->timeout);
@ -7497,6 +7516,81 @@ void bt_le_per_adv_sync_cb_register(struct bt_le_per_adv_sync_cb *cb)
{
sys_slist_append(&pa_sync_cbs, &cb->node);
}
static int bt_le_set_per_adv_recv_enable(
struct bt_le_per_adv_sync *per_adv_sync, bool enable)
{
struct bt_hci_cp_le_set_per_adv_recv_enable *cp;
struct bt_le_per_adv_sync_cb *listener;
struct bt_le_per_adv_sync_state_info info;
struct net_buf *buf;
struct cmd_state_set state;
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
return -EAGAIN;
}
if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) {
return -ENOTSUP;
}
if (!atomic_test_bit(per_adv_sync->flags, BT_PER_ADV_SYNC_SYNCED)) {
return -EINVAL;
}
if ((enable && !atomic_test_bit(per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED)) ||
(!enable && atomic_test_bit(per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED))) {
return -EALREADY;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE,
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(per_adv_sync->handle);
cp->enable = enable ? 1 : 0;
cmd_state_set_init(&state, per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED,
enable);
cmd(buf)->state = &state;
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_RECV_ENABLE,
buf, NULL);
if (err) {
return err;
}
info.recv_enabled = !atomic_test_bit(per_adv_sync->flags,
BT_PER_ADV_SYNC_RECV_DISABLED);
SYS_SLIST_FOR_EACH_CONTAINER(&pa_sync_cbs, listener, node) {
if (listener->state_changed) {
listener->state_changed(per_adv_sync, &info);
}
}
return 0;
}
int bt_le_per_adv_sync_recv_enable(struct bt_le_per_adv_sync *per_adv_sync)
{
return bt_le_set_per_adv_recv_enable(per_adv_sync, true);
}
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);
}
#endif /* defined(CONFIG_BT_PER_ADV_SYNC) */
static bool valid_adv_ext_param(const struct bt_le_adv_param *param)

View file

@ -143,6 +143,9 @@ enum {
/** Periodic advertising is attempting sync sync */
BT_PER_ADV_SYNC_SYNCING,
/** Periodic advertising is attempting sync sync */
BT_PER_ADV_SYNC_RECV_DISABLED,
BT_PER_ADV_SYNC_NUM_FLAGS,
};