I am trying to add Coded Phy to my Zephyr BLE Mesh project. I know this is not officially supported, however is possible. Running the stock BLE Mesh Zephyr sample, I keep getting the following error, on trying to start a scan:
[00:00:10.746,246] <wrn> bt_hci_core: opcode 0x2041 status 0x12
[00:00:10.754,455] <err> bt_mesh_adv: starting scan failed (err -22)
Opcode 0x2041 is SDC_HCI_OPCODE_CMD_LE_SET_EXT_SCAN_PARAMS, and is returning invalid parameters, however the parameters specified don't seem to have any effect. Do I need to add something to prj.conf to support this?
I have modified adv.c as:
static void bt_mesh_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { if (adv_type != BT_GAP_ADV_TYPE_EXT_ADV) { return; } .. int bt_mesh_scan_enable(void) { struct bt_le_scan_param scan_param = { .type = active_scanning ? BT_LE_SCAN_TYPE_ACTIVE : BT_LE_SCAN_TYPE_PASSIVE, .interval = MESH_SCAN_INTERVAL, .options = BT_LE_SCAN_OPT_CODED | BT_LE_SCAN_OPT_NO_1M, .window = MESH_SCAN_WINDOW }; int err; LOG_DBG(""); err = bt_le_scan_start(&scan_param, bt_mesh_scan_cb); if (err && err != -EALREADY) { LOG_ERR("starting scan failed (err %d)", err); return err; } return 0; }
adv_ext:
struct bt_le_adv_param adv_param = { .id = BT_ID_DEFAULT, .interval_min = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS), .interval_max = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS), #if defined(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR) .options = BT_LE_ADV_OPT_USE_IDENTITY, #endif .options = BT_LE_ADV_OPT_CODED | BT_LE_ADV_OPT_EXT_ADV, };
I have tried adjusting the scan window and interval as well as the advertising parameters, but this doesn't help. What am I missing?
/* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/kernel.h> #include <zephyr/debug/stack.h> #include <zephyr/sys/util.h> #include <zephyr/net_buf.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> #include <zephyr/bluetooth/conn.h> #include <zephyr/bluetooth/mesh.h> #include "common/bt_str.h" #include "net.h" #include "foundation.h" #include "beacon.h" #include "prov.h" #include "proxy.h" #include "pb_gatt_srv.h" #include "solicitation.h" #include "statistic.h" #define LOG_LEVEL CONFIG_BT_MESH_ADV_LOG_LEVEL #include <zephyr/logging/log.h> LOG_MODULE_REGISTER(bt_mesh_adv); /* Window and Interval are equal for continuous scanning */ #define MESH_SCAN_INTERVAL BT_MESH_ADV_SCAN_UNIT(BT_MESH_SCAN_INTERVAL_MS) #define MESH_SCAN_WINDOW BT_MESH_ADV_SCAN_UNIT(BT_MESH_SCAN_WINDOW_MS) const uint8_t bt_mesh_adv_type[BT_MESH_ADV_TYPES] = { [BT_MESH_ADV_PROV] = BT_DATA_MESH_PROV, [BT_MESH_ADV_DATA] = BT_DATA_MESH_MESSAGE, [BT_MESH_ADV_BEACON] = BT_DATA_MESH_BEACON, [BT_MESH_ADV_URI] = BT_DATA_URI, }; static bool active_scanning; static K_FIFO_DEFINE(bt_mesh_adv_queue); static K_FIFO_DEFINE(bt_mesh_relay_queue); static K_FIFO_DEFINE(bt_mesh_friend_queue); K_MEM_SLAB_DEFINE_STATIC(local_adv_pool, sizeof(struct bt_mesh_adv), CONFIG_BT_MESH_ADV_BUF_COUNT, __alignof__(struct bt_mesh_adv)); #if defined(CONFIG_BT_MESH_RELAY_BUF_COUNT) K_MEM_SLAB_DEFINE_STATIC(relay_adv_pool, sizeof(struct bt_mesh_adv), CONFIG_BT_MESH_RELAY_BUF_COUNT, __alignof__(struct bt_mesh_adv)); #endif #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) K_MEM_SLAB_DEFINE_STATIC(friend_adv_pool, sizeof(struct bt_mesh_adv), CONFIG_BT_MESH_FRIEND_LPN_COUNT, __alignof__(struct bt_mesh_adv)); #endif void bt_mesh_adv_send_start(uint16_t duration, int err, struct bt_mesh_adv_ctx *ctx) { if (!ctx->started) { ctx->started = 1; if (ctx->cb && ctx->cb->start) { ctx->cb->start(duration, err, ctx->cb_data); } if (err) { ctx->cb = NULL; } else if (IS_ENABLED(CONFIG_BT_MESH_STATISTIC)) { bt_mesh_stat_succeeded_count(ctx); } } } void bt_mesh_adv_send_end(int err, struct bt_mesh_adv_ctx const *ctx) { if (ctx->started && ctx->cb && ctx->cb->end) { ctx->cb->end(err, ctx->cb_data); } } static struct bt_mesh_adv *adv_create_from_pool(struct k_mem_slab *buf_pool, enum bt_mesh_adv_type type, enum bt_mesh_adv_tag tag, uint8_t xmit, k_timeout_t timeout) { struct bt_mesh_adv_ctx *ctx; struct bt_mesh_adv *adv; int err; if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { LOG_WRN("Refusing to allocate buffer while suspended"); return NULL; } err = k_mem_slab_alloc(buf_pool, (void **)&adv, timeout); if (err) { return NULL; } adv->__ref = 1; net_buf_simple_init_with_data(&adv->b, adv->__bufs, BT_MESH_ADV_DATA_SIZE); net_buf_simple_reset(&adv->b); ctx = &adv->ctx; (void)memset(ctx, 0, sizeof(*ctx)); ctx->type = type; ctx->tag = tag; ctx->xmit = xmit; return adv; } struct bt_mesh_adv *bt_mesh_adv_ref(struct bt_mesh_adv *adv) { __ASSERT_NO_MSG(adv->__ref < UINT8_MAX); adv->__ref++; return adv; } void bt_mesh_adv_unref(struct bt_mesh_adv *adv) { __ASSERT_NO_MSG(adv->__ref > 0); if (--adv->__ref > 0) { return; } struct k_mem_slab *slab = &local_adv_pool; #if defined(CONFIG_BT_MESH_RELAY) if (adv->ctx.tag == BT_MESH_ADV_TAG_RELAY) { slab = &relay_adv_pool; } #endif #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) if (adv->ctx.tag == BT_MESH_ADV_TAG_FRIEND) { slab = &friend_adv_pool; } #endif k_mem_slab_free(slab, (void *)adv); } struct bt_mesh_adv *bt_mesh_adv_create(enum bt_mesh_adv_type type, enum bt_mesh_adv_tag tag, uint8_t xmit, k_timeout_t timeout) { #if defined(CONFIG_BT_MESH_RELAY) if (tag == BT_MESH_ADV_TAG_RELAY) { return adv_create_from_pool(&relay_adv_pool, type, tag, xmit, timeout); } #endif #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) if (tag == BT_MESH_ADV_TAG_FRIEND) { return adv_create_from_pool(&friend_adv_pool, type, tag, xmit, timeout); } #endif return adv_create_from_pool(&local_adv_pool, type, tag, xmit, timeout); } static struct bt_mesh_adv *process_events(struct k_poll_event *ev, int count) { for (; count; ev++, count--) { LOG_DBG("ev->state %u", ev->state); switch (ev->state) { case K_POLL_STATE_FIFO_DATA_AVAILABLE: return k_fifo_get(ev->fifo, K_NO_WAIT); case K_POLL_STATE_NOT_READY: case K_POLL_STATE_CANCELLED: break; default: LOG_WRN("Unexpected k_poll event state %u", ev->state); break; } } return NULL; } struct bt_mesh_adv *bt_mesh_adv_get(k_timeout_t timeout) { int err; struct k_poll_event events[] = { K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY, &bt_mesh_adv_queue, 0), #if defined(CONFIG_BT_MESH_RELAY) && \ (defined(CONFIG_BT_MESH_ADV_LEGACY) || \ defined(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET) || \ !(CONFIG_BT_MESH_RELAY_ADV_SETS)) K_POLL_EVENT_STATIC_INITIALIZER(K_POLL_TYPE_FIFO_DATA_AVAILABLE, K_POLL_MODE_NOTIFY_ONLY, &bt_mesh_relay_queue, 0), #endif }; err = k_poll(events, ARRAY_SIZE(events), timeout); if (err) { return NULL; } return process_events(events, ARRAY_SIZE(events)); } struct bt_mesh_adv *bt_mesh_adv_get_by_tag(enum bt_mesh_adv_tag_bit tags, k_timeout_t timeout) { if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && tags & BT_MESH_ADV_TAG_BIT_FRIEND) { return k_fifo_get(&bt_mesh_friend_queue, timeout); } if (IS_ENABLED(CONFIG_BT_MESH_RELAY) && !(tags & BT_MESH_ADV_TAG_BIT_LOCAL)) { return k_fifo_get(&bt_mesh_relay_queue, timeout); } if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE) && tags & BT_MESH_ADV_TAG_BIT_PROXY) { return NULL; } return bt_mesh_adv_get(timeout); } void bt_mesh_adv_get_cancel(void) { LOG_DBG(""); k_fifo_cancel_wait(&bt_mesh_adv_queue); if (IS_ENABLED(CONFIG_BT_MESH_RELAY)) { k_fifo_cancel_wait(&bt_mesh_relay_queue); } if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)) { k_fifo_cancel_wait(&bt_mesh_friend_queue); } } void bt_mesh_adv_send(struct bt_mesh_adv *adv, const struct bt_mesh_send_cb *cb, void *cb_data) { LOG_DBG("type 0x%02x len %u: %s", adv->ctx.type, adv->b.len, bt_hex(adv->b.data, adv->b.len)); if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { LOG_WRN("Sending advertisement while suspended"); } adv->ctx.cb = cb; adv->ctx.cb_data = cb_data; adv->ctx.busy = 1U; if (IS_ENABLED(CONFIG_BT_MESH_STATISTIC)) { bt_mesh_stat_planned_count(&adv->ctx); } if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && adv->ctx.tag == BT_MESH_ADV_TAG_FRIEND) { k_fifo_put(&bt_mesh_friend_queue, bt_mesh_adv_ref(adv)); bt_mesh_adv_friend_ready(); return; } if ((IS_ENABLED(CONFIG_BT_MESH_RELAY) && adv->ctx.tag == BT_MESH_ADV_TAG_RELAY) || (IS_ENABLED(CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS) && adv->ctx.tag == BT_MESH_ADV_TAG_PROV)) { k_fifo_put(&bt_mesh_relay_queue, bt_mesh_adv_ref(adv)); bt_mesh_adv_relay_ready(); return; } k_fifo_put(&bt_mesh_adv_queue, bt_mesh_adv_ref(adv)); bt_mesh_adv_local_ready(); } int bt_mesh_adv_gatt_send(void) { if (bt_mesh_is_provisioned()) { if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { LOG_DBG("Proxy Advertising"); return bt_mesh_proxy_adv_start(); } } else if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) { LOG_DBG("PB-GATT Advertising"); return bt_mesh_pb_gatt_srv_adv_start(); } return -ENOTSUP; } static void bt_mesh_scan_cb(const bt_addr_le_t *addr, int8_t rssi, uint8_t adv_type, struct net_buf_simple *buf) { if (adv_type != BT_GAP_ADV_TYPE_EXT_ADV) { return; } LOG_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len)); while (buf->len > 1) { struct net_buf_simple_state state; uint8_t len, type; len = net_buf_simple_pull_u8(buf); /* Check for early termination */ if (len == 0U) { return; } if (len > buf->len) { LOG_WRN("AD malformed"); return; } net_buf_simple_save(buf, &state); type = net_buf_simple_pull_u8(buf); buf->len = len - 1; switch (type) { case BT_DATA_MESH_MESSAGE: bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV); break; #if defined(CONFIG_BT_MESH_PB_ADV) case BT_DATA_MESH_PROV: bt_mesh_pb_adv_recv(buf); break; #endif case BT_DATA_MESH_BEACON: bt_mesh_beacon_recv(buf); break; case BT_DATA_UUID16_SOME: /* Fall through */ case BT_DATA_UUID16_ALL: if (IS_ENABLED(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV)) { /* Restore buffer with Solicitation PDU */ net_buf_simple_restore(buf, &state); bt_mesh_sol_recv(buf, len - 1); } break; default: break; } net_buf_simple_restore(buf, &state); net_buf_simple_pull(buf, len); } } int bt_mesh_scan_active_set(bool active) { if (active_scanning == active) { return 0; } active_scanning = active; bt_mesh_scan_disable(); return bt_mesh_scan_enable(); } int bt_mesh_scan_enable(void) { struct bt_le_scan_param scan_param = { .type = active_scanning ? BT_LE_SCAN_TYPE_ACTIVE : BT_LE_SCAN_TYPE_PASSIVE, .interval = MESH_SCAN_INTERVAL, .options = BT_LE_SCAN_OPT_CODED | BT_LE_SCAN_OPT_NO_1M, .window = MESH_SCAN_WINDOW }; int err; LOG_DBG(""); err = bt_le_scan_start(&scan_param, bt_mesh_scan_cb); if (err && err != -EALREADY) { LOG_ERR("starting scan failed (err %d)", err); return err; } return 0; } int bt_mesh_scan_disable(void) { int err; LOG_DBG(""); err = bt_le_scan_stop(); if (err && err != -EALREADY) { LOG_ERR("stopping scan failed (err %d)", err); return err; } return 0; }
/* * Copyright (c) 2021 Xiaomi Corporation * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include <zephyr/kernel.h> #include <zephyr/debug/stack.h> #include <zephyr/sys/iterable_sections.h> #include <zephyr/net_buf.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> #include <zephyr/bluetooth/mesh.h> #if defined(CONFIG_BT_LL_SOFTDEVICE) #include <sdc_hci_vs.h> #endif #include "common/bt_str.h" #include "host/hci_core.h" #include "net.h" #include "proxy.h" #include "solicitation.h" #define LOG_LEVEL CONFIG_BT_MESH_ADV_LOG_LEVEL #include <zephyr/logging/log.h> LOG_MODULE_REGISTER(bt_mesh_adv_ext); /* Convert from ms to 0.625ms units */ #define ADV_INT_FAST_MS 300 #ifndef CONFIG_BT_MESH_RELAY_ADV_SETS #define CONFIG_BT_MESH_RELAY_ADV_SETS 0 #endif #ifdef CONFIG_BT_MESH_ADV_STACK_SIZE #define MESH_WORKQ_PRIORITY CONFIG_BT_MESH_ADV_PRIO #define MESH_WORKQ_STACK_SIZE CONFIG_BT_MESH_ADV_STACK_SIZE #else #define MESH_WORKQ_PRIORITY 0 #define MESH_WORKQ_STACK_SIZE 0 #endif enum { /** Controller is currently advertising */ ADV_FLAG_ACTIVE, /** Advertising sending completed */ ADV_FLAG_SENT, /** Currently performing proxy advertising */ ADV_FLAG_PROXY, /** The proxy has been start, but maybe pending. */ ADV_FLAG_PROXY_START, /** The send-call has been pending. */ ADV_FLAG_SCHEDULE_PENDING, /** Custom adv params have been set, we need to update the parameters on * the next send. */ ADV_FLAG_UPDATE_PARAMS, /** The advertiser is suspending. */ ADV_FLAG_SUSPENDING, /* Number of adv flags. */ ADV_FLAGS_NUM }; struct bt_mesh_ext_adv { const enum bt_mesh_adv_tag_bit tags; ATOMIC_DEFINE(flags, ADV_FLAGS_NUM); struct bt_le_ext_adv *instance; struct bt_mesh_adv *adv; uint32_t timestamp; struct k_work work; struct bt_le_adv_param adv_param; }; static void send_pending_adv(struct k_work *work); static bool schedule_send(struct bt_mesh_ext_adv *ext_adv); static struct k_work_q bt_mesh_workq; static K_KERNEL_STACK_DEFINE(thread_stack, MESH_WORKQ_STACK_SIZE); #if defined(CONFIG_BT_MESH_WORKQ_MESH) #define MESH_WORKQ &bt_mesh_workq #else /* CONFIG_BT_MESH_WORKQ_SYS */ #define MESH_WORKQ &k_sys_work_q #endif /* CONFIG_BT_MESH_WORKQ_MESH */ static struct bt_mesh_ext_adv advs[] = { [0] = { .tags = ( #if !defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) BT_MESH_ADV_TAG_BIT_FRIEND | #endif #if !defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE) BT_MESH_ADV_TAG_BIT_PROXY | #endif /* !CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ #if defined(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET) BT_MESH_ADV_TAG_BIT_RELAY | #endif /* CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET */ #if defined(CONFIG_BT_MESH_PB_ADV) BT_MESH_ADV_TAG_BIT_PROV | #endif /* CONFIG_BT_MESH_PB_ADV */ BT_MESH_ADV_TAG_BIT_LOCAL ), .work = Z_WORK_INITIALIZER(send_pending_adv), }, #if CONFIG_BT_MESH_RELAY_ADV_SETS [1 ... CONFIG_BT_MESH_RELAY_ADV_SETS] = { .tags = ( #if defined(CONFIG_BT_MESH_RELAY) BT_MESH_ADV_TAG_BIT_RELAY | #endif /* CONFIG_BT_MESH_RELAY */ #if defined(CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS) BT_MESH_ADV_TAG_BIT_PROV | #endif /* CONFIG_BT_MESH_PB_ADV_USE_RELAY_SETS */ 0), .work = Z_WORK_INITIALIZER(send_pending_adv), }, #endif /* CONFIG_BT_MESH_RELAY_ADV_SETS */ #if defined(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) { .tags = BT_MESH_ADV_TAG_BIT_FRIEND, .work = Z_WORK_INITIALIZER(send_pending_adv), }, #endif /* CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE */ #if defined(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE) { .tags = BT_MESH_ADV_TAG_BIT_PROXY, .work = Z_WORK_INITIALIZER(send_pending_adv), }, #endif /* CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE */ }; BUILD_ASSERT(ARRAY_SIZE(advs) <= CONFIG_BT_EXT_ADV_MAX_ADV_SET, "Insufficient adv instances"); static inline struct bt_mesh_ext_adv *relay_adv_get(void) { if (!!(CONFIG_BT_MESH_RELAY_ADV_SETS)) { return &advs[1]; } else { return &advs[0]; } } static inline struct bt_mesh_ext_adv *gatt_adv_get(void) { if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_GATT_SEPARATE)) { return &advs[ARRAY_SIZE(advs) - 1]; } else { return &advs[0]; } } static int set_adv_randomness(uint8_t handle, int rand_us) { #if defined(CONFIG_BT_LL_SOFTDEVICE) struct net_buf *buf; sdc_hci_cmd_vs_set_adv_randomness_t *cmd_params; buf = bt_hci_cmd_create(SDC_HCI_OPCODE_CMD_VS_SET_ADV_RANDOMNESS, sizeof(*cmd_params)); if (!buf) { LOG_ERR("Could not allocate command buffer"); return -ENOMEM; } cmd_params = net_buf_add(buf, sizeof(*cmd_params)); cmd_params->adv_handle = handle; cmd_params->rand_us = rand_us; return bt_hci_cmd_send_sync(SDC_HCI_OPCODE_CMD_VS_SET_ADV_RANDOMNESS, buf, NULL); #else return 0; #endif /* defined(CONFIG_BT_LL_SOFTDEVICE) */ } static int adv_start(struct bt_mesh_ext_adv *ext_adv, const struct bt_le_adv_param *param, struct bt_le_ext_adv_start_param *start, const struct bt_data *ad, size_t ad_len, const struct bt_data *sd, size_t sd_len) { int err; if (!ext_adv->instance) { LOG_ERR("Mesh advertiser not enabled"); return -ENODEV; } if (atomic_test_and_set_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) { LOG_ERR("Advertiser is busy"); return -EBUSY; } if (atomic_test_bit(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS)) { err = bt_le_ext_adv_update_param(ext_adv->instance, param); if (err) { LOG_ERR("Failed updating adv params: %d", err); atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE); return err; } atomic_set_bit_to(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS, param != &ext_adv->adv_param); } err = bt_le_ext_adv_set_data(ext_adv->instance, ad, ad_len, sd, sd_len); if (err) { LOG_ERR("Failed setting adv data: %d", err); atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE); return err; } ext_adv->timestamp = k_uptime_get_32(); err = bt_le_ext_adv_start(ext_adv->instance, start); if (err) { LOG_ERR("Advertising failed: err %d", err); atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE); } return err; } static int bt_data_send(struct bt_mesh_ext_adv *ext_adv, uint8_t num_events, uint16_t adv_interval, const struct bt_data *ad, size_t ad_len) { struct bt_le_ext_adv_start_param start = { .num_events = num_events, }; adv_interval = MAX(ADV_INT_FAST_MS, adv_interval); /* Only update advertising parameters if they're different */ if (ext_adv->adv_param.interval_min != BT_MESH_ADV_SCAN_UNIT(adv_interval)) { ext_adv->adv_param.interval_min = BT_MESH_ADV_SCAN_UNIT(adv_interval); ext_adv->adv_param.interval_max = ext_adv->adv_param.interval_min; atomic_set_bit(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS); } return adv_start(ext_adv, &ext_adv->adv_param, &start, ad, ad_len, NULL, 0); } static int adv_send(struct bt_mesh_ext_adv *ext_adv, struct bt_mesh_adv *adv) { uint8_t num_events = BT_MESH_TRANSMIT_COUNT(adv->ctx.xmit) + 1; uint16_t duration, adv_int; struct bt_data ad; int err; adv_int = BT_MESH_TRANSMIT_INT(adv->ctx.xmit); /* Upper boundary estimate: */ duration = num_events * (adv_int + 10); LOG_DBG("type %u len %u: %s", adv->ctx.type, adv->b.len, bt_hex(adv->b.data, adv->b.len)); LOG_DBG("count %u interval %ums duration %ums", num_events, adv_int, duration); ad.type = bt_mesh_adv_type[adv->ctx.type]; ad.data_len = adv->b.len; ad.data = adv->b.data; err = bt_data_send(ext_adv, num_events, adv_int, &ad, 1); if (!err) { ext_adv->adv = bt_mesh_adv_ref(adv); } bt_mesh_adv_send_start(duration, err, &adv->ctx); return err; } static const char * const adv_tag_to_str[] = { [BT_MESH_ADV_TAG_LOCAL] = "local adv", [BT_MESH_ADV_TAG_RELAY] = "relay adv", [BT_MESH_ADV_TAG_PROXY] = "proxy adv", [BT_MESH_ADV_TAG_FRIEND] = "friend adv", [BT_MESH_ADV_TAG_PROV] = "prov adv", }; static bool schedule_send_with_mask(struct bt_mesh_ext_adv *ext_adv, int ignore_mask) { if (atomic_test_and_clear_bit(ext_adv->flags, ADV_FLAG_PROXY)) { atomic_clear_bit(ext_adv->flags, ADV_FLAG_PROXY_START); (void)bt_le_ext_adv_stop(ext_adv->instance); atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE); } if (atomic_test_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) { atomic_set_bit(ext_adv->flags, ADV_FLAG_SCHEDULE_PENDING); return false; } else if ((~ignore_mask) & k_work_busy_get(&ext_adv->work)) { return false; } atomic_clear_bit(ext_adv->flags, ADV_FLAG_SCHEDULE_PENDING); bt_mesh_wq_submit(&ext_adv->work); return true; } static void send_pending_adv(struct k_work *work) { struct bt_mesh_ext_adv *ext_adv; struct bt_mesh_adv *adv; int err; ext_adv = CONTAINER_OF(work, struct bt_mesh_ext_adv, work); if (atomic_test_bit(ext_adv->flags, ADV_FLAG_SUSPENDING)) { LOG_DBG("Advertiser is suspending"); return; } if (atomic_test_and_clear_bit(ext_adv->flags, ADV_FLAG_SENT)) { LOG_DBG("Advertising stopped after %u ms for %s", k_uptime_get_32() - ext_adv->timestamp, ext_adv->adv ? adv_tag_to_str[ext_adv->adv->ctx.tag] : adv_tag_to_str[BT_MESH_ADV_TAG_PROXY]); atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE); atomic_clear_bit(ext_adv->flags, ADV_FLAG_PROXY); atomic_clear_bit(ext_adv->flags, ADV_FLAG_PROXY_START); if (ext_adv->adv) { struct bt_mesh_adv_ctx ctx = ext_adv->adv->ctx; ext_adv->adv->ctx.started = 0; bt_mesh_adv_unref(ext_adv->adv); bt_mesh_adv_send_end(0, &ctx); ext_adv->adv = NULL; } } while ((adv = bt_mesh_adv_get_by_tag(ext_adv->tags, K_NO_WAIT))) { /* busy == 0 means this was canceled */ if (!adv->ctx.busy) { bt_mesh_adv_unref(adv); continue; } adv->ctx.busy = 0U; err = adv_send(ext_adv, adv); bt_mesh_adv_unref(adv); if (!err) { return; /* Wait for advertising to finish */ } } if (ext_adv->instance == NULL) { LOG_DBG("Advertiser is suspended or deleted"); return; } if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION) && !bt_mesh_sol_send()) { return; } if (!IS_ENABLED(CONFIG_BT_MESH_GATT_SERVER) || !(ext_adv->tags & BT_MESH_ADV_TAG_BIT_PROXY)) { return; } atomic_set_bit(ext_adv->flags, ADV_FLAG_PROXY_START); if (!bt_mesh_adv_gatt_send()) { atomic_set_bit(ext_adv->flags, ADV_FLAG_PROXY); } if (atomic_test_and_clear_bit(ext_adv->flags, ADV_FLAG_SCHEDULE_PENDING)) { schedule_send_with_mask(ext_adv, K_WORK_RUNNING); } } static bool schedule_send(struct bt_mesh_ext_adv *ext_adv) { return schedule_send_with_mask(ext_adv, 0); } void bt_mesh_adv_gatt_update(void) { (void)schedule_send(gatt_adv_get()); } void bt_mesh_adv_local_ready(void) { (void)schedule_send(advs); } void bt_mesh_adv_relay_ready(void) { struct bt_mesh_ext_adv *ext_adv = relay_adv_get(); for (int i = 0; i < CONFIG_BT_MESH_RELAY_ADV_SETS; i++) { if (schedule_send(&ext_adv[i])) { return; } } /* Use the main adv set for the sending of relay messages. */ if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_RELAY_USING_MAIN_ADV_SET) || CONFIG_BT_MESH_RELAY_ADV_SETS == 0) { (void)schedule_send(advs); } } void bt_mesh_adv_friend_ready(void) { if (IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE)) { schedule_send(&advs[1 + CONFIG_BT_MESH_RELAY_ADV_SETS]); } else { schedule_send(&advs[0]); } } int bt_mesh_adv_terminate(struct bt_mesh_adv *adv) { int err; for (int i = 0; i < ARRAY_SIZE(advs); i++) { struct bt_mesh_ext_adv *ext_adv = &advs[i]; if (ext_adv->adv != adv) { continue; } if (!atomic_test_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) { return 0; } err = bt_le_ext_adv_stop(ext_adv->instance); if (err) { LOG_ERR("Failed to stop adv %d", err); return err; } /* Do not call `cb:end`, since this user action */ adv->ctx.cb = NULL; atomic_set_bit(ext_adv->flags, ADV_FLAG_SENT); bt_mesh_wq_submit(&ext_adv->work); return 0; } return -EINVAL; } void bt_mesh_adv_init(void) { struct bt_le_adv_param adv_param = { .id = BT_ID_DEFAULT, .interval_min = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS), .interval_max = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS), #if defined(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR) .options = BT_LE_ADV_OPT_USE_IDENTITY, #endif .options = BT_LE_ADV_OPT_CODED | BT_LE_ADV_OPT_EXT_ADV, }; for (int i = 0; i < ARRAY_SIZE(advs); i++) { (void)memcpy(&advs[i].adv_param, &adv_param, sizeof(adv_param)); } if (IS_ENABLED(CONFIG_BT_MESH_WORKQ_MESH)) { k_work_queue_init(&bt_mesh_workq); k_work_queue_start(&bt_mesh_workq, thread_stack, MESH_WORKQ_STACK_SIZE, K_PRIO_COOP(MESH_WORKQ_PRIORITY), NULL); k_thread_name_set(&bt_mesh_workq.thread, "BT MESH WQ"); } #if defined(CONFIG_BT_LL_SOFTDEVICE) const sdc_hci_cmd_vs_scan_accept_ext_adv_packets_set_t cmd_params = { .accept_ext_adv_packets = IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_ACCEPT_EXT_ADV_PACKETS), }; int err = sdc_hci_cmd_vs_scan_accept_ext_adv_packets_set(&cmd_params); if (err) { LOG_ERR("Failed to set accept_ext_adv_packets: %d", err); } #endif } static struct bt_mesh_ext_adv *adv_instance_find(struct bt_le_ext_adv *instance) { for (int i = 0; i < ARRAY_SIZE(advs); i++) { if (advs[i].instance == instance) { return &advs[i]; } } return NULL; } static void adv_sent(struct bt_le_ext_adv *instance, struct bt_le_ext_adv_sent_info *info) { struct bt_mesh_ext_adv *ext_adv = adv_instance_find(instance); if (!ext_adv) { LOG_WRN("Unexpected adv instance"); return; } if (!atomic_test_bit(ext_adv->flags, ADV_FLAG_ACTIVE)) { return; } atomic_set_bit(ext_adv->flags, ADV_FLAG_SENT); bt_mesh_wq_submit(&ext_adv->work); } #if defined(CONFIG_BT_MESH_GATT_SERVER) static void connected(struct bt_le_ext_adv *instance, struct bt_le_ext_adv_connected_info *info) { struct bt_mesh_ext_adv *ext_adv = gatt_adv_get(); if (atomic_test_and_clear_bit(ext_adv->flags, ADV_FLAG_PROXY_START)) { atomic_clear_bit(ext_adv->flags, ADV_FLAG_ACTIVE); (void)schedule_send(ext_adv); } } #endif /* CONFIG_BT_MESH_GATT_SERVER */ int bt_mesh_adv_enable(void) { int err; static const struct bt_le_ext_adv_cb adv_cb = { .sent = adv_sent, #if defined(CONFIG_BT_MESH_GATT_SERVER) .connected = connected, #endif /* CONFIG_BT_MESH_GATT_SERVER */ }; if (advs[0].instance) { /* Already initialized */ return 0; } for (int i = 0; i < ARRAY_SIZE(advs); i++) { err = bt_le_ext_adv_create(&advs[i].adv_param, &adv_cb, &advs[i].instance); if (err) { return err; } if (IS_ENABLED(CONFIG_BT_LL_SOFTDEVICE) && IS_ENABLED(CONFIG_BT_MESH_ADV_EXT_FRIEND_SEPARATE) && advs[i].tags == BT_MESH_ADV_TAG_BIT_FRIEND) { err = set_adv_randomness(advs[i].instance->handle, 0); if (err) { LOG_ERR("Failed to set zero randomness: %d", err); } } } return 0; } int bt_mesh_adv_disable(void) { struct k_work_sync sync; int err; for (int i = 0; i < ARRAY_SIZE(advs); i++) { atomic_set_bit(advs[i].flags, ADV_FLAG_SUSPENDING); if (k_current_get() != k_work_queue_thread_get(MESH_WORKQ) || (k_work_busy_get(&advs[i].work) & K_WORK_RUNNING) == 0) { k_work_flush(&advs[i].work, &sync); } err = bt_le_ext_adv_stop(advs[i].instance); if (err) { LOG_ERR("Failed to stop adv %d", err); return err; } /* `adv_sent` is called to finish transmission of an adv buffer that was pushed to * the host before the advertiser was stopped, but did not finish. */ adv_sent(advs[i].instance, NULL); err = bt_le_ext_adv_delete(advs[i].instance); if (err) { LOG_ERR("Failed to delete adv %d", err); return err; } advs[i].instance = NULL; atomic_clear_bit(advs[i].flags, ADV_FLAG_SUSPENDING); } return 0; } int bt_mesh_adv_gatt_start(const struct bt_le_adv_param *param, int32_t duration, const struct bt_data *ad, size_t ad_len, const struct bt_data *sd, size_t sd_len) { struct bt_mesh_ext_adv *ext_adv = gatt_adv_get(); struct bt_le_ext_adv_start_param start = { /* Timeout is set in 10 ms steps, with 0 indicating "forever" */ .timeout = (duration == SYS_FOREVER_MS) ? 0 : MAX(1, duration / 10), }; LOG_DBG("Start advertising %d ms", duration); atomic_set_bit(ext_adv->flags, ADV_FLAG_UPDATE_PARAMS); return adv_start(ext_adv, param, &start, ad, ad_len, sd, sd_len); } int bt_mesh_adv_bt_data_send(uint8_t num_events, uint16_t adv_interval, const struct bt_data *ad, size_t ad_len) { return bt_data_send(advs, num_events, adv_interval, ad, ad_len); } int bt_mesh_wq_submit(struct k_work *work) { return k_work_submit_to_queue(MESH_WORKQ, work); }