diff options
Diffstat (limited to 'src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c')
-rw-r--r-- | src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c new file mode 100644 index 00000000..4bd51cc1 --- /dev/null +++ b/src/libs/mynewt-nimble/nimble/host/mesh/src/adv.c @@ -0,0 +1,439 @@ +/* Bluetooth Mesh */ + +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "syscfg/syscfg.h" +#define MESH_LOG_MODULE BLE_MESH_ADV_LOG + +#include "mesh/mesh.h" +#include "host/ble_hs_adv.h" +#include "host/ble_gap.h" +#include "nimble/hci_common.h" +#include "mesh/porting.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 + +static s32_t adv_int_min = ADV_INT_DEFAULT_MS; + +/* TinyCrypt PRNG consumes a lot of stack space, so we need to have + * an increased call stack whenever it's used. + */ +#if MYNEWT +#define ADV_STACK_SIZE 768 +OS_TASK_STACK_DEFINE(g_blemesh_stack, ADV_STACK_SIZE); +struct os_task adv_task; +#endif + +static struct ble_npl_eventq adv_queue; +extern u8_t g_mesh_addr_type; +static int adv_initialized = false; + +static os_membuf_t adv_buf_mem[OS_MEMPOOL_SIZE( + MYNEWT_VAL(BLE_MESH_ADV_BUF_COUNT), + BT_MESH_ADV_DATA_SIZE + BT_MESH_MBUF_HEADER_SIZE)]; + +struct os_mbuf_pool adv_os_mbuf_pool; +static struct os_mempool adv_buf_mempool; + +static const u8_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, +}; + + +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(u16_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 os_mbuf *buf) +{ + const struct bt_mesh_send_cb *cb = BT_MESH_ADV(buf)->cb; + void *cb_data = BT_MESH_ADV(buf)->cb_data; + struct ble_gap_adv_params param = { 0 }; + u16_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 + duration = (MESH_SCAN_WINDOW_MS + + ((BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1) * + (adv_int + 10))); +#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; + + 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(false); + adv_send_end(err, cb, cb_data); + if (err) { + BT_ERR("Stopping advertising failed: err %d", err); + return; + } + + BT_DBG("Advertising stopped"); +} + +void +mesh_adv_thread(void *args) +{ + static struct ble_npl_event *ev; + struct os_mbuf *buf; +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + s32_t timeout; +#endif + + BT_DBG("started"); + + while (1) { +#if (MYNEWT_VAL(BLE_MESH_PROXY)) + ev = ble_npl_eventq_get(&adv_queue, 0); + while (!ev) { + timeout = bt_mesh_proxy_adv_start(); + BT_DBG("Proxy Advertising up to %d ms", (int) timeout); + + // FIXME: should we redefine K_SECONDS macro instead in glue? + if (timeout != K_FOREVER) { + timeout = ble_npl_time_ms_to_ticks32(timeout); + } + + ev = ble_npl_eventq_get(&adv_queue, timeout); + bt_mesh_proxy_adv_stop(); + } +#else + ev = ble_npl_eventq_get(&adv_queue, BLE_NPL_TIME_FOREVER); +#endif + + 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); + } else { + 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(&adv_queue, &ev); +} + +struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool, + bt_mesh_adv_alloc_t get_id, + enum bt_mesh_adv_type type, + u8_t xmit, s32_t timeout) +{ + struct bt_mesh_adv *adv; + struct os_mbuf *buf; + + if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + BT_WARN("Refusing to allocate buffer while suspended"); + return NULL; + } + + buf = os_mbuf_get_pkthdr(pool, BT_MESH_ADV_USER_DATA_SIZE); + if (!buf) { + return NULL; + } + + adv = get_id(net_buf_id(buf)); + BT_MESH_ADV(buf) = adv; + + memset(adv, 0, sizeof(*adv)); + + adv->type = type; + adv->xmit = xmit; + + adv->ref_cnt = 1; + ble_npl_event_set_arg(&adv->ev, buf); + + return buf; +} + +struct os_mbuf *bt_mesh_adv_create(enum bt_mesh_adv_type type, u8_t xmit, + s32_t timeout) +{ + return bt_mesh_adv_create_from_pool(&adv_os_mbuf_pool, adv_alloc, type, + xmit, timeout); +} + +void bt_mesh_adv_send(struct os_mbuf *buf, const struct bt_mesh_send_cb *cb, + void *cb_data) +{ + BT_DBG("buf %p, type 0x%02x len %u: %s", buf, BT_MESH_ADV(buf)->type, buf->om_len, + bt_hex(buf->om_data, buf->om_len)); + + BT_MESH_ADV(buf)->cb = cb; + BT_MESH_ADV(buf)->cb_data = cb_data; + BT_MESH_ADV(buf)->busy = 1; + + net_buf_put(&adv_queue, net_buf_ref(buf)); +} + +static void bt_mesh_scan_cb(const bt_addr_le_t *addr, s8_t rssi, + u8_t adv_type, struct os_mbuf *buf) +{ + if (adv_type != BLE_HCI_ADV_TYPE_ADV_NONCONN_IND) { + return; + } + +#if BT_MESH_EXTENDED_DEBUG + BT_DBG("len %u: %s", buf->om_len, bt_hex(buf->om_data, buf->om_len)); +#endif + + while (buf->om_len > 1) { + struct net_buf_simple_state state; + u8_t len, type; + + len = net_buf_simple_pull_u8(buf); + /* Check for early termination */ + if (len == 0) { + return; + } + + if (len > buf->om_len) { + BT_WARN("AD malformed"); + return; + } + + net_buf_simple_save(buf, &state); + + type = net_buf_simple_pull_u8(buf); + + switch (type) { + case BLE_HS_ADV_TYPE_MESH_MESSAGE: + bt_mesh_net_recv(buf, rssi, BT_MESH_NET_IF_ADV); + break; +#if MYNEWT_VAL(BLE_MESH_PB_ADV) + case BLE_HS_ADV_TYPE_MESH_PROV: + bt_mesh_pb_adv_recv(buf); + break; +#endif + case BLE_HS_ADV_TYPE_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) +{ + 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(&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, 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 +ble_adv_gap_mesh_cb(struct ble_gap_event *event, void *arg) +{ +#if MYNEWT_VAL(BLE_EXT_ADV) + struct ble_gap_ext_disc_desc *ext_desc; +#endif + struct ble_gap_disc_desc *desc; + struct os_mbuf *buf = NULL; + +#if BT_MESH_EXTENDED_DEBUG + BT_DBG("event->type %d", event->type); +#endif + + switch (event->type) { +#if MYNEWT_VAL(BLE_EXT_ADV) + case BLE_GAP_EVENT_EXT_DISC: + ext_desc = &event->ext_disc; + buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0); + if (!buf || os_mbuf_append(buf, ext_desc->data, ext_desc->length_data)) { + BT_ERR("Could not append data"); + goto done; + } + bt_mesh_scan_cb(&ext_desc->addr, ext_desc->rssi, + ext_desc->legacy_event_type, buf); + break; +#endif + case BLE_GAP_EVENT_DISC: + desc = &event->disc; + buf = os_mbuf_get_pkthdr(&adv_os_mbuf_pool, 0); + if (!buf || os_mbuf_append(buf, desc->data, desc->length_data)) { + BT_ERR("Could not append data"); + goto done; + } + + bt_mesh_scan_cb(&desc->addr, desc->rssi, desc->event_type, buf); + break; + default: + break; + } + +done: + if (buf) { + os_mbuf_free_chain(buf); + } + + return 0; +} + +int bt_mesh_scan_enable(void) +{ + int err; + +#if MYNEWT_VAL(BLE_EXT_ADV) + struct ble_gap_ext_disc_params uncoded_params = + { .itvl = MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW, + .passive = 1 }; + + BT_DBG(""); + + err = ble_gap_ext_disc(g_mesh_addr_type, 0, 0, 0, 0, 0, + &uncoded_params, NULL, NULL, NULL); +#else + struct ble_gap_disc_params scan_param = + { .passive = 1, .filter_duplicates = 0, .itvl = + MESH_SCAN_INTERVAL, .window = MESH_SCAN_WINDOW }; + + BT_DBG(""); + + err = ble_gap_disc(g_mesh_addr_type, BLE_HS_FOREVER, &scan_param, + NULL, NULL); +#endif + if (err && err != BLE_HS_EALREADY) { + BT_ERR("starting scan failed (err %d)", err); + return err; + } + + return 0; +} + +int bt_mesh_scan_disable(void) +{ + int err; + + BT_DBG(""); + + err = ble_gap_disc_cancel(); + if (err && err != BLE_HS_EALREADY) { + BT_ERR("stopping scan failed (err %d)", err); + return err; + } + + return 0; +} |