| /* |
| * 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 "host/hci_core.h" |
| |
| #include "net.h" |
| #include "foundation.h" |
| #include "beacon.h" |
| #include "prov.h" |
| #include "solicitation.h" |
| |
| #define LOG_LEVEL CONFIG_BT_MESH_ADV_LOG_LEVEL |
| #include <zephyr/logging/log.h> |
| LOG_MODULE_REGISTER(bt_mesh_adv_legacy); |
| |
| /* 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 |
| |
| static struct k_thread adv_thread_data; |
| static K_KERNEL_STACK_DEFINE(adv_thread_stack, CONFIG_BT_MESH_ADV_STACK_SIZE); |
| static int32_t adv_timeout; |
| |
| static bool is_mesh_suspended(void) |
| { |
| return atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED); |
| } |
| |
| static int bt_data_send(uint8_t num_events, uint16_t adv_int, |
| const struct bt_data *ad, size_t ad_len, |
| struct bt_mesh_adv_ctx *ctx) |
| { |
| struct bt_le_adv_param param = {}; |
| uint64_t uptime = k_uptime_get(); |
| uint16_t duration; |
| int err; |
| const int32_t adv_int_min = |
| ((bt_dev.hci_version >= BT_HCI_VERSION_5_0) ? |
| ADV_INT_FAST_MS : |
| ADV_INT_DEFAULT_MS); |
| |
| adv_int = MAX(adv_int_min, adv_int); |
| |
| ARG_UNUSED(uptime); |
| |
| /* Zephyr Bluetooth Low Energy Controller for mesh stack uses |
| * pre-emptible continuous scanning, allowing advertising events to be |
| * transmitted without delay when advertising is enabled. No need to |
| * compensate with scan window duration. |
| * An advertising event could be delayed by upto one interval when |
| * advertising is stopped and started in quick succession, hence add |
| * advertising interval to the total advertising duration. |
| */ |
| duration = adv_int + num_events * (adv_int + 10); |
| |
| /* Zephyr Bluetooth Low Energy Controller built for nRF51x SoCs use |
| * CONFIG_BT_CTLR_LOW_LAT=y, and continuous scanning cannot be |
| * pre-empted, hence, scanning will block advertising events from |
| * being transmitted. Increase the advertising duration by the |
| * amount of scan window duration to compensate for the blocked |
| * advertising events. |
| */ |
| if (IS_ENABLED(CONFIG_BT_CTLR_LOW_LAT)) { |
| duration += BT_MESH_SCAN_WINDOW_MS; |
| } |
| |
| LOG_DBG("count %u interval %ums duration %ums", |
| num_events, adv_int, duration); |
| |
| 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 = BT_MESH_ADV_SCAN_UNIT(adv_int); |
| param.interval_max = param.interval_min; |
| |
| err = bt_le_adv_start(¶m, ad, ad_len, NULL, 0); |
| |
| if (err) { |
| LOG_ERR("Advertising failed: err %d", err); |
| return err; |
| } |
| |
| LOG_DBG("Advertising started. Sleeping %u ms", duration); |
| |
| if (ctx) { |
| bt_mesh_adv_send_start(duration, err, ctx); |
| } |
| |
| if (!is_mesh_suspended()) { |
| k_sleep(K_MSEC(duration)); |
| } |
| |
| err = bt_le_adv_stop(); |
| if (err) { |
| LOG_ERR("Stopping advertising failed: err %d", err); |
| return err; |
| } |
| |
| LOG_DBG("Advertising stopped (%u ms)", (uint32_t) k_uptime_delta(&uptime)); |
| |
| return 0; |
| } |
| |
| int bt_mesh_adv_bt_data_send(uint8_t num_events, uint16_t adv_int, |
| const struct bt_data *ad, size_t ad_len) |
| { |
| return bt_data_send(num_events, adv_int, ad, ad_len, NULL); |
| } |
| |
| static inline void adv_send(struct bt_mesh_adv *adv) |
| { |
| uint16_t num_events = BT_MESH_TRANSMIT_COUNT(adv->ctx.xmit) + 1; |
| uint16_t adv_int; |
| struct bt_data ad; |
| |
| adv_int = BT_MESH_TRANSMIT_INT(adv->ctx.xmit); |
| |
| LOG_DBG("type %u len %u: %s", adv->ctx.type, |
| adv->b.len, bt_hex(adv->b.data, adv->b.len)); |
| |
| ad.type = bt_mesh_adv_type[adv->ctx.type]; |
| ad.data_len = adv->b.len; |
| ad.data = adv->b.data; |
| |
| bt_data_send(num_events, adv_int, &ad, 1, &adv->ctx); |
| } |
| |
| static void adv_thread(void *p1, void *p2, void *p3) |
| { |
| LOG_DBG("started"); |
| struct bt_mesh_adv *adv; |
| |
| while (!is_mesh_suspended()) { |
| if (IS_ENABLED(CONFIG_BT_MESH_GATT_SERVER)) { |
| adv = bt_mesh_adv_get(K_NO_WAIT); |
| if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION) && !adv) { |
| (void)bt_mesh_sol_send(); |
| } |
| |
| while (!adv) { |
| |
| /* Adv timeout may be set by a call from proxy |
| * to bt_mesh_adv_gatt_start: |
| */ |
| adv_timeout = SYS_FOREVER_MS; |
| (void)bt_mesh_adv_gatt_send(); |
| |
| adv = bt_mesh_adv_get(SYS_TIMEOUT_MS(adv_timeout)); |
| bt_le_adv_stop(); |
| |
| if (IS_ENABLED(CONFIG_BT_MESH_PROXY_SOLICITATION) && !adv) { |
| (void)bt_mesh_sol_send(); |
| } |
| } |
| } else { |
| adv = bt_mesh_adv_get(K_FOREVER); |
| } |
| |
| if (!adv) { |
| continue; |
| } |
| |
| /* busy == 0 means this was canceled */ |
| if (adv->ctx.busy) { |
| adv->ctx.busy = 0U; |
| adv_send(adv); |
| } |
| |
| struct bt_mesh_adv_ctx ctx = adv->ctx; |
| |
| adv->ctx.started = 0; |
| bt_mesh_adv_unref(adv); |
| bt_mesh_adv_send_end(0, &ctx); |
| |
| /* Give other threads a chance to run */ |
| k_yield(); |
| } |
| |
| /* Empty the advertising pool when advertising is disabled */ |
| while ((adv = bt_mesh_adv_get(K_NO_WAIT))) { |
| bt_mesh_adv_send_start(0, -ENODEV, &adv->ctx); |
| bt_mesh_adv_unref(adv); |
| } |
| } |
| |
| void bt_mesh_adv_local_ready(void) |
| { |
| /* Will be handled automatically */ |
| } |
| |
| void bt_mesh_adv_relay_ready(void) |
| { |
| /* Will be handled automatically */ |
| } |
| |
| void bt_mesh_adv_gatt_update(void) |
| { |
| bt_mesh_adv_get_cancel(); |
| } |
| |
| int bt_mesh_adv_terminate(struct bt_mesh_adv *adv) |
| { |
| ARG_UNUSED(adv); |
| |
| return 0; |
| } |
| |
| void bt_mesh_adv_init(void) |
| { |
| k_thread_create(&adv_thread_data, adv_thread_stack, |
| K_KERNEL_STACK_SIZEOF(adv_thread_stack), adv_thread, |
| NULL, NULL, NULL, K_PRIO_COOP(CONFIG_BT_MESH_ADV_PRIO), |
| 0, K_FOREVER); |
| k_thread_name_set(&adv_thread_data, "BT Mesh adv"); |
| } |
| |
| int bt_mesh_adv_enable(void) |
| { |
| /* The advertiser thread relies on BT_MESH_SUSPENDED flag. No point in starting the |
| * advertiser thread if the flag is not set. |
| */ |
| if (is_mesh_suspended()) { |
| return -EINVAL; |
| } |
| |
| k_thread_start(&adv_thread_data); |
| return 0; |
| } |
| |
| int bt_mesh_adv_disable(void) |
| { |
| int err; |
| |
| /* k_thread_join will sleep forever if BT_MESH_SUSPENDED flag is not set. The advertiser |
| * thread will exit once the flag is set. The flag is set by the higher layer function. Here |
| * we need to check that the flag is dropped and ensure that the thread is stopped. |
| */ |
| if (!is_mesh_suspended()) { |
| return -EINVAL; |
| } |
| |
| err = k_thread_join(&adv_thread_data, K_FOREVER); |
| LOG_DBG("Advertising disabled: %d", err); |
| |
| /* Since the thread will immediately stop after this function call and won’t perform any |
| * further operations, it’s safe to ignore the deadlock error (EDEADLK). |
| */ |
| return err == -EDEADLK ? 0 : err; |
| } |
| |
| 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) |
| { |
| adv_timeout = duration; |
| return bt_le_adv_start(param, ad, ad_len, sd, sd_len); |
| } |
| |
| int bt_mesh_wq_submit(struct k_work *work) |
| { |
| return k_work_submit(work); |
| } |