From 53cea4719d62139d8e6d36bfec63ae8cb76c87ce Mon Sep 17 00:00:00 2001 From: Joakim Andersson Date: Mon, 8 Mar 2021 14:30:34 +0100 Subject: [PATCH] Bluetooth: host: Refactor out advertiser roles from hci_core to adv Refactor out the advertiser roles handling from hci_core.c to its own source file in adv.c. Advertising roles consists of legacy and extended advertiser, and periodic advertiser. Signed-off-by: Joakim Andersson --- subsys/bluetooth/host/CMakeLists.txt | 4 + subsys/bluetooth/host/adv.c | 1635 ++++++++++++++++++++++++++ subsys/bluetooth/host/adv.h | 21 + subsys/bluetooth/host/conn.c | 1 + subsys/bluetooth/host/hci_core.c | 1619 +------------------------ subsys/bluetooth/host/hci_core.h | 19 +- subsys/bluetooth/host/id.c | 1 + 7 files changed, 1671 insertions(+), 1629 deletions(-) create mode 100644 subsys/bluetooth/host/adv.c create mode 100644 subsys/bluetooth/host/adv.h diff --git a/subsys/bluetooth/host/CMakeLists.txt b/subsys/bluetooth/host/CMakeLists.txt index fdcdb4d79da..6e3b5a278a5 100644 --- a/subsys/bluetooth/host/CMakeLists.txt +++ b/subsys/bluetooth/host/CMakeLists.txt @@ -36,6 +36,10 @@ if(CONFIG_BT_HCI_HOST) hci_common.c id.c ) + zephyr_library_sources_ifdef( + CONFIG_BT_BROADCASTER + adv.c + ) zephyr_library_sources_ifdef( CONFIG_BT_OBSERVER scan.c diff --git a/subsys/bluetooth/host/adv.c b/subsys/bluetooth/host/adv.c new file mode 100644 index 00000000000..6a76eccf17e --- /dev/null +++ b/subsys/bluetooth/host/adv.c @@ -0,0 +1,1635 @@ +/* + * Copyright (c) 2017-2021 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include + +#include + +#include +#include +#include + +#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(0 <= index && 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 (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 (!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; + + /* 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_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); +} +#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) */ diff --git a/subsys/bluetooth/host/adv.h b/subsys/bluetooth/host/adv.h new file mode 100644 index 00000000000..28d7a626792 --- /dev/null +++ b/subsys/bluetooth/host/adv.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2017-2021 Nordic Semiconductor ASA + * Copyright (c) 2015-2016 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +void bt_le_adv_resume(void); + +struct bt_le_ext_adv *bt_le_adv_lookup_legacy(void); + +void bt_le_adv_delete_legacy(void); +int bt_le_adv_set_enable(struct bt_le_ext_adv *adv, bool enable); + +void bt_le_ext_adv_foreach(void (*func)(struct bt_le_ext_adv *adv, void *data), + void *data); + +int bt_le_adv_set_enable_ext(struct bt_le_ext_adv *adv, + bool enable, + const struct bt_le_ext_adv_start_param *param); +int bt_le_adv_set_enable_legacy(struct bt_le_ext_adv *adv, bool enable); diff --git a/subsys/bluetooth/host/conn.c b/subsys/bluetooth/host/conn.c index 2117e2025cd..deba3c6bff2 100644 --- a/subsys/bluetooth/host/conn.c +++ b/subsys/bluetooth/host/conn.c @@ -29,6 +29,7 @@ #include "hci_core.h" #include "id.h" +#include "adv.h" #include "conn_internal.h" #include "l2cap_internal.h" #include "keys.h" diff --git a/subsys/bluetooth/host/hci_core.c b/subsys/bluetooth/host/hci_core.c index 59fbb425097..b2e8830cb25 100644 --- a/subsys/bluetooth/host/hci_core.c +++ b/subsys/bluetooth/host/hci_core.c @@ -39,6 +39,7 @@ #include "hci_ecc.h" #include "ecc.h" #include "id.h" +#include "adv.h" #include "scan.h" #include "conn_internal.h" @@ -89,10 +90,6 @@ struct bt_dev bt_dev = { static bt_ready_cb_t ready_cb; -#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_HCI_VS_EVT_USER) static bt_hci_vnd_evt_cb_t *hci_vnd_evt_cb; #endif /* CONFIG_BT_HCI_VS_EVT_USER */ @@ -340,187 +337,6 @@ int bt_hci_cmd_send_sync(uint16_t opcode, struct net_buf *buf, return 0; } -#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(0 <= index && 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) */ - -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 -} - -static void 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 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; - } -} - static int hci_le_read_max_data_len(uint16_t *tx_octets, uint16_t *tx_time) { struct bt_hci_rp_le_read_max_data_len *rp; @@ -1209,7 +1025,7 @@ static void le_conn_complete_adv_timeout(void) /* No advertising set terminated event, must be a * legacy advertiser set. */ - adv_delete_legacy(); + bt_le_adv_delete_legacy(); } /* There is no need to check ID address as only one @@ -1377,7 +1193,7 @@ static void enh_conn_complete(struct bt_hci_evt_le_enh_conn_complete *evt) * legacy advertiser set. */ if (!atomic_test_bit(adv->flags, BT_ADV_PERSIST)) { - adv_delete_legacy(); + bt_le_adv_delete_legacy(); } } } @@ -2874,144 +2690,6 @@ static void hci_cmd_status(struct net_buf *buf) } } -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); - } -} - - -#if defined(CONFIG_BT_EXT_ADV) -#if defined(CONFIG_BT_BROADCASTER) -static void 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) { - adv_delete_legacy(); - } -} - -static void 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) */ - int bt_hci_get_conn_handle(const struct bt_conn *conn, uint16_t *conn_handle) { if (conn->state != BT_CONN_CONNECTED) { @@ -3095,9 +2773,9 @@ static const struct event_handler meta_events[] = { #endif /* CONFIG_BT_SMP */ #if defined(CONFIG_BT_EXT_ADV) #if defined(CONFIG_BT_BROADCASTER) - EVENT_HANDLER(BT_HCI_EVT_LE_ADV_SET_TERMINATED, le_adv_set_terminated, + EVENT_HANDLER(BT_HCI_EVT_LE_ADV_SET_TERMINATED, bt_hci_le_adv_set_terminated, sizeof(struct bt_hci_evt_le_adv_set_terminated)), - EVENT_HANDLER(BT_HCI_EVT_LE_SCAN_REQ_RECEIVED, le_scan_req_received, + EVENT_HANDLER(BT_HCI_EVT_LE_SCAN_REQ_RECEIVED, bt_hci_le_scan_req_received, sizeof(struct bt_hci_evt_le_scan_req_received)), #endif #if defined(CONFIG_BT_OBSERVER) @@ -4673,128 +4351,6 @@ int bt_enable(bt_ready_cb_t cb) return 0; } -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); -} - int bt_set_name(const char *name) { #if defined(CONFIG_BT_DEVICE_NAME_DYNAMIC) @@ -4846,1171 +4402,6 @@ bool bt_addr_le_is_bonded(uint8_t id, const bt_addr_le_t *addr) } } -#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 (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 (!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; - - /* 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_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); -} -#endif /* CONFIG_BT_CONN */ -#endif /* CONFIG_BT_PER_ADV */ - -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); -} - -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; -} - -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) { - 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. - */ - 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; - } - } - - 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_WHITELIST) int bt_le_whitelist_add(const bt_addr_le_t *addr) { diff --git a/subsys/bluetooth/host/hci_core.h b/subsys/bluetooth/host/hci_core.h index ca5019fe729..5629759dd32 100644 --- a/subsys/bluetooth/host/hci_core.h +++ b/subsys/bluetooth/host/hci_core.h @@ -364,21 +364,6 @@ void bt_setup_public_id_addr(void); void bt_finalize_init(void); -int bt_le_adv_start_internal(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, - const bt_addr_le_t *peer); - -void bt_le_adv_resume(void); -int bt_le_adv_set_enable_legacy(struct bt_le_ext_adv *adv, bool enable); -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 bt_le_ext_adv *bt_le_adv_lookup_legacy(void); -int bt_le_adv_set_enable(struct bt_le_ext_adv *adv, bool enable); -void bt_le_ext_adv_foreach(void (*func)(struct bt_le_ext_adv *adv, void *data), - void *data); - void bt_hci_host_num_completed_packets(struct net_buf *buf); /* HCI event handlers */ @@ -406,3 +391,7 @@ void bt_hci_le_per_adv_report(struct net_buf *buf); void bt_hci_le_per_adv_sync_lost(struct net_buf *buf); void bt_hci_le_biginfo_adv_report(struct net_buf *buf); void bt_hci_le_past_received(struct net_buf *buf); + +/* Adv HCI event handlers */ +void bt_hci_le_adv_set_terminated(struct net_buf *buf); +void bt_hci_le_scan_req_received(struct net_buf *buf); diff --git a/subsys/bluetooth/host/id.c b/subsys/bluetooth/host/id.c index af3f44eb5da..713fcf268ff 100644 --- a/subsys/bluetooth/host/id.c +++ b/subsys/bluetooth/host/id.c @@ -14,6 +14,7 @@ #include "hci_core.h" #include "id.h" #include "scan.h" +#include "adv.h" #include "smp.h" #include "conn_internal.h" #include "keys.h"