zephyr/subsys/bluetooth/host/adv.c

1651 lines
38 KiB
C

/*
* Copyright (c) 2017-2021 Nordic Semiconductor ASA
* Copyright (c) 2015-2016 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <sys/types.h>
#include <sys/byteorder.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/buf.h>
#include "hci_core.h"
#include "conn_internal.h"
#include "id.h"
#include "scan.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_DEBUG_HCI_CORE)
#define LOG_MODULE_NAME bt_adv
#include "common/log.h"
#if defined(CONFIG_BT_EXT_ADV)
static struct bt_le_ext_adv adv_pool[CONFIG_BT_EXT_ADV_MAX_ADV_SET];
#endif /* defined(CONFIG_BT_EXT_ADV) */
#if defined(CONFIG_BT_EXT_ADV)
uint8_t bt_le_ext_adv_get_index(struct bt_le_ext_adv *adv)
{
ptrdiff_t index = adv - adv_pool;
__ASSERT(index >= 0 && index < ARRAY_SIZE(adv_pool),
"Invalid bt_adv pointer");
return (uint8_t)index;
}
static struct bt_le_ext_adv *adv_new(void)
{
struct bt_le_ext_adv *adv = NULL;
int i;
for (i = 0; i < ARRAY_SIZE(adv_pool); i++) {
if (!atomic_test_bit(adv_pool[i].flags, BT_ADV_CREATED)) {
adv = &adv_pool[i];
break;
}
}
if (!adv) {
return NULL;
}
(void)memset(adv, 0, sizeof(*adv));
atomic_set_bit(adv_pool[i].flags, BT_ADV_CREATED);
adv->handle = i;
return adv;
}
static void adv_delete(struct bt_le_ext_adv *adv)
{
atomic_clear_bit(adv->flags, BT_ADV_CREATED);
}
#if defined(CONFIG_BT_BROADCASTER)
static struct bt_le_ext_adv *bt_adv_lookup_handle(uint8_t handle)
{
if (handle < ARRAY_SIZE(adv_pool) &&
atomic_test_bit(adv_pool[handle].flags, BT_ADV_CREATED)) {
return &adv_pool[handle];
}
return NULL;
}
#endif /* CONFIG_BT_BROADCASTER */
#endif /* defined(CONFIG_BT_EXT_ADV) */
static void adv_id_check_connectable_func(struct bt_le_ext_adv *adv, void *data)
{
struct bt_adv_id_check_data *check_data = data;
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED) &&
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE) &&
check_data->id != adv->id) {
check_data->adv_enabled = true;
}
}
void bt_le_ext_adv_foreach(void (*func)(struct bt_le_ext_adv *adv, void *data),
void *data)
{
#if defined(CONFIG_BT_EXT_ADV)
for (size_t i = 0; i < ARRAY_SIZE(adv_pool); i++) {
if (atomic_test_bit(adv_pool[i].flags, BT_ADV_CREATED)) {
func(&adv_pool[i], data);
}
}
#else
func(&bt_dev.adv, data);
#endif /* defined(CONFIG_BT_EXT_ADV) */
}
static struct bt_le_ext_adv *adv_new_legacy(void)
{
#if defined(CONFIG_BT_EXT_ADV)
if (bt_dev.adv) {
return NULL;
}
bt_dev.adv = adv_new();
return bt_dev.adv;
#else
return &bt_dev.adv;
#endif
}
void bt_le_adv_delete_legacy(void)
{
#if defined(CONFIG_BT_EXT_ADV)
if (bt_dev.adv) {
atomic_clear_bit(bt_dev.adv->flags, BT_ADV_CREATED);
bt_dev.adv = NULL;
}
#endif
}
struct bt_le_ext_adv *bt_le_adv_lookup_legacy(void)
{
#if defined(CONFIG_BT_EXT_ADV)
return bt_dev.adv;
#else
return &bt_dev.adv;
#endif
}
int bt_le_adv_set_enable_legacy(struct bt_le_ext_adv *adv, bool enable)
{
struct net_buf *buf;
struct bt_hci_cmd_state_set state;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_ENABLE, 1);
if (!buf) {
return -ENOBUFS;
}
if (enable) {
net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE);
} else {
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
}
bt_hci_cmd_state_set_init(buf, &state, adv->flags, BT_ADV_ENABLED, enable);
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_ENABLE, buf, NULL);
if (err) {
return err;
}
return 0;
}
int bt_le_adv_set_enable_ext(struct bt_le_ext_adv *adv,
bool enable,
const struct bt_le_ext_adv_start_param *param)
{
struct net_buf *buf;
struct bt_hci_cmd_state_set state;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_ADV_ENABLE, 6);
if (!buf) {
return -ENOBUFS;
}
if (enable) {
net_buf_add_u8(buf, BT_HCI_LE_ADV_ENABLE);
} else {
net_buf_add_u8(buf, BT_HCI_LE_ADV_DISABLE);
}
net_buf_add_u8(buf, 1);
net_buf_add_u8(buf, adv->handle);
net_buf_add_le16(buf, param ? sys_cpu_to_le16(param->timeout) : 0);
net_buf_add_u8(buf, param ? param->num_events : 0);
bt_hci_cmd_state_set_init(buf, &state, adv->flags, BT_ADV_ENABLED, enable);
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_ADV_ENABLE, buf, NULL);
if (err) {
return err;
}
return 0;
}
int bt_le_adv_set_enable(struct bt_le_ext_adv *adv, bool enable)
{
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
return bt_le_adv_set_enable_ext(adv, enable, NULL);
}
return bt_le_adv_set_enable_legacy(adv, enable);
}
static bool valid_adv_ext_param(const struct bt_le_adv_param *param)
{
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
if (param->peer &&
!(param->options & BT_LE_ADV_OPT_EXT_ADV) &&
!(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
/* Cannot do directed non-connectable advertising
* without extended advertising.
*/
return false;
}
if (param->peer &&
(param->options & BT_LE_ADV_OPT_EXT_ADV) &&
!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) {
/* High duty cycle directed connectable advertising
* shall not be used with Extended Advertising.
*/
return false;
}
if (!(param->options & BT_LE_ADV_OPT_EXT_ADV) &&
param->options & (BT_LE_ADV_OPT_EXT_ADV |
BT_LE_ADV_OPT_NO_2M |
BT_LE_ADV_OPT_CODED |
BT_LE_ADV_OPT_ANONYMOUS |
BT_LE_ADV_OPT_USE_TX_POWER)) {
/* Extended options require extended advertising. */
return false;
}
}
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
param->peer &&
(param->options & BT_LE_ADV_OPT_USE_IDENTITY) &&
(param->options & BT_LE_ADV_OPT_DIR_ADDR_RPA)) {
/* own addr type used for both RPAs in directed advertising. */
return false;
}
if (param->id >= bt_dev.id_count ||
!bt_addr_le_cmp(&bt_dev.id_addr[param->id], BT_ADDR_LE_ANY)) {
return false;
}
if (!(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
/*
* BT Core 4.2 [Vol 2, Part E, 7.8.5]
* The Advertising_Interval_Min and Advertising_Interval_Max
* shall not be set to less than 0x00A0 (100 ms) if the
* Advertising_Type is set to ADV_SCAN_IND or ADV_NONCONN_IND.
*/
if (bt_dev.hci_version < BT_HCI_VERSION_5_0 &&
param->interval_min < 0x00a0) {
return false;
}
}
if ((param->options & (BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY |
BT_LE_ADV_OPT_DIR_ADDR_RPA)) &&
!param->peer) {
return false;
}
if ((param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) ||
!param->peer) {
if (param->interval_min > param->interval_max ||
param->interval_min < 0x0020 ||
param->interval_max > 0x4000) {
return false;
}
}
if ((param->options & BT_LE_ADV_OPT_DISABLE_CHAN_37) &&
(param->options & BT_LE_ADV_OPT_DISABLE_CHAN_38) &&
(param->options & BT_LE_ADV_OPT_DISABLE_CHAN_39)) {
return false;
}
return true;
}
static bool valid_adv_param(const struct bt_le_adv_param *param)
{
if (param->options & BT_LE_ADV_OPT_EXT_ADV) {
return false;
}
if (param->peer && !(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
return false;
}
return valid_adv_ext_param(param);
}
struct bt_ad {
const struct bt_data *data;
size_t len;
};
static int set_data_add(uint8_t *set_data, uint8_t set_data_len_max,
const struct bt_ad *ad, size_t ad_len, uint8_t *data_len)
{
uint8_t set_data_len = 0;
for (size_t i = 0; i < ad_len; i++) {
const struct bt_data *data = ad[i].data;
for (size_t j = 0; j < ad[i].len; j++) {
size_t len = data[j].data_len;
uint8_t type = data[j].type;
/* Check if ad fit in the remaining buffer */
if ((set_data_len + len + 2) > set_data_len_max) {
ssize_t shortened_len = set_data_len_max -
(set_data_len + 2);
if (!(type == BT_DATA_NAME_COMPLETE &&
shortened_len > 0)) {
BT_ERR("Too big advertising data");
return -EINVAL;
}
type = BT_DATA_NAME_SHORTENED;
len = shortened_len;
}
set_data[set_data_len++] = len + 1;
set_data[set_data_len++] = type;
memcpy(&set_data[set_data_len], data[j].data, len);
set_data_len += len;
}
}
*data_len = set_data_len;
return 0;
}
static int hci_set_ad(uint16_t hci_op, const struct bt_ad *ad, size_t ad_len)
{
struct bt_hci_cp_le_set_adv_data *set_data;
struct net_buf *buf;
int err;
buf = bt_hci_cmd_create(hci_op, sizeof(*set_data));
if (!buf) {
return -ENOBUFS;
}
set_data = net_buf_add(buf, sizeof(*set_data));
(void)memset(set_data, 0, sizeof(*set_data));
err = set_data_add(set_data->data, BT_GAP_ADV_MAX_ADV_DATA_LEN,
ad, ad_len, &set_data->len);
if (err) {
net_buf_unref(buf);
return err;
}
return bt_hci_cmd_send_sync(hci_op, buf, NULL);
}
/* Set legacy data using Extended Advertising HCI commands */
static int hci_set_ad_ext(struct bt_le_ext_adv *adv, uint16_t hci_op,
const struct bt_ad *ad, size_t ad_len)
{
struct bt_hci_cp_le_set_ext_adv_data *set_data;
struct net_buf *buf;
int err;
buf = bt_hci_cmd_create(hci_op, sizeof(*set_data));
if (!buf) {
return -ENOBUFS;
}
set_data = net_buf_add(buf, sizeof(*set_data));
(void)memset(set_data, 0, sizeof(*set_data));
err = set_data_add(set_data->data, BT_HCI_LE_EXT_ADV_FRAG_MAX_LEN,
ad, ad_len, &set_data->len);
if (err) {
net_buf_unref(buf);
return err;
}
set_data->handle = adv->handle;
set_data->op = BT_HCI_LE_EXT_ADV_OP_COMPLETE_DATA;
set_data->frag_pref = BT_HCI_LE_EXT_ADV_FRAG_DISABLED;
return bt_hci_cmd_send_sync(hci_op, buf, NULL);
}
static int set_ad(struct bt_le_ext_adv *adv, const struct bt_ad *ad,
size_t ad_len)
{
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
return hci_set_ad_ext(adv, BT_HCI_OP_LE_SET_EXT_ADV_DATA,
ad, ad_len);
}
return hci_set_ad(BT_HCI_OP_LE_SET_ADV_DATA, ad, ad_len);
}
static int set_sd(struct bt_le_ext_adv *adv, const struct bt_ad *sd,
size_t sd_len)
{
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
return hci_set_ad_ext(adv, BT_HCI_OP_LE_SET_EXT_SCAN_RSP_DATA,
sd, sd_len);
}
return hci_set_ad(BT_HCI_OP_LE_SET_SCAN_RSP_DATA, sd, sd_len);
}
static inline bool ad_has_name(const struct bt_data *ad, size_t ad_len)
{
size_t i;
for (i = 0; i < ad_len; i++) {
if (ad[i].type == BT_DATA_NAME_COMPLETE ||
ad[i].type == BT_DATA_NAME_SHORTENED) {
return true;
}
}
return false;
}
static int le_adv_update(struct bt_le_ext_adv *adv,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len,
bool ext_adv, bool scannable, bool use_name)
{
struct bt_ad d[2] = {};
struct bt_data data;
size_t d_len;
int err;
if (use_name) {
const char *name = bt_get_name();
if ((ad && ad_has_name(ad, ad_len)) ||
(sd && ad_has_name(sd, sd_len))) {
/* Cannot use name if name is already set */
return -EINVAL;
}
data = (struct bt_data)BT_DATA(
BT_DATA_NAME_COMPLETE,
name, strlen(name));
}
if (!(ext_adv && scannable)) {
d_len = 1;
d[0].data = ad;
d[0].len = ad_len;
if (use_name && !scannable) {
d[1].data = &data;
d[1].len = 1;
d_len = 2;
}
err = set_ad(adv, d, d_len);
if (err) {
return err;
}
}
if (scannable) {
d_len = 1;
d[0].data = sd;
d[0].len = sd_len;
if (use_name) {
d[1].data = &data;
d[1].len = 1;
d_len = 2;
}
err = set_sd(adv, d, d_len);
if (err) {
return err;
}
}
atomic_set_bit(adv->flags, BT_ADV_DATA_SET);
return 0;
}
int bt_le_adv_update_data(const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
struct bt_le_ext_adv *adv = bt_le_adv_lookup_legacy();
bool scannable, use_name;
if (!adv) {
return -EINVAL;
}
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
return -EAGAIN;
}
scannable = atomic_test_bit(adv->flags, BT_ADV_SCANNABLE);
use_name = atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME);
return le_adv_update(adv, ad, ad_len, sd, sd_len, false, scannable,
use_name);
}
static uint8_t get_filter_policy(uint32_t options)
{
if (!IS_ENABLED(CONFIG_BT_WHITELIST)) {
return BT_LE_ADV_FP_NO_WHITELIST;
} else if ((options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) &&
(options & BT_LE_ADV_OPT_FILTER_CONN)) {
return BT_LE_ADV_FP_WHITELIST_BOTH;
} else if (options & BT_LE_ADV_OPT_FILTER_SCAN_REQ) {
return BT_LE_ADV_FP_WHITELIST_SCAN_REQ;
} else if (options & BT_LE_ADV_OPT_FILTER_CONN) {
return BT_LE_ADV_FP_WHITELIST_CONN_IND;
} else {
return BT_LE_ADV_FP_NO_WHITELIST;
}
}
static uint8_t get_adv_channel_map(uint32_t options)
{
uint8_t channel_map = 0x07;
if (options & BT_LE_ADV_OPT_DISABLE_CHAN_37) {
channel_map &= ~0x01;
}
if (options & BT_LE_ADV_OPT_DISABLE_CHAN_38) {
channel_map &= ~0x02;
}
if (options & BT_LE_ADV_OPT_DISABLE_CHAN_39) {
channel_map &= ~0x04;
}
return channel_map;
}
static int le_adv_start_add_conn(const struct bt_le_ext_adv *adv,
struct bt_conn **out_conn)
{
struct bt_adv_id_check_data check_data = {
.id = adv->id,
.adv_enabled = false
};
struct bt_conn *conn;
bt_le_ext_adv_foreach(adv_id_check_connectable_func, &check_data);
if (check_data.adv_enabled) {
return -ENOTSUP;
}
bt_dev.adv_conn_id = adv->id;
if (!bt_addr_le_cmp(&adv->target_addr, BT_ADDR_LE_ANY)) {
/* Undirected advertising */
conn = bt_conn_add_le(adv->id, BT_ADDR_LE_NONE);
if (!conn) {
return -ENOMEM;
}
bt_conn_set_state(conn, BT_CONN_CONNECT_ADV);
*out_conn = conn;
return 0;
}
if (bt_conn_exists_le(adv->id, &adv->target_addr)) {
return -EINVAL;
}
conn = bt_conn_add_le(adv->id, &adv->target_addr);
if (!conn) {
return -ENOMEM;
}
bt_conn_set_state(conn, BT_CONN_CONNECT_DIR_ADV);
*out_conn = conn;
return 0;
}
static void le_adv_stop_free_conn(const struct bt_le_ext_adv *adv, uint8_t status)
{
struct bt_conn *conn;
if (!bt_addr_le_cmp(&adv->target_addr, BT_ADDR_LE_ANY)) {
conn = bt_conn_lookup_state_le(adv->id, BT_ADDR_LE_NONE,
BT_CONN_CONNECT_ADV);
} else {
conn = bt_conn_lookup_state_le(adv->id, &adv->target_addr,
BT_CONN_CONNECT_DIR_ADV);
}
if (conn) {
conn->err = status;
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
bt_conn_unref(conn);
}
}
int bt_le_adv_start_legacy(struct bt_le_ext_adv *adv,
const struct bt_le_adv_param *param,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
struct bt_hci_cp_le_set_adv_param set_param;
struct bt_conn *conn = NULL;
struct net_buf *buf;
bool dir_adv = (param->peer != NULL), scannable;
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
return -EAGAIN;
}
if (!valid_adv_param(param)) {
return -EINVAL;
}
if (!bt_id_adv_random_addr_check(param)) {
return -EINVAL;
}
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
return -EALREADY;
}
(void)memset(&set_param, 0, sizeof(set_param));
set_param.min_interval = sys_cpu_to_le16(param->interval_min);
set_param.max_interval = sys_cpu_to_le16(param->interval_max);
set_param.channel_map = get_adv_channel_map(param->options);
set_param.filter_policy = get_filter_policy(param->options);
if (adv->id != param->id) {
atomic_clear_bit(bt_dev.flags, BT_DEV_RPA_VALID);
}
adv->id = param->id;
bt_dev.adv_conn_id = adv->id;
err = bt_id_set_adv_own_addr(adv, param->options, dir_adv,
&set_param.own_addr_type);
if (err) {
return err;
}
if (dir_adv) {
bt_addr_le_copy(&adv->target_addr, param->peer);
} else {
bt_addr_le_copy(&adv->target_addr, BT_ADDR_LE_ANY);
}
if (param->options & BT_LE_ADV_OPT_CONNECTABLE) {
scannable = true;
if (dir_adv) {
if (param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY) {
set_param.type = BT_HCI_ADV_DIRECT_IND_LOW_DUTY;
} else {
set_param.type = BT_HCI_ADV_DIRECT_IND;
}
bt_addr_le_copy(&set_param.direct_addr, param->peer);
} else {
set_param.type = BT_HCI_ADV_IND;
}
} else {
scannable = sd || (param->options & BT_LE_ADV_OPT_USE_NAME);
set_param.type = scannable ? BT_HCI_ADV_SCAN_IND :
BT_HCI_ADV_NONCONN_IND;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_ADV_PARAM, sizeof(set_param));
if (!buf) {
return -ENOBUFS;
}
net_buf_add_mem(buf, &set_param, sizeof(set_param));
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_ADV_PARAM, buf, NULL);
if (err) {
return err;
}
if (!dir_adv) {
err = le_adv_update(adv, ad, ad_len, sd, sd_len, false,
scannable,
param->options & BT_LE_ADV_OPT_USE_NAME);
if (err) {
return err;
}
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
err = le_adv_start_add_conn(adv, &conn);
if (err) {
if (err == -ENOMEM && !dir_adv &&
!(param->options & BT_LE_ADV_OPT_ONE_TIME)) {
goto set_adv_state;
}
return err;
}
}
err = bt_le_adv_set_enable(adv, true);
if (err) {
BT_ERR("Failed to start advertiser");
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
bt_conn_unref(conn);
}
return err;
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
/* If undirected connectable advertiser we have created a
* connection object that we don't yet give to the application.
* Since we don't give the application a reference to manage in
* this case, we need to release this reference here
*/
bt_conn_unref(conn);
}
set_adv_state:
atomic_set_bit_to(adv->flags, BT_ADV_PERSIST, !dir_adv &&
!(param->options & BT_LE_ADV_OPT_ONE_TIME));
atomic_set_bit_to(adv->flags, BT_ADV_INCLUDE_NAME,
param->options & BT_LE_ADV_OPT_USE_NAME);
atomic_set_bit_to(adv->flags, BT_ADV_CONNECTABLE,
param->options & BT_LE_ADV_OPT_CONNECTABLE);
atomic_set_bit_to(adv->flags, BT_ADV_SCANNABLE, scannable);
atomic_set_bit_to(adv->flags, BT_ADV_USE_IDENTITY,
param->options & BT_LE_ADV_OPT_USE_IDENTITY);
return 0;
}
static int le_ext_adv_param_set(struct bt_le_ext_adv *adv,
const struct bt_le_adv_param *param,
bool has_scan_data)
{
struct bt_hci_cp_le_set_ext_adv_param *cp;
bool dir_adv = param->peer != NULL, scannable;
struct net_buf *buf, *rsp;
int err;
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_EXT_ADV_PARAM, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
(void)memset(cp, 0, sizeof(*cp));
err = bt_id_set_adv_own_addr(adv, param->options, dir_adv,
&cp->own_addr_type);
if (err) {
return err;
}
if (dir_adv) {
bt_addr_le_copy(&adv->target_addr, param->peer);
} else {
bt_addr_le_copy(&adv->target_addr, BT_ADDR_LE_ANY);
}
cp->handle = adv->handle;
sys_put_le24(param->interval_min, cp->prim_min_interval);
sys_put_le24(param->interval_max, cp->prim_max_interval);
cp->prim_channel_map = get_adv_channel_map(param->options);
cp->filter_policy = get_filter_policy(param->options);
cp->tx_power = BT_HCI_LE_ADV_TX_POWER_NO_PREF;
cp->prim_adv_phy = BT_HCI_LE_PHY_1M;
if (param->options & BT_LE_ADV_OPT_EXT_ADV) {
if (param->options & BT_LE_ADV_OPT_NO_2M) {
cp->sec_adv_phy = BT_HCI_LE_PHY_1M;
} else {
cp->sec_adv_phy = BT_HCI_LE_PHY_2M;
}
}
if (param->options & BT_LE_ADV_OPT_CODED) {
cp->prim_adv_phy = BT_HCI_LE_PHY_CODED;
cp->sec_adv_phy = BT_HCI_LE_PHY_CODED;
}
if (!(param->options & BT_LE_ADV_OPT_EXT_ADV)) {
cp->props |= BT_HCI_LE_ADV_PROP_LEGACY;
}
if (param->options & BT_LE_ADV_OPT_USE_TX_POWER) {
cp->props |= BT_HCI_LE_ADV_PROP_TX_POWER;
}
if (param->options & BT_LE_ADV_OPT_ANONYMOUS) {
cp->props |= BT_HCI_LE_ADV_PROP_ANON;
}
if (param->options & BT_LE_ADV_OPT_NOTIFY_SCAN_REQ) {
cp->scan_req_notify_enable = BT_HCI_LE_ADV_SCAN_REQ_ENABLE;
}
if (param->options & BT_LE_ADV_OPT_CONNECTABLE) {
cp->props |= BT_HCI_LE_ADV_PROP_CONN;
if (!dir_adv && !(param->options & BT_LE_ADV_OPT_EXT_ADV)) {
/* When using non-extended adv packets then undirected
* advertising has to be scannable as well.
* We didn't require this option to be set before, so
* it is implicitly set instead in this case.
*/
cp->props |= BT_HCI_LE_ADV_PROP_SCAN;
}
}
if ((param->options & BT_LE_ADV_OPT_SCANNABLE) || has_scan_data) {
cp->props |= BT_HCI_LE_ADV_PROP_SCAN;
}
scannable = !!(cp->props & BT_HCI_LE_ADV_PROP_SCAN);
if (dir_adv) {
cp->props |= BT_HCI_LE_ADV_PROP_DIRECT;
if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) {
cp->props |= BT_HCI_LE_ADV_PROP_HI_DC_CONN;
}
bt_addr_le_copy(&cp->peer_addr, param->peer);
}
cp->sid = param->sid;
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_EXT_ADV_PARAM, buf, &rsp);
if (err) {
return err;
}
#if defined(CONFIG_BT_EXT_ADV)
struct bt_hci_rp_le_set_ext_adv_param *rp = (void *)rsp->data;
adv->tx_power = rp->tx_power;
#endif /* defined(CONFIG_BT_EXT_ADV) */
net_buf_unref(rsp);
atomic_set_bit(adv->flags, BT_ADV_PARAMS_SET);
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_RANDOM_ADDR_PENDING)) {
err = bt_id_set_adv_random_addr(adv, &adv->random_addr.a);
if (err) {
return err;
}
}
/* Flag only used by bt_le_adv_start API. */
atomic_set_bit_to(adv->flags, BT_ADV_PERSIST, false);
atomic_set_bit_to(adv->flags, BT_ADV_INCLUDE_NAME,
param->options & BT_LE_ADV_OPT_USE_NAME);
atomic_set_bit_to(adv->flags, BT_ADV_CONNECTABLE,
param->options & BT_LE_ADV_OPT_CONNECTABLE);
atomic_set_bit_to(adv->flags, BT_ADV_SCANNABLE, scannable);
atomic_set_bit_to(adv->flags, BT_ADV_USE_IDENTITY,
param->options & BT_LE_ADV_OPT_USE_IDENTITY);
atomic_set_bit_to(adv->flags, BT_ADV_EXT_ADV,
param->options & BT_LE_ADV_OPT_EXT_ADV);
return 0;
}
int bt_le_adv_start_ext(struct bt_le_ext_adv *adv,
const struct bt_le_adv_param *param,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
struct bt_le_ext_adv_start_param start_param = {
.timeout = 0,
.num_events = 0,
};
bool dir_adv = (param->peer != NULL);
struct bt_conn *conn = NULL;
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
return -EAGAIN;
}
if (!valid_adv_param(param)) {
return -EINVAL;
}
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
return -EALREADY;
}
adv->id = param->id;
err = le_ext_adv_param_set(adv, param, sd ||
(param->options & BT_LE_ADV_OPT_USE_NAME));
if (err) {
return err;
}
if (!dir_adv) {
err = bt_le_ext_adv_set_data(adv, ad, ad_len, sd, sd_len);
if (err) {
return err;
}
} else {
if (!(param->options & BT_LE_ADV_OPT_DIR_MODE_LOW_DUTY)) {
start_param.timeout =
BT_GAP_ADV_HIGH_DUTY_CYCLE_MAX_TIMEOUT;
atomic_set_bit(adv->flags, BT_ADV_LIMITED);
}
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
(param->options & BT_LE_ADV_OPT_CONNECTABLE)) {
err = le_adv_start_add_conn(adv, &conn);
if (err) {
if (err == -ENOMEM && !dir_adv &&
!(param->options & BT_LE_ADV_OPT_ONE_TIME)) {
goto set_adv_state;
}
return err;
}
}
err = bt_le_adv_set_enable_ext(adv, true, &start_param);
if (err) {
BT_ERR("Failed to start advertiser");
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
bt_conn_unref(conn);
}
return err;
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
/* If undirected connectable advertiser we have created a
* connection object that we don't yet give to the application.
* Since we don't give the application a reference to manage in
* this case, we need to release this reference here
*/
bt_conn_unref(conn);
}
set_adv_state:
/* Flag always set to false by le_ext_adv_param_set */
atomic_set_bit_to(adv->flags, BT_ADV_PERSIST, !dir_adv &&
!(param->options & BT_LE_ADV_OPT_ONE_TIME));
return 0;
}
int bt_le_adv_start(const struct bt_le_adv_param *param,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
struct bt_le_ext_adv *adv = adv_new_legacy();
int err;
if (!adv) {
return -ENOMEM;
}
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
err = bt_le_adv_start_ext(adv, param, ad, ad_len, sd, sd_len);
} else {
err = bt_le_adv_start_legacy(adv, param, ad, ad_len, sd, sd_len);
}
if (err) {
bt_le_adv_delete_legacy();
}
return err;
}
int bt_le_adv_stop(void)
{
struct bt_le_ext_adv *adv = bt_le_adv_lookup_legacy();
int err;
if (!adv) {
BT_ERR("No valid legacy adv");
return 0;
}
/* Make sure advertising is not re-enabled later even if it's not
* currently enabled (i.e. BT_DEV_ADVERTISING is not set).
*/
atomic_clear_bit(adv->flags, BT_ADV_PERSIST);
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
/* Legacy advertiser exists, but is not currently advertising.
* This happens when keep advertising behavior is active but
* no conn object is available to do connectable advertising.
*/
bt_le_adv_delete_legacy();
return 0;
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
le_adv_stop_free_conn(adv, 0);
}
if (IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
err = bt_le_adv_set_enable_ext(adv, false, NULL);
if (err) {
return err;
}
} else {
err = bt_le_adv_set_enable_legacy(adv, false);
if (err) {
return err;
}
}
bt_le_adv_delete_legacy();
#if defined(CONFIG_BT_OBSERVER)
if (!(IS_ENABLED(CONFIG_BT_EXT_ADV) &&
BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) &&
!IS_ENABLED(CONFIG_BT_PRIVACY) &&
!IS_ENABLED(CONFIG_BT_SCAN_WITH_IDENTITY)) {
/* If scan is ongoing set back NRPA */
if (atomic_test_bit(bt_dev.flags, BT_DEV_SCANNING)) {
bt_le_scan_set_enable(BT_HCI_LE_SCAN_DISABLE);
bt_id_set_private_addr(BT_ID_DEFAULT);
bt_le_scan_set_enable(BT_HCI_LE_SCAN_ENABLE);
}
}
#endif /* defined(CONFIG_BT_OBSERVER) */
return 0;
}
#if defined(CONFIG_BT_PERIPHERAL)
void bt_le_adv_resume(void)
{
struct bt_le_ext_adv *adv = bt_le_adv_lookup_legacy();
struct bt_conn *conn;
bool persist_paused = false;
int err;
if (!adv) {
BT_DBG("No valid legacy adv");
return;
}
if (!(atomic_test_bit(adv->flags, BT_ADV_PERSIST) &&
!atomic_test_bit(adv->flags, BT_ADV_ENABLED))) {
return;
}
if (!atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
return;
}
err = le_adv_start_add_conn(adv, &conn);
if (err) {
BT_DBG("Host cannot resume connectable advertising (%d)", err);
return;
}
BT_DBG("Resuming connectable advertising");
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
bt_id_set_adv_private_addr(adv);
}
err = bt_le_adv_set_enable(adv, true);
if (err) {
BT_DBG("Controller cannot resume connectable advertising (%d)",
err);
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
/* Temporarily clear persist flag to avoid recursion in
* bt_conn_unref if the flag is still set.
*/
persist_paused = atomic_test_and_clear_bit(adv->flags,
BT_ADV_PERSIST);
}
/* Since we don't give the application a reference to manage in
* this case, we need to release this reference here.
*/
bt_conn_unref(conn);
if (persist_paused) {
atomic_set_bit(adv->flags, BT_ADV_PERSIST);
}
}
#endif /* defined(CONFIG_BT_PERIPHERAL) */
#if defined(CONFIG_BT_EXT_ADV)
int bt_le_ext_adv_get_info(const struct bt_le_ext_adv *adv,
struct bt_le_ext_adv_info *info)
{
info->id = adv->id;
info->tx_power = adv->tx_power;
return 0;
}
int bt_le_ext_adv_create(const struct bt_le_adv_param *param,
const struct bt_le_ext_adv_cb *cb,
struct bt_le_ext_adv **out_adv)
{
struct bt_le_ext_adv *adv;
int err;
if (!atomic_test_bit(bt_dev.flags, BT_DEV_READY)) {
return -EAGAIN;
}
if (!valid_adv_ext_param(param)) {
return -EINVAL;
}
adv = adv_new();
if (!adv) {
return -ENOMEM;
}
adv->id = param->id;
adv->cb = cb;
err = le_ext_adv_param_set(adv, param, false);
if (err) {
adv_delete(adv);
return err;
}
*out_adv = adv;
return 0;
}
int bt_le_ext_adv_update_param(struct bt_le_ext_adv *adv,
const struct bt_le_adv_param *param)
{
if (!valid_adv_ext_param(param)) {
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_PER_ADV) &&
atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
/* If params for per adv has been set, do not allow setting
* connectable, scanable or use legacy adv
*/
if (param->options & BT_LE_ADV_OPT_CONNECTABLE ||
param->options & BT_LE_ADV_OPT_SCANNABLE ||
!(param->options & BT_LE_ADV_OPT_EXT_ADV) ||
param->options & BT_LE_ADV_OPT_ANONYMOUS) {
return -EINVAL;
}
}
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
return -EINVAL;
}
if (param->id != adv->id) {
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
}
return le_ext_adv_param_set(adv, param, false);
}
int bt_le_ext_adv_start(struct bt_le_ext_adv *adv,
struct bt_le_ext_adv_start_param *param)
{
struct bt_conn *conn = NULL;
int err;
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
err = le_adv_start_add_conn(adv, &conn);
if (err) {
return err;
}
}
atomic_set_bit_to(adv->flags, BT_ADV_LIMITED, param &&
(param->timeout > 0 || param->num_events > 0));
if (atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
bt_id_set_adv_private_addr(adv);
}
} else {
if (!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
bt_id_set_adv_private_addr(adv);
}
}
if (atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME) &&
!atomic_test_bit(adv->flags, BT_ADV_DATA_SET)) {
/* Set the advertiser name */
bt_le_ext_adv_set_data(adv, NULL, 0, NULL, 0);
}
err = bt_le_adv_set_enable_ext(adv, true, param);
if (err) {
BT_ERR("Failed to start advertiser");
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
bt_conn_set_state(conn, BT_CONN_DISCONNECTED);
bt_conn_unref(conn);
}
return err;
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) && conn) {
/* If undirected connectable advertiser we have created a
* connection object that we don't yet give to the application.
* Since we don't give the application a reference to manage in
* this case, we need to release this reference here
*/
bt_conn_unref(conn);
}
return 0;
}
int bt_le_ext_adv_stop(struct bt_le_ext_adv *adv)
{
atomic_clear_bit(adv->flags, BT_ADV_PERSIST);
if (!atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
return 0;
}
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_LIMITED)) {
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
#if defined(CONFIG_BT_SMP)
bt_id_pending_keys_update();
#endif
}
if (IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
le_adv_stop_free_conn(adv, 0);
}
return bt_le_adv_set_enable_ext(adv, false, NULL);
}
int bt_le_ext_adv_set_data(struct bt_le_ext_adv *adv,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
bool ext_adv, scannable, use_name;
ext_adv = atomic_test_bit(adv->flags, BT_ADV_EXT_ADV);
scannable = atomic_test_bit(adv->flags, BT_ADV_SCANNABLE);
use_name = atomic_test_bit(adv->flags, BT_ADV_INCLUDE_NAME);
return le_adv_update(adv, ad, ad_len, sd, sd_len, ext_adv, scannable,
use_name);
}
int bt_le_ext_adv_delete(struct bt_le_ext_adv *adv)
{
struct bt_hci_cp_le_remove_adv_set *cp;
struct net_buf *buf;
int err;
if (!BT_FEAT_LE_EXT_ADV(bt_dev.le.features)) {
return -ENOTSUP;
}
/* Advertising set should be stopped first */
if (atomic_test_bit(adv->flags, BT_ADV_ENABLED)) {
return -EINVAL;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_REMOVE_ADV_SET, sizeof(*cp));
if (!buf) {
BT_WARN("No HCI buffers");
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
cp->handle = adv->handle;
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_REMOVE_ADV_SET, buf, NULL);
if (err) {
return err;
}
adv_delete(adv);
return 0;
}
#endif /* defined(CONFIG_BT_EXT_ADV) */
#if defined(CONFIG_BT_PER_ADV)
int bt_le_per_adv_set_param(struct bt_le_ext_adv *adv,
const struct bt_le_per_adv_param *param)
{
struct bt_hci_cp_le_set_per_adv_param *cp;
struct net_buf *buf;
int err;
if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) {
return -ENOTSUP;
}
if (atomic_test_bit(adv->flags, BT_ADV_SCANNABLE)) {
return -EINVAL;
} else if (atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
return -EINVAL;
} else if (!atomic_test_bit(adv->flags, BT_ADV_EXT_ADV)) {
return -EINVAL;
}
if (param->interval_min < 0x0006 ||
param->interval_max > 0xFFFF ||
param->interval_min > param->interval_max) {
return -EINVAL;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_PARAM, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
(void)memset(cp, 0, sizeof(*cp));
cp->handle = adv->handle;
cp->min_interval = sys_cpu_to_le16(param->interval_min);
cp->max_interval = sys_cpu_to_le16(param->interval_max);
if (param->options & BT_LE_PER_ADV_OPT_USE_TX_POWER) {
cp->props |= BT_HCI_LE_ADV_PROP_TX_POWER;
}
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_PARAM, buf, NULL);
if (err) {
return err;
}
atomic_set_bit(adv->flags, BT_PER_ADV_PARAMS_SET);
return 0;
}
int bt_le_per_adv_set_data(const struct bt_le_ext_adv *adv,
const struct bt_data *ad, size_t ad_len)
{
struct bt_hci_cp_le_set_per_adv_data *cp;
struct net_buf *buf;
struct bt_ad d = { .data = ad, .len = ad_len };
int err;
if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) {
return -ENOTSUP;
}
if (!atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
return -EINVAL;
}
if (!ad_len || !ad) {
return -EINVAL;
}
if (ad_len > BT_HCI_LE_PER_ADV_FRAG_MAX_LEN) {
return -EINVAL;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_DATA, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
(void)memset(cp, 0, sizeof(*cp));
cp->handle = adv->handle;
/* TODO: If data is longer than what the controller can manage,
* split the data. Read size from controller on boot.
*/
cp->op = BT_HCI_LE_PER_ADV_OP_COMPLETE_DATA;
err = set_data_add(cp->data, BT_HCI_LE_PER_ADV_FRAG_MAX_LEN, &d, 1,
&cp->len);
if (err) {
return err;
}
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_DATA, buf, NULL);
if (err) {
return err;
}
return 0;
}
static int bt_le_per_adv_enable(struct bt_le_ext_adv *adv, bool enable)
{
struct bt_hci_cp_le_set_per_adv_enable *cp;
struct net_buf *buf;
struct bt_hci_cmd_state_set state;
int err;
if (!BT_FEAT_LE_EXT_PER_ADV(bt_dev.le.features)) {
return -ENOTSUP;
}
/* TODO: We could setup some default ext adv params if not already set*/
if (!atomic_test_bit(adv->flags, BT_PER_ADV_PARAMS_SET)) {
return -EINVAL;
}
if (atomic_test_bit(adv->flags, BT_PER_ADV_ENABLED) == enable) {
return -EALREADY;
}
buf = bt_hci_cmd_create(BT_HCI_OP_LE_SET_PER_ADV_ENABLE, sizeof(*cp));
if (!buf) {
return -ENOBUFS;
}
cp = net_buf_add(buf, sizeof(*cp));
(void)memset(cp, 0, sizeof(*cp));
cp->handle = adv->handle;
cp->enable = enable ? 1 : 0;
bt_hci_cmd_state_set_init(buf, &state, adv->flags,
BT_PER_ADV_ENABLED, enable);
err = bt_hci_cmd_send_sync(BT_HCI_OP_LE_SET_PER_ADV_ENABLE, buf, NULL);
if (err) {
return err;
}
return 0;
}
int bt_le_per_adv_start(struct bt_le_ext_adv *adv)
{
return bt_le_per_adv_enable(adv, true);
}
int bt_le_per_adv_stop(struct bt_le_ext_adv *adv)
{
return bt_le_per_adv_enable(adv, false);
}
#if defined(CONFIG_BT_CONN)
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_EXT_PER_ADV(bt_dev.le.features)) {
return -ENOTSUP;
} else if (!BT_FEAT_LE_PAST_SEND(bt_dev.le.features)) {
return -ENOTSUP;
}
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);
}
#endif /* CONFIG_BT_CONN */
#endif /* CONFIG_BT_PER_ADV */
#if defined(CONFIG_BT_EXT_ADV)
#if defined(CONFIG_BT_BROADCASTER)
void bt_hci_le_adv_set_terminated(struct net_buf *buf)
{
struct bt_hci_evt_le_adv_set_terminated *evt;
struct bt_le_ext_adv *adv;
uint16_t conn_handle;
evt = (void *)buf->data;
adv = bt_adv_lookup_handle(evt->adv_handle);
conn_handle = sys_le16_to_cpu(evt->conn_handle);
BT_DBG("status 0x%02x adv_handle %u conn_handle 0x%02x num %u",
evt->status, evt->adv_handle, conn_handle,
evt->num_completed_ext_adv_evts);
if (!adv) {
BT_ERR("No valid adv");
return;
}
atomic_clear_bit(adv->flags, BT_ADV_ENABLED);
if (evt->status && IS_ENABLED(CONFIG_BT_PERIPHERAL) &&
atomic_test_bit(adv->flags, BT_ADV_CONNECTABLE)) {
/* Only set status for legacy advertising API.
* This will call connected callback for high duty cycle
* directed advertiser timeout.
*/
le_adv_stop_free_conn(adv, adv == bt_dev.adv ? evt->status : 0);
}
if (IS_ENABLED(CONFIG_BT_CONN) && !evt->status) {
struct bt_conn *conn = bt_conn_lookup_handle(conn_handle);
if (conn) {
if (IS_ENABLED(CONFIG_BT_PRIVACY) &&
!atomic_test_bit(adv->flags, BT_ADV_USE_IDENTITY)) {
/* Set Responder address unless already set */
conn->le.resp_addr.type = BT_ADDR_LE_RANDOM;
if (bt_addr_cmp(&conn->le.resp_addr.a,
BT_ADDR_ANY) == 0) {
bt_addr_copy(&conn->le.resp_addr.a,
&adv->random_addr.a);
}
} else {
bt_addr_le_copy(&conn->le.resp_addr,
&bt_dev.id_addr[conn->id]);
}
if (adv->cb && adv->cb->connected) {
struct bt_le_ext_adv_connected_info info = {
.conn = conn,
};
adv->cb->connected(adv, &info);
}
bt_conn_unref(conn);
}
}
if (atomic_test_and_clear_bit(adv->flags, BT_ADV_LIMITED)) {
atomic_clear_bit(adv->flags, BT_ADV_RPA_VALID);
#if defined(CONFIG_BT_SMP)
bt_id_pending_keys_update();
#endif
if (adv->cb && adv->cb->sent) {
struct bt_le_ext_adv_sent_info info = {
.num_sent = evt->num_completed_ext_adv_evts,
};
adv->cb->sent(adv, &info);
}
}
if (!atomic_test_bit(adv->flags, BT_ADV_PERSIST) && adv == bt_dev.adv) {
bt_le_adv_delete_legacy();
}
}
void bt_hci_le_scan_req_received(struct net_buf *buf)
{
struct bt_hci_evt_le_scan_req_received *evt;
struct bt_le_ext_adv *adv;
evt = (void *)buf->data;
adv = bt_adv_lookup_handle(evt->handle);
BT_DBG("handle %u peer %s", evt->handle, bt_addr_le_str(&evt->addr));
if (!adv) {
BT_ERR("No valid adv");
return;
}
if (adv->cb && adv->cb->scanned) {
struct bt_le_ext_adv_scanned_info info;
bt_addr_le_t id_addr;
if (evt->addr.type == BT_ADDR_LE_PUBLIC_ID ||
evt->addr.type == BT_ADDR_LE_RANDOM_ID) {
bt_addr_le_copy(&id_addr, &evt->addr);
id_addr.type -= BT_ADDR_LE_PUBLIC_ID;
} else {
bt_addr_le_copy(&id_addr,
bt_lookup_id_addr(adv->id, &evt->addr));
}
info.addr = &id_addr;
adv->cb->scanned(adv, &info);
}
}
#endif /* defined(CONFIG_BT_BROADCASTER) */
#endif /* defined(CONFIG_BT_EXT_ADV) */