/* Bluetooth Mesh */ /* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_ADV) #define LOG_MODULE_NAME bt_mesh_adv #include "common/log.h" #include "host/hci_core.h" #include "adv.h" #include "net.h" #include "foundation.h" #include "beacon.h" #include "prov.h" #include "proxy.h" /* Convert from ms to 0.625ms units */ #define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5) /* Window and Interval are equal for continuous scanning */ #define MESH_SCAN_INTERVAL_MS 30 #define MESH_SCAN_WINDOW_MS 30 #define MESH_SCAN_INTERVAL ADV_SCAN_UNIT(MESH_SCAN_INTERVAL_MS) #define MESH_SCAN_WINDOW ADV_SCAN_UNIT(MESH_SCAN_WINDOW_MS) /* Pre-5.0 controllers enforce a minimum interval of 100ms * whereas 5.0+ controllers can go down to 20ms. */ #define ADV_INT_DEFAULT_MS 100 #define ADV_INT_FAST_MS 20 #if defined(CONFIG_BT_HOST_CRYPTO) #define ADV_STACK_SIZE 1024 #else #define ADV_STACK_SIZE 768 #endif static K_FIFO_DEFINE(adv_queue); static struct k_thread adv_thread_data; static K_THREAD_STACK_DEFINE(adv_thread_stack, ADV_STACK_SIZE); NET_BUF_POOL_DEFINE(adv_buf_pool, CONFIG_BT_MESH_ADV_BUF_COUNT, BT_MESH_ADV_DATA_SIZE, BT_MESH_ADV_USER_DATA_SIZE, NULL); static struct bt_mesh_adv adv_pool[CONFIG_BT_MESH_ADV_BUF_COUNT]; static struct bt_mesh_adv *adv_alloc(int id) { return &adv_pool[id]; } static inline void adv_send_start(uint16_t duration, int err, const struct bt_mesh_send_cb *cb, void *cb_data) { if (cb && cb->start) { cb->start(duration, err, cb_data); } } static inline void adv_send_end(int err, const struct bt_mesh_send_cb *cb, void *cb_data) { if (cb && cb->end) { cb->end(err, cb_data); } } static inline void adv_send(struct net_buf *buf) { static const uint8_t adv_type[] = { [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, }; const int32_t adv_int_min = ((bt_dev.hci_version >= BT_HCI_VERSION_5_0) ? ADV_INT_FAST_MS : ADV_INT_DEFAULT_MS); const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb; void *cb_data = BT_MESH_ADV(buf)->cb_data; struct bt_le_adv_param param = {}; uint16_t duration, adv_int; struct bt_data ad; int err; adv_int = MAX(adv_int_min, BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit)); duration = (MESH_SCAN_WINDOW_MS + ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * (adv_int + 10))); BT_DBG("type %u len %u: %s", BT_MESH_ADV(buf)->type, buf->len, bt_hex(buf->data, buf->len)); BT_DBG("count %u interval %ums duration %ums", BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1, adv_int, duration); ad.type = adv_type[BT_MESH_ADV(buf)->type]; ad.data_len = buf->len; ad.data = buf->data; if (IS_ENABLED(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR)) { param.options = BT_LE_ADV_OPT_USE_IDENTITY; } else { param.options = 0U; } param.id = BT_ID_DEFAULT; param.interval_min = ADV_SCAN_UNIT(adv_int); param.interval_max = param.interval_min; err = bt_le_adv_start(¶m, &ad, 1, NULL, 0); net_buf_unref(buf); adv_send_start(duration, err, cb, cb_data); if (err) { BT_ERR("Advertising failed: err %d", err); return; } BT_DBG("Advertising started. Sleeping %u ms", duration); k_sleep(K_MSEC(duration)); err = bt_le_adv_stop(); adv_send_end(err, cb, cb_data); if (err) { BT_ERR("Stopping advertising failed: err %d", err); return; } BT_DBG("Advertising stopped"); } static void adv_thread(void *p1, void *p2, void *p3) { BT_DBG("started"); while (1) { struct net_buf *buf; if (IS_ENABLED(CONFIG_BT_MESH_PROXY)) { buf = net_buf_get(&adv_queue, K_NO_WAIT); while (!buf) { k_timeout_t timeout; timeout = bt_mesh_proxy_adv_start(); BT_DBG("Proxy Advertising"); buf = net_buf_get(&adv_queue, timeout); bt_mesh_proxy_adv_stop(); } } else { buf = net_buf_get(&adv_queue, K_FOREVER); } if (!buf) { continue; } /* busy == 0 means this was canceled */ if (BT_MESH_ADV(buf)->busy) { BT_MESH_ADV(buf)->busy = 0U; adv_send(buf); } else { net_buf_unref(buf); } /* Give other threads a chance to run */ k_yield(); } } void bt_mesh_adv_update(void) { BT_DBG(""); k_fifo_cancel_wait(&adv_queue); } struct net_buf *bt_mesh_adv_create_from_pool(struct net_buf_pool *pool, bt_mesh_adv_alloc_t get_id, enum bt_mesh_adv_type type, uint8_t xmit, k_timeout_t timeout) { struct bt_mesh_adv *adv; struct net_buf *buf; if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { BT_WARN("Refusing to allocate buffer while suspended"); return NULL; } buf = net_buf_alloc(pool, timeout); if (!buf) { return NULL; } adv = get_id(net_buf_id(buf)); BT_MESH_ADV(buf) = adv; (void)memset(adv, 0, sizeof(*adv)); adv->type = type; adv->xmit = xmit; return buf; } struct net_buf *bt_mesh_adv_create(enum bt_mesh_adv_type type, uint8_t xmit, k_timeout_t timeout) { return bt_mesh_adv_create_from_pool(&adv_buf_pool, adv_alloc, type, xmit, timeout); } void bt_mesh_adv_send(struct net_buf *buf, const struct bt_mesh_send_cb *cb, void *cb_data) { BT_DBG("type 0x%02x len %u: %s", BT_MESH_ADV(buf)->type, buf->len, bt_hex(buf->data, buf->len)); BT_MESH_ADV(buf)->cb = cb; BT_MESH_ADV(buf)->cb_data = cb_data; BT_MESH_ADV(buf)->busy = 1U; net_buf_put(&adv_queue, net_buf_ref(buf)); } 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; } BT_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) { BT_WARN("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; default: break; } net_buf_simple_restore(buf, &state); net_buf_simple_pull(buf, len); } } void bt_mesh_adv_init(void) { k_thread_create(&adv_thread_data, adv_thread_stack, K_THREAD_STACK_SIZEOF(adv_thread_stack), adv_thread, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT); k_thread_name_set(&adv_thread_data, "BT Mesh adv"); } int bt_mesh_scan_enable(void) { struct bt_le_scan_param scan_param = { .type = BT_HCI_LE_SCAN_PASSIVE, .filter_dup = BT_LE_SCAN_OPT_NONE, .interval = MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW }; int err; BT_DBG(""); err = bt_le_scan_start(&scan_param, bt_mesh_scan_cb); if (err && err != -EALREADY) { BT_ERR("starting scan failed (err %d)", err); return err; } return 0; } int bt_mesh_scan_disable(void) { int err; BT_DBG(""); err = bt_le_scan_stop(); if (err && err != -EALREADY) { BT_ERR("stopping scan failed (err %d)", err); return err; } return 0; }