|  | /* | 
|  | * 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) || defined(CONFIG_BT_MESH_BRG_CFG_SRV)) | 
|  | 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) || defined(CONFIG_BT_MESH_BRG_CFG_SRV)) | 
|  | 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_BRG_CFG_SRV)) && \ | 
|  | (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) || IS_ENABLED(CONFIG_BT_MESH_BRG_CFG_SRV)) && | 
|  | !(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) || IS_ENABLED(CONFIG_BT_MESH_BRG_CFG_SRV))) { | 
|  | 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) || IS_ENABLED(CONFIG_BT_MESH_BRG_CFG_SRV)) && | 
|  | 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_ADV_NONCONN_IND) { | 
|  | 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, | 
|  | .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; | 
|  | } |