Zephyr BLE Mesh Coded Phy

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);
}
Related