zephyr/subsys/bluetooth/mesh/adv_ext.c
Ingar Kulbrandstad 3ec6411c7f Bluetooth: Mesh: Align capitalization for BT mesh
Align the capitalization of the term "Bluetooth Mesh" to Bluetooth mesh"
in the documentation. This is done to to match the new updated naming
convention done in Bluetooth SIG. In the upcoming spec versions, it its
used "Bluetooth mesh" with the lower case convention.

Signed-off-by: Ingar Kulbrandstad <ingar.kulbrandstad@nordicsemi.no>
2021-07-13 11:23:54 -04:00

282 lines
6.7 KiB
C

/*
* 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 <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/mesh.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_ADV)
#define LOG_MODULE_NAME bt_mesh_adv_ext
#include "common/log.h"
#include "host/hci_core.h"
#include "adv.h"
#include "net.h"
#include "proxy.h"
/* Convert from ms to 0.625ms units */
#define ADV_INT_FAST_MS 20
static struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
.interval_min = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS),
.interval_max = BT_MESH_ADV_SCAN_UNIT(ADV_INT_FAST_MS),
#if defined(CONFIG_BT_MESH_DEBUG_USE_ID_ADDR)
.options = BT_LE_ADV_OPT_USE_IDENTITY,
#endif
};
enum {
/** Controller is currently advertising */
ADV_FLAG_ACTIVE,
/** Currently performing proxy advertising */
ADV_FLAG_PROXY,
/** The send-call has been scheduled. */
ADV_FLAG_SCHEDULED,
/** Custom adv params have been set, we need to update the parameters on
* the next send.
*/
ADV_FLAG_UPDATE_PARAMS,
/* Number of adv flags. */
ADV_FLAGS_NUM
};
static struct {
ATOMIC_DEFINE(flags, ADV_FLAGS_NUM);
struct bt_le_ext_adv *instance;
struct net_buf *buf;
uint64_t timestamp;
struct k_work_delayable work;
} adv;
static int adv_start(const struct bt_le_adv_param *param,
struct bt_le_ext_adv_start_param *start,
const struct bt_data *ad, size_t ad_len,
const struct bt_data *sd, size_t sd_len)
{
int err;
if (!adv.instance) {
BT_ERR("Mesh advertiser not enabled");
return -ENODEV;
}
if (atomic_test_and_set_bit(adv.flags, ADV_FLAG_ACTIVE)) {
BT_ERR("Advertiser is busy");
return -EBUSY;
}
if (atomic_test_bit(adv.flags, ADV_FLAG_UPDATE_PARAMS)) {
err = bt_le_ext_adv_update_param(adv.instance, param);
if (err) {
BT_ERR("Failed updating adv params: %d", err);
atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
return err;
}
atomic_set_bit_to(adv.flags, ADV_FLAG_UPDATE_PARAMS,
param != &adv_param);
}
err = bt_le_ext_adv_set_data(adv.instance, ad, ad_len, sd, sd_len);
if (err) {
BT_ERR("Failed setting adv data: %d", err);
atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
return err;
}
adv.timestamp = k_uptime_get();
err = bt_le_ext_adv_start(adv.instance, start);
if (err) {
BT_ERR("Advertising failed: err %d", err);
atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
}
return err;
}
static int buf_send(struct net_buf *buf)
{
struct bt_le_ext_adv_start_param start = {
.num_events =
BT_MESH_TRANSMIT_COUNT(BT_MESH_ADV(buf)->xmit) + 1,
};
uint16_t duration, adv_int;
struct bt_data ad;
int err;
adv_int = MAX(ADV_INT_FAST_MS,
BT_MESH_TRANSMIT_INT(BT_MESH_ADV(buf)->xmit));
/* Upper boundary estimate: */
duration = start.num_events * (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;
/* Only update advertising parameters if they're different */
if (adv_param.interval_min != BT_MESH_ADV_SCAN_UNIT(adv_int)) {
adv_param.interval_min = BT_MESH_ADV_SCAN_UNIT(adv_int);
adv_param.interval_max = adv_param.interval_min;
atomic_set_bit(adv.flags, ADV_FLAG_UPDATE_PARAMS);
}
err = adv_start(&adv_param, &start, &ad, 1, NULL, 0);
if (!err) {
adv.buf = net_buf_ref(buf);
}
bt_mesh_adv_send_start(duration, err, BT_MESH_ADV(buf));
return err;
}
static void send_pending_adv(struct k_work *work)
{
struct net_buf *buf;
int err;
atomic_clear_bit(adv.flags, ADV_FLAG_SCHEDULED);
while ((buf = net_buf_get(&bt_mesh_adv_queue, K_NO_WAIT))) {
/* busy == 0 means this was canceled */
if (!BT_MESH_ADV(buf)->busy) {
net_buf_unref(buf);
continue;
}
BT_MESH_ADV(buf)->busy = 0U;
err = buf_send(buf);
net_buf_unref(buf);
if (!err) {
return; /* Wait for advertising to finish */
}
}
/* No more pending buffers */
if (IS_ENABLED(CONFIG_BT_MESH_GATT_SERVER)) {
BT_DBG("Proxy Advertising");
err = bt_mesh_proxy_adv_start();
if (!err) {
atomic_set_bit(adv.flags, ADV_FLAG_PROXY);
}
}
}
static void schedule_send(void)
{
uint64_t timestamp = adv.timestamp;
int64_t delta;
if (atomic_test_and_clear_bit(adv.flags, ADV_FLAG_PROXY)) {
bt_le_ext_adv_stop(adv.instance);
atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
}
if (atomic_test_bit(adv.flags, ADV_FLAG_ACTIVE) ||
atomic_test_and_set_bit(adv.flags, ADV_FLAG_SCHEDULED)) {
return;
}
/* The controller will send the next advertisement immediately.
* Introduce a delay here to avoid sending the next mesh packet closer
* to the previous packet than what's permitted by the specification.
*/
delta = k_uptime_delta(&timestamp);
k_work_reschedule(&adv.work, K_MSEC(ADV_INT_FAST_MS - delta));
}
void bt_mesh_adv_update(void)
{
BT_DBG("");
schedule_send();
}
void bt_mesh_adv_buf_ready(void)
{
schedule_send();
}
void bt_mesh_adv_init(void)
{
k_work_init_delayable(&adv.work, send_pending_adv);
}
static void adv_sent(struct bt_le_ext_adv *instance,
struct bt_le_ext_adv_sent_info *info)
{
/* Calling k_uptime_delta on a timestamp moves it to the current time.
* This is essential here, as schedule_send() uses the end of the event
* as a reference to avoid sending the next advertisement too soon.
*/
int64_t duration = k_uptime_delta(&adv.timestamp);
BT_DBG("Advertising stopped after %u ms", (uint32_t)duration);
atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
if (!atomic_test_and_clear_bit(adv.flags, ADV_FLAG_PROXY)) {
net_buf_unref(adv.buf);
}
schedule_send();
}
static void connected(struct bt_le_ext_adv *instance,
struct bt_le_ext_adv_connected_info *info)
{
if (atomic_test_and_clear_bit(adv.flags, ADV_FLAG_PROXY)) {
atomic_clear_bit(adv.flags, ADV_FLAG_ACTIVE);
schedule_send();
}
}
int bt_mesh_adv_enable(void)
{
static const struct bt_le_ext_adv_cb adv_cb = {
.sent = adv_sent,
.connected = connected,
};
if (adv.instance) {
/* Already initialized */
return 0;
}
return bt_le_ext_adv_create(&adv_param, &adv_cb, &adv.instance);
}
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)
{
struct bt_le_ext_adv_start_param start = {
/* Timeout is set in 10 ms steps, with 0 indicating "forever" */
.timeout = (duration == SYS_FOREVER_MS) ? 0 : (duration / 10),
};
BT_DBG("Start advertising %d ms", duration);
atomic_set_bit(adv.flags, ADV_FLAG_UPDATE_PARAMS);
return adv_start(param, &start, ad, ad_len, sd, sd_len);
}