zephyr/subsys/bluetooth/mesh/adv_legacy.c
Trond Einar Snekvik 81bf99145a Bluetooth: Mesh: Extended advertising support
Adds support for extended advertiser commands in the mesh. This doubles
throughput for common packet sending, and significantly improves timing
accuracy for the Friend and Low Power features.

The proxy module's advertisement control has been moved inside the adv
module to abstract away the different advertiser modes.

The extended advertiser mode does not need an advertising thread to
operate, and ends up with a net reduction in RAM usage.

Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
2020-12-09 12:57:59 +02:00

182 lines
4.2 KiB
C

/* Bluetooth Mesh */
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <debug/stack.h>
#include <sys/util.h>
#include <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/conn.h>
#include <bluetooth/mesh.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_ADV)
#define LOG_MODULE_NAME bt_mesh_adv_legacy
#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"
/* 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 inline void adv_send(struct net_buf *buf)
{
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 = (BT_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 = bt_mesh_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 = BT_MESH_ADV_SCAN_UNIT(adv_int);
param.interval_max = param.interval_min;
uint64_t time = k_uptime_get();
err = bt_le_adv_start(&param, &ad, 1, NULL, 0);
net_buf_unref(buf);
bt_mesh_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();
bt_mesh_adv_send_end(err, cb, cb_data);
if (err) {
BT_ERR("Stopping advertising failed: err %d", err);
return;
}
BT_DBG("Advertising stopped (%u ms)", (uint32_t) k_uptime_delta(&time));
}
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(&bt_mesh_adv_queue, K_NO_WAIT);
while (!buf) {
/* Adv timeout may be set by a call from proxy
* to bt_mesh_adv_start:
*/
adv_timeout = SYS_FOREVER_MS;
bt_mesh_proxy_adv_start();
BT_DBG("Proxy Advertising");
buf = net_buf_get(&bt_mesh_adv_queue,
SYS_TIMEOUT_MS(adv_timeout));
bt_le_adv_stop();
}
} else {
buf = net_buf_get(&bt_mesh_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(&bt_mesh_adv_queue);
}
void bt_mesh_adv_buf_ready(void)
{
/* Will be handled automatically */
}
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(7), 0, K_FOREVER);
k_thread_name_set(&adv_thread_data, "BT Mesh adv");
}
int bt_mesh_adv_enable(void)
{
k_thread_start(&adv_thread_data);
return 0;
}
int bt_mesh_adv_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);
}