/* Bluetooth Mesh */ /* * Copyright (c) 2018 Nordic Semiconductor ASA * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #define MESH_LOG_MODULE BLE_MESH_ADV_LOG #include "adv.h" #include "net.h" #include "foundation.h" #include "beacon.h" #include "prov.h" #include "proxy.h" #include "mesh/glue.h" #include "pb_gatt_srv.h" #if MYNEWT_VAL(BLE_MESH_ADV_LEGACY) /* Convert from ms to 0.625ms units */ #define ADV_SCAN_UNIT(_ms) ((_ms) * 8 / 5) #if (MYNEWT_VAL(BSP_NRF51) && !MYNEWT_VAL(BLE_CONTROLLER)) #define CONFIG_BT_CTLR_LOW_LAT 1 #else #define CONFIG_BT_CTLR_LOW_LAT 0 #endif /* 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 int32_t adv_int_min = ADV_INT_DEFAULT_MS; static int adv_initialized = false; /* TinyCrypt PRNG consumes a lot of stack space, so we need to have * an increased call stack whenever it's used. */ #if MYNEWT OS_TASK_STACK_DEFINE(g_blemesh_stack, MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE)); struct os_task adv_task; #endif static int32_t adv_timeout; static inline void adv_send(struct os_mbuf *buf) { static const uint8_t adv_type[] = { [BT_MESH_ADV_PROV] = BLE_HS_ADV_TYPE_MESH_PROV, [BT_MESH_ADV_DATA] = BLE_HS_ADV_TYPE_MESH_MESSAGE, [BT_MESH_ADV_BEACON] = BLE_HS_ADV_TYPE_MESH_BEACON, [BT_MESH_ADV_URI] = BLE_HS_ADV_TYPE_URI, }; struct ble_gap_adv_params param = { 0 }; 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)); #if MYNEWT_VAL(BLE_CONTROLLER) duration = ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * (adv_int + 10)); #else /* 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 + ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * (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 (CONFIG_BT_CTLR_LOW_LAT) { duration += BT_MESH_SCAN_WINDOW_MS; } #endif BT_DBG("type %u om_len %u: %s", BT_MESH_ADV(buf)->type, buf->om_len, bt_hex(buf->om_data, buf->om_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->om_len; ad.data = buf->om_data; param.itvl_min = ADV_SCAN_UNIT(adv_int); param.itvl_max = param.itvl_min; param.conn_mode = BLE_GAP_CONN_MODE_NON; int64_t time = k_uptime_get(); err = bt_le_adv_start(¶m, &ad, 1, NULL, 0); bt_mesh_adv_send_start(duration, err, BT_MESH_ADV(buf)); 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(); if (err) { BT_ERR("Stopping advertising failed: err %d", err); return; } BT_DBG("Advertising stopped (%u ms)", (uint32_t) k_uptime_delta(&time)); } void mesh_adv_thread(void *args) { static struct ble_npl_event *ev; struct os_mbuf *buf; BT_DBG("started"); while (1) { if (MYNEWT_VAL(BLE_MESH_GATT_SERVER)) { ev = ble_npl_eventq_get(&bt_mesh_adv_queue, 0); while (!ev) { /* Adv timeout may be set by a call from proxy * to bt_mesh_adv_start: */ adv_timeout = K_FOREVER; if (bt_mesh_is_provisioned()) { if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { bt_mesh_proxy_adv_start(); BT_DBG("Proxy Advertising up to %d ms", (int) adv_timeout); } } else if (IS_ENABLED(CONFIG_BT_MESH_PB_GATT)) { bt_mesh_pb_gatt_adv_start(); BT_DBG("PB-GATT Advertising up to %d ms", (int) adv_timeout); } ev = ble_npl_eventq_get(&bt_mesh_adv_queue, ble_npl_time_ms_to_ticks32(adv_timeout)); bt_le_adv_stop(); } } else { ev = ble_npl_eventq_get(&bt_mesh_adv_queue, BLE_NPL_TIME_FOREVER); } if (!ev || !ble_npl_event_get_arg(ev)) { continue; } buf = ble_npl_event_get_arg(ev); /* busy == 0 means this was canceled */ if (BT_MESH_ADV(buf)->busy) { BT_MESH_ADV(buf)->busy = 0; adv_send(buf); } net_buf_unref(buf); /* os_sched(NULL); */ } } void bt_mesh_adv_update(void) { static struct ble_npl_event ev = { }; BT_DBG(""); ble_npl_eventq_put(&bt_mesh_adv_queue, &ev); } void bt_mesh_adv_buf_ready(void) { /* Will be handled automatically */ } void bt_mesh_adv_init(void) { int rc; /* Advertising should only be initialized once. Calling * os_task init the second time will result in an assert. */ if (adv_initialized) { return; } rc = os_mempool_init(&adv_buf_mempool, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT), BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, adv_buf_mem, "adv_buf_pool"); assert(rc == 0); rc = os_mbuf_pool_init(&adv_os_mbuf_pool, &adv_buf_mempool, BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE, MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT)); assert(rc == 0); ble_npl_eventq_init(&bt_mesh_adv_queue); #if MYNEWT os_task_init(&adv_task, "mesh_adv", mesh_adv_thread, NULL, MYNEWT_VAL(BLE_MESH_ADV_TASK_PRIO), OS_WAIT_FOREVER, g_blemesh_stack, MYNEWT_VAL(BLE_MESH_ADV_STACK_SIZE)); #endif /* For BT5 controllers we can have fast advertising interval */ if (ble_hs_hci_get_hci_version() >= BLE_HCI_VER_BCS_5_0) { adv_int_min = ADV_INT_FAST_MS; } adv_initialized = true; } int bt_mesh_adv_enable(void) { /* Dummy function - in legacy adv thread is started on init*/ return 0; } int bt_mesh_adv_start(const struct ble_gap_adv_params *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); } #endif