Bluetooth: Mesh: Move heartbeat to separate module

Encapsulates the Heartbeat state and functionality in a separate
heartbeat module, removing all manipulation of the heartbeat state from
the transport and config server modules.

Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2020-08-12 17:07:38 +02:00 committed by Johan Hedberg
commit 0dc9e5cd96
14 changed files with 659 additions and 451 deletions

View file

@ -21,6 +21,7 @@
#include <bluetooth/mesh/cfg_cli.h>
#include <bluetooth/mesh/health_cli.h>
#include <bluetooth/mesh/proxy.h>
#include <bluetooth/mesh/heartbeat.h>
#include <bluetooth/mesh/cdb.h>
#include <bluetooth/mesh/cfg.h>

View file

@ -34,8 +34,11 @@ struct bt_mesh_cfg_srv {
uint8_t frnd; /**< Friend state */
uint8_t default_ttl; /**< Default TTL */
/** Heartbeat Publication parameters */
struct bt_mesh_hb_pub {
/** Heartbeat Publication parameters.
*
* @deprecated in favor of standalone API in bluetooth/mesh/heartbeat.h.
*/
struct {
struct k_delayed_work timer;
/** Destination address. */
@ -55,10 +58,13 @@ struct bt_mesh_cfg_srv {
uint16_t feat;
/** Network index used for publishing. */
uint16_t net_idx;
} hb_pub;
} hb_pub __deprecated;
/** Heartbeat Subscription parameters. */
struct bt_mesh_hb_sub {
/** Heartbeat Subscription parameters.
*
* @deprecated in favor of standalone API in bluetooth/mesh/heartbeat.h.
*/
struct {
/** Subscription period exipration timestamp. */
int64_t expiry;
/** Source address to receive Heartbeats from. */
@ -93,9 +99,12 @@ struct bt_mesh_cfg_srv {
* @ref BT_MESH_FEAT_PROXY,
* @ref BT_MESH_FEAT_FRIEND and
* @ref BT_MESH_FEAT_LOW_POWER.
*
* @deprecated Please use the @ref bt_mesh_hb_cb registration
* function.
*/
void (*func)(uint8_t hops, uint16_t feat);
} hb_sub;
} hb_sub __deprecated;
};
/** @def BT_MESH_MODEL_CFG_SRV

View file

@ -0,0 +1,135 @@
/** @file
* @brief Bluetooth Mesh Heartbeat API.
*/
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_HEARTBEAT_H_
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_HEARTBEAT_H_
#include <sys/slist.h>
/**
* @brief Bluetooth Mesh
* @defgroup bt_mesh_heartbeat Bluetooth Mesh Heartbeat
* @ingroup bt_mesh
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/** Heartbeat Publication parameters */
struct bt_mesh_hb_pub {
/** Destination address. */
uint16_t dst;
/** Remaining publish count. */
uint16_t count;
/** Time To Live value. */
uint8_t ttl;
/**
* Bitmap of features that trigger a Heartbeat publication if
* they change. Legal values are @ref BT_MESH_FEAT_RELAY,
* @ref BT_MESH_FEAT_PROXY, @ref BT_MESH_FEAT_FRIEND and
* @ref BT_MESH_FEAT_LOW_POWER.
*/
uint16_t feat;
/** Network index used for publishing. */
uint16_t net_idx;
/** Publication period in seconds. */
uint32_t period;
};
/** Heartbeat Subscription parameters. */
struct bt_mesh_hb_sub {
/** Subscription period in seconds. */
uint32_t period;
/** Remaining subscription time in seconds. */
uint32_t remaining;
/** Source address to receive Heartbeats from. */
uint16_t src;
/** Destination address to received Heartbeats on. */
uint16_t dst;
/** The number of received Heartbeat messages so far. */
uint16_t count;
/**
* Minimum hops in received messages, ie the shortest registered
* path from the publishing node to the subscribing node. A
* Heartbeat received from an immediate neighbor has hop
* count = 1.
*/
uint8_t min_hops;
/**
* Maximum hops in received messages, ie the longest registered
* path from the publishing node to the subscribing node. A
* Heartbeat received from an immediate neighbor has hop
* count = 1.
*/
uint8_t max_hops;
};
/** Heartbeat callback structure */
struct bt_mesh_hb_cb {
/** @brief Receive callback for heartbeats.
*
* Gets called on every received Heartbeat that matches the current
* Heartbeat subscription parameters.
*
* @param sub Current Heartbeat subscription parameters.
* @param hops The number of hops the Heartbeat was received
* with.
* @param feat The feature set of the publishing node. The
* value is a bitmap of @ref BT_MESH_FEAT_RELAY,
* @ref BT_MESH_FEAT_PROXY,
* @ref BT_MESH_FEAT_FRIEND and
* @ref BT_MESH_FEAT_LOW_POWER.
*/
void (*recv)(const struct bt_mesh_hb_sub *sub, uint8_t hops,
uint16_t feat);
/** @brief Subscription end callback for heartbeats.
*
* Gets called when the subscription period ends, providing a summary
* of the received heartbeat messages.
*
* @param sub Current Heartbeat subscription parameters.
*/
void (*sub_end)(const struct bt_mesh_hb_sub *sub);
};
/** @def BT_MESH_HB_CB_DEFINE
*
* @brief Register a callback structure for Heartbeat events.
*
* Registers a callback structure that will be called whenever Heartbeat
* events occur
*
* @param _name Name of callback structure.
*/
#define BT_MESH_HB_CB_DEFINE(_name) \
static const Z_STRUCT_SECTION_ITERABLE(bt_mesh_hb_cb, _name)
/** @brief Get the current Heartbeat publication parameters.
*
* @param get Heartbeat publication parameters return buffer.
*/
void bt_mesh_hb_pub_get(struct bt_mesh_hb_pub *get);
/** @brief Get the current Heartbeat subscription parameters.
*
* @param get Heartbeat subscription parameters return buffer.
*/
void bt_mesh_hb_sub_get(struct bt_mesh_hb_sub *get);
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_HEARTBEAT_H_ */

View file

@ -105,6 +105,8 @@
#if defined(CONFIG_BT_MESH)
Z_ITERABLE_SECTION_ROM(bt_mesh_subnet_cb, 4)
Z_ITERABLE_SECTION_ROM(bt_mesh_app_key_cb, 4)
Z_ITERABLE_SECTION_ROM(bt_mesh_hb_cb, 4)
#endif
#if defined(CONFIG_BT_MESH_FRIEND)

View file

@ -12,6 +12,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH
app_keys.c
transport.c
rpl.c
heartbeat.c
crypto.c
access.c
cfg_srv.c

View file

@ -30,6 +30,7 @@
#include "rpl.h"
#include "lpn.h"
#include "transport.h"
#include "heartbeat.h"
#include "crypto.h"
#include "access.h"
#include "beacon.h"
@ -620,9 +621,7 @@ static void gatt_proxy_set(struct bt_mesh_model *model,
bt_mesh_store_cfg();
}
if (cfg->hb_pub.feat & BT_MESH_FEAT_PROXY) {
(void)bt_mesh_heartbeat_send(NULL, NULL);
}
bt_mesh_hb_feature_changed(BT_MESH_FEAT_PROXY);
send_status:
send_gatt_proxy_status(model, ctx);
@ -726,9 +725,7 @@ static void relay_set(struct bt_mesh_model *model,
BT_MESH_TRANSMIT_COUNT(cfg->relay_retransmit),
BT_MESH_TRANSMIT_INT(cfg->relay_retransmit));
if ((cfg->hb_pub.feat & BT_MESH_FEAT_RELAY) && change) {
(void)bt_mesh_heartbeat_send(NULL, NULL);
}
bt_mesh_hb_feature_changed(BT_MESH_FEAT_RELAY);
} else {
BT_WARN("Invalid Relay value 0x%02x", buf->data[0]);
return;
@ -1699,18 +1696,6 @@ static void net_key_update(struct bt_mesh_model *model,
send_net_key_status(model, ctx, idx, status);
}
static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg)
{
BT_DBG("");
cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED;
cfg->hb_pub.count = 0U;
cfg->hb_pub.ttl = 0U;
cfg->hb_pub.period = 0U;
k_delayed_work_cancel(&cfg->hb_pub.timer);
}
static void net_key_del(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
@ -2139,9 +2124,7 @@ static void friend_set(struct bt_mesh_model *model,
}
}
if (cfg->hb_pub.feat & BT_MESH_FEAT_FRIEND) {
(void)bt_mesh_heartbeat_send(NULL, NULL);
}
bt_mesh_hb_feature_changed(BT_MESH_FEAT_FRIEND);
send_status:
send_friend_status(model, ctx);
@ -2249,17 +2232,6 @@ static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
send_krp_status(model, ctx, idx, phase, status);
}
static uint8_t hb_log(uint16_t val)
{
if (!val) {
return 0x00;
} else if (val == 0xffff) {
return 0xff;
} else {
return 32 - __builtin_clz(val);
}
}
static uint8_t hb_pub_count_log(uint16_t val)
{
if (!val) {
@ -2273,17 +2245,6 @@ static uint8_t hb_pub_count_log(uint16_t val)
}
}
static uint16_t hb_pwr2(uint8_t val, uint8_t sub)
{
if (!val) {
return 0x0000;
} else if (val == 0xff || val == 0x11) {
return 0xffff;
} else {
return (1 << (val - sub));
}
}
struct hb_pub_param {
uint16_t dst;
uint8_t count_log;
@ -2295,10 +2256,9 @@ struct hb_pub_param {
static void hb_pub_send_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint8_t status,
struct hb_pub_param *orig_msg)
const struct bt_mesh_hb_pub *pub)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_PUB_STATUS, 10);
struct bt_mesh_cfg_srv *cfg = model->user_data;
BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status);
@ -2306,20 +2266,13 @@ static void hb_pub_send_status(struct bt_mesh_model *model,
net_buf_simple_add_u8(&msg, status);
if (orig_msg) {
memcpy(net_buf_simple_add(&msg, sizeof(*orig_msg)), orig_msg,
sizeof(*orig_msg));
goto send;
}
net_buf_simple_add_le16(&msg, pub->dst);
net_buf_simple_add_u8(&msg, hb_pub_count_log(pub->count));
net_buf_simple_add_u8(&msg, bt_mesh_hb_log(pub->period));
net_buf_simple_add_u8(&msg, pub->ttl);
net_buf_simple_add_le16(&msg, pub->feat);
net_buf_simple_add_le16(&msg, pub->net_idx);
net_buf_simple_add_le16(&msg, cfg->hb_pub.dst);
net_buf_simple_add_u8(&msg, hb_pub_count_log(cfg->hb_pub.count));
net_buf_simple_add_u8(&msg, cfg->hb_pub.period);
net_buf_simple_add_u8(&msg, cfg->hb_pub.ttl);
net_buf_simple_add_le16(&msg, cfg->hb_pub.feat);
net_buf_simple_add_le16(&msg, cfg->hb_pub.net_idx);
send:
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send Heartbeat Publication Status");
}
@ -2329,9 +2282,13 @@ static void heartbeat_pub_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_hb_pub pub;
BT_DBG("src 0x%04x", ctx->addr);
hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL);
bt_mesh_hb_pub_get(&pub);
hb_pub_send_status(model, ctx, STATUS_SUCCESS, &pub);
}
static void heartbeat_pub_set(struct bt_mesh_model *model,
@ -2339,27 +2296,32 @@ static void heartbeat_pub_set(struct bt_mesh_model *model,
struct net_buf_simple *buf)
{
struct hb_pub_param *param = (void *)buf->data;
struct bt_mesh_cfg_srv *cfg = model->user_data;
uint16_t dst, feat, idx;
struct bt_mesh_hb_pub pub;
uint8_t status;
BT_DBG("src 0x%04x", ctx->addr);
dst = sys_le16_to_cpu(param->dst);
pub.dst = sys_le16_to_cpu(param->dst);
pub.count = bt_mesh_hb_pwr2(param->count_log);
pub.period = bt_mesh_hb_pwr2(param->period_log);
pub.ttl = param->ttl;
pub.feat = sys_le16_to_cpu(param->feat);
pub.net_idx = sys_le16_to_cpu(param->net_idx);
/* All other address types but virtual are valid */
if (BT_MESH_ADDR_IS_VIRTUAL(dst)) {
if (BT_MESH_ADDR_IS_VIRTUAL(pub.dst)) {
status = STATUS_INVALID_ADDRESS;
goto failed;
goto rsp;
}
if (param->count_log > 0x11 && param->count_log != 0xff) {
status = STATUS_CANNOT_SET;
goto failed;
goto rsp;
}
if (param->period_log > 0x10) {
status = STATUS_CANNOT_SET;
goto failed;
goto rsp;
}
if (param->ttl > BT_MESH_TTL_MAX && param->ttl != BT_MESH_TTL_DEFAULT) {
@ -2367,82 +2329,34 @@ static void heartbeat_pub_set(struct bt_mesh_model *model,
return;
}
feat = sys_le16_to_cpu(param->feat);
idx = sys_le16_to_cpu(param->net_idx);
if (idx > 0xfff) {
BT_ERR("Invalid NetKeyIndex 0x%04x", idx);
if (pub.net_idx > 0xfff) {
BT_ERR("Invalid NetKeyIndex 0x%04x", pub.net_idx);
return;
}
if (!bt_mesh_subnet_get(idx)) {
status = STATUS_INVALID_NETKEY;
goto failed;
}
status = bt_mesh_hb_pub_set(&pub);
cfg->hb_pub.dst = dst;
cfg->hb_pub.period = param->period_log;
cfg->hb_pub.feat = feat & BT_MESH_FEAT_SUPPORTED;
cfg->hb_pub.net_idx = idx;
if (dst == BT_MESH_ADDR_UNASSIGNED) {
hb_pub_disable(cfg);
} else {
/* 2^(n-1) */
cfg->hb_pub.count = hb_pwr2(param->count_log, 1);
cfg->hb_pub.ttl = param->ttl;
BT_DBG("period %u ms", hb_pwr2(param->period_log, 1) * 1000U);
/* The first Heartbeat message shall be published as soon
* as possible after the Heartbeat Publication Period state
* has been configured for periodic publishing.
*/
if (param->period_log && param->count_log) {
k_work_submit(&cfg->hb_pub.timer.work);
} else {
k_delayed_work_cancel(&cfg->hb_pub.timer);
}
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_hb_pub();
}
hb_pub_send_status(model, ctx, STATUS_SUCCESS, NULL);
return;
failed:
hb_pub_send_status(model, ctx, status, param);
rsp:
hb_pub_send_status(model, ctx, status, &pub);
}
static void hb_sub_send_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx, uint8_t status)
struct bt_mesh_msg_ctx *ctx,
const struct bt_mesh_hb_sub *sub)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_HEARTBEAT_SUB_STATUS, 9);
struct bt_mesh_cfg_srv *cfg = model->user_data;
uint16_t period;
int64_t uptime;
BT_DBG("src 0x%04x status 0x%02x", ctx->addr, status);
uptime = k_uptime_get();
if (uptime > cfg->hb_sub.expiry) {
period = 0U;
} else {
period = (cfg->hb_sub.expiry - uptime) / 1000;
}
BT_DBG("src 0x%04x ", ctx->addr);
bt_mesh_model_msg_init(&msg, OP_HEARTBEAT_SUB_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, cfg->hb_sub.src);
net_buf_simple_add_le16(&msg, cfg->hb_sub.dst);
net_buf_simple_add_u8(&msg, hb_log(period));
net_buf_simple_add_u8(&msg, hb_log(cfg->hb_sub.count));
net_buf_simple_add_u8(&msg, cfg->hb_sub.min_hops);
net_buf_simple_add_u8(&msg, cfg->hb_sub.max_hops);
net_buf_simple_add_u8(&msg, STATUS_SUCCESS);
net_buf_simple_add_le16(&msg, sub->src);
net_buf_simple_add_le16(&msg, sub->dst);
net_buf_simple_add_u8(&msg, bt_mesh_hb_log(sub->remaining));
net_buf_simple_add_u8(&msg, bt_mesh_hb_log(sub->count));
net_buf_simple_add_u8(&msg, sub->min_hops);
net_buf_simple_add_u8(&msg, sub->max_hops);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send Heartbeat Subscription Status");
@ -2453,92 +2367,58 @@ static void heartbeat_sub_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_hb_sub sub;
BT_DBG("src 0x%04x", ctx->addr);
hb_sub_send_status(model, ctx, STATUS_SUCCESS);
bt_mesh_hb_sub_get(&sub);
hb_sub_send_status(model, ctx, &sub);
}
static void heartbeat_sub_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_cfg_srv *cfg = model->user_data;
uint8_t period_log, status;
struct bt_mesh_hb_sub sub;
uint16_t sub_src, sub_dst;
uint8_t sub_period;
int32_t period_ms;
uint32_t period;
BT_DBG("src 0x%04x", ctx->addr);
sub_src = net_buf_simple_pull_le16(buf);
sub_dst = net_buf_simple_pull_le16(buf);
sub_period = net_buf_simple_pull_u8(buf);
period_log = net_buf_simple_pull_u8(buf);
BT_DBG("sub_src 0x%04x sub_dst 0x%04x period 0x%02x",
sub_src, sub_dst, sub_period);
sub_src, sub_dst, period_log);
if (sub_src != BT_MESH_ADDR_UNASSIGNED &&
!BT_MESH_ADDR_IS_UNICAST(sub_src)) {
BT_WARN("Prohibited source address");
if (period_log > 0x11) {
BT_WARN("Prohibited subscription period 0x%02x", period_log);
return;
}
if (BT_MESH_ADDR_IS_VIRTUAL(sub_dst) || BT_MESH_ADDR_IS_RFU(sub_dst) ||
(BT_MESH_ADDR_IS_UNICAST(sub_dst) &&
sub_dst != bt_mesh_primary_addr())) {
BT_WARN("Prohibited destination address");
return;
}
period = bt_mesh_hb_pwr2(period_log);
if (sub_period > 0x11) {
BT_WARN("Prohibited subscription period 0x%02x", sub_period);
return;
}
if (sub_src == BT_MESH_ADDR_UNASSIGNED ||
sub_dst == BT_MESH_ADDR_UNASSIGNED ||
sub_period == 0x00) {
/* Only an explicit address change to unassigned should
* trigger clearing of the values according to
* MESH/NODE/CFG/HBS/BV-02-C.
status = bt_mesh_hb_sub_set(sub_src, sub_dst, period);
if (status != STATUS_SUCCESS) {
/* All errors are caused by invalid packets, which should be
* ignored.
*/
if (sub_src == BT_MESH_ADDR_UNASSIGNED ||
sub_dst == BT_MESH_ADDR_UNASSIGNED) {
cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED;
cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED;
cfg->hb_sub.min_hops = BT_MESH_TTL_MAX;
cfg->hb_sub.max_hops = 0U;
cfg->hb_sub.count = 0U;
}
period_ms = 0;
} else {
cfg->hb_sub.src = sub_src;
cfg->hb_sub.dst = sub_dst;
cfg->hb_sub.min_hops = BT_MESH_TTL_MAX;
cfg->hb_sub.max_hops = 0U;
cfg->hb_sub.count = 0U;
period_ms = hb_pwr2(sub_period, 1) * 1000U;
return;
}
/* Let the transport layer know it needs to handle this address */
bt_mesh_set_hb_sub_dst(cfg->hb_sub.dst);
BT_DBG("period_ms %u", period_ms);
if (period_ms) {
cfg->hb_sub.expiry = k_uptime_get() + period_ms;
} else {
cfg->hb_sub.expiry = 0;
}
hb_sub_send_status(model, ctx, STATUS_SUCCESS);
bt_mesh_hb_sub_get(&sub);
/* MESH/NODE/CFG/HBS/BV-01-C expects the MinHops to be 0x7f after
* disabling subscription, but 0x00 for subsequent Get requests.
*/
if (!period_ms) {
cfg->hb_sub.min_hops = 0U;
if (!period_log) {
sub.min_hops = BT_MESH_TTL_MAX;
}
hb_sub_send_status(model, ctx, &sub);
}
const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = {
@ -2592,60 +2472,6 @@ const struct bt_mesh_model_op bt_mesh_cfg_srv_op[] = {
BT_MESH_MODEL_OP_END,
};
static void hb_publish_end_cb(int err, void *cb_data)
{
struct bt_mesh_cfg_srv *cfg = cb_data;
uint16_t period_ms;
period_ms = hb_pwr2(cfg->hb_pub.period, 1) * 1000U;
if (period_ms && cfg->hb_pub.count > 1) {
k_delayed_work_submit(&cfg->hb_pub.timer, K_MSEC(period_ms));
}
if (cfg->hb_pub.count != 0xffff) {
cfg->hb_pub.count--;
}
}
static void hb_publish_start_cb(uint16_t duration, int err, void *cb_data)
{
if (err) {
hb_publish_end_cb(err, cb_data);
}
}
static void hb_publish(struct k_work *work)
{
static const struct bt_mesh_send_cb publish_cb = {
.start = hb_publish_start_cb,
.end = hb_publish_end_cb,
};
struct bt_mesh_cfg_srv *cfg = CONTAINER_OF(work,
struct bt_mesh_cfg_srv,
hb_pub.timer.work);
struct bt_mesh_subnet *sub;
int err;
BT_DBG("hb_pub.count: %u", cfg->hb_pub.count);
sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx);
if (!sub) {
BT_ERR("No matching subnet for idx 0x%02x",
cfg->hb_pub.net_idx);
cfg->hb_pub.dst = BT_MESH_ADDR_UNASSIGNED;
return;
}
if (cfg->hb_pub.count == 0U) {
return;
}
err = bt_mesh_heartbeat_send(&publish_cb, cfg);
if (err) {
hb_publish_end_cb(err, cfg);
}
}
static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg)
{
if (cfg->relay > 0x02) {
@ -2671,6 +2497,14 @@ static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg)
return true;
}
static void (*hb_sub_cb)(uint8_t hops, uint16_t features);
static struct bt_mesh_hb_cb hb_cb;
static void hb_recv_wrapper(const struct bt_mesh_hb_sub *sub, uint8_t hops, uint16_t features)
{
hb_sub_cb(hops, features);
}
static int cfg_srv_init(struct bt_mesh_model *model)
{
struct bt_mesh_cfg_srv *cfg = model->user_data;
@ -2690,6 +2524,11 @@ static int cfg_srv_init(struct bt_mesh_model *model)
return -EINVAL;
}
if (cfg->hb_sub.func) {
hb_sub_cb = cfg->hb_sub.func;
hb_cb.recv = hb_recv_wrapper;
}
/*
* Configuration Model security is device-key based and only the local
* device-key is allowed to access this model.
@ -2708,10 +2547,6 @@ static int cfg_srv_init(struct bt_mesh_model *model)
cfg->gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED;
}
k_delayed_work_init(&cfg->hb_pub.timer, hb_publish);
cfg->hb_pub.net_idx = BT_MESH_KEY_UNUSED;
cfg->hb_sub.expiry = 0;
cfg->model = model;
conf = cfg;
@ -2749,49 +2584,9 @@ static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
void bt_mesh_cfg_reset(void)
{
struct bt_mesh_cfg_srv *cfg = conf;
BT_DBG("");
bt_mesh_set_hb_sub_dst(BT_MESH_ADDR_UNASSIGNED);
cfg->hb_sub.src = BT_MESH_ADDR_UNASSIGNED;
cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED;
cfg->hb_sub.expiry = 0;
bt_mesh_model_foreach(mod_reset, NULL);
}
void bt_mesh_heartbeat(uint16_t src, uint16_t dst, uint8_t hops, uint16_t feat)
{
struct bt_mesh_cfg_srv *cfg = conf;
if (src != cfg->hb_sub.src || dst != cfg->hb_sub.dst) {
BT_WARN("No subscription for received heartbeat");
return;
}
if (k_uptime_get() > cfg->hb_sub.expiry) {
BT_WARN("Heartbeat subscription period expired");
return;
}
cfg->hb_sub.min_hops = MIN(cfg->hb_sub.min_hops, hops);
cfg->hb_sub.max_hops = MAX(cfg->hb_sub.max_hops, hops);
if (cfg->hb_sub.count < 0xffff) {
cfg->hb_sub.count++;
}
BT_DBG("src 0x%04x dst 0x%04x hops %u min %u max %u count %u", src,
dst, hops, cfg->hb_sub.min_hops, cfg->hb_sub.max_hops,
cfg->hb_sub.count);
if (cfg->hb_sub.func) {
cfg->hb_sub.func(hops, feat);
}
}
uint8_t bt_mesh_net_transmit_get(void)
{
if (conf) {
@ -2856,16 +2651,6 @@ uint8_t bt_mesh_default_ttl_get(void)
return DEFAULT_TTL;
}
struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void)
{
return &conf->hb_pub;
}
void bt_mesh_hb_pub_disable(void)
{
hb_pub_disable(conf);
}
struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void)
{
return conf;

View file

@ -125,12 +125,8 @@ struct label {
void bt_mesh_cfg_reset(void);
void bt_mesh_heartbeat(uint16_t src, uint16_t dst, uint8_t hops, uint16_t feat);
void bt_mesh_attention(struct bt_mesh_model *model, uint8_t time);
struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void);
void bt_mesh_hb_pub_disable(void);
struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void);
uint8_t bt_mesh_net_transmit_get(void);

View file

@ -0,0 +1,356 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <bluetooth/mesh.h>
#include "net.h"
#include "rpl.h"
#include "access.h"
#include "lpn.h"
#include "settings.h"
#include "mesh.h"
#include "transport.h"
#include "heartbeat.h"
#include "foundation.h"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_TRANS)
#define LOG_MODULE_NAME bt_mesh_hb
#include "common/log.h"
static struct bt_mesh_hb_pub pub;
static struct bt_mesh_hb_sub sub;
static struct k_delayed_work sub_timer;
static struct k_delayed_work pub_timer;
static int64_t sub_remaining(void)
{
if (sub.dst == BT_MESH_ADDR_UNASSIGNED) {
return 0U;
}
return k_delayed_work_remaining_get(&sub_timer) / MSEC_PER_SEC;
}
static void hb_publish_end_cb(int err, void *cb_data)
{
if (pub.period && pub.count > 1) {
k_delayed_work_submit(&pub_timer, K_SECONDS(pub.period));
}
if (pub.count != 0xffff) {
pub.count--;
}
}
static void notify_recv(uint8_t hops, uint16_t feat)
{
sub.remaining = sub_remaining();
Z_STRUCT_SECTION_FOREACH(bt_mesh_hb_cb, cb) {
if (cb->recv) {
cb->recv(&sub, hops, feat);
}
}
}
static void notify_sub_end(void)
{
sub.remaining = 0;
Z_STRUCT_SECTION_FOREACH(bt_mesh_hb_cb, cb) {
if (cb->sub_end) {
cb->sub_end(&sub);
}
}
}
static void sub_end(struct k_work *work)
{
notify_sub_end();
}
static int heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data)
{
uint16_t feat = 0U;
struct __packed {
uint8_t init_ttl;
uint16_t feat;
} hb;
struct bt_mesh_msg_ctx ctx = {
.net_idx = pub.net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = pub.dst,
.send_ttl = pub.ttl,
};
struct bt_mesh_net_tx tx = {
.sub = bt_mesh_subnet_get(pub.net_idx),
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = bt_mesh_net_transmit_get(),
};
/* Do nothing if heartbeat publication is not enabled */
if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
return 0U;
}
hb.init_ttl = pub.ttl;
if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) {
feat |= BT_MESH_FEAT_RELAY;
}
if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
feat |= BT_MESH_FEAT_PROXY;
}
if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) {
feat |= BT_MESH_FEAT_FRIEND;
}
if (bt_mesh_lpn_established()) {
feat |= BT_MESH_FEAT_LOW_POWER;
}
hb.feat = sys_cpu_to_be16(feat);
BT_DBG("InitTTL %u feat 0x%04x", pub.ttl, feat);
return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb),
cb, cb_data);
}
static void hb_publish_start_cb(uint16_t duration, int err, void *cb_data)
{
if (err) {
hb_publish_end_cb(err, cb_data);
}
}
static void hb_publish(struct k_work *work)
{
static const struct bt_mesh_send_cb publish_cb = {
.start = hb_publish_start_cb,
.end = hb_publish_end_cb,
};
struct bt_mesh_subnet *sub;
int err;
BT_DBG("hb_pub.count: %u", pub.count);
sub = bt_mesh_subnet_get(pub.net_idx);
if (!sub) {
BT_ERR("No matching subnet for idx 0x%02x", pub.net_idx);
pub.dst = BT_MESH_ADDR_UNASSIGNED;
return;
}
if (pub.count == 0U) {
return;
}
err = heartbeat_send(&publish_cb, NULL);
if (err) {
hb_publish_end_cb(err, NULL);
}
}
int bt_mesh_hb_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
{
uint8_t init_ttl, hops;
uint16_t feat;
if (buf->len < 3) {
BT_ERR("Too short heartbeat message");
return -EINVAL;
}
init_ttl = (net_buf_simple_pull_u8(buf) & 0x7f);
feat = net_buf_simple_pull_be16(buf);
hops = (init_ttl - rx->ctx.recv_ttl + 1);
if (rx->ctx.addr != sub.src || rx->ctx.recv_dst != sub.dst) {
BT_DBG("No subscription for received heartbeat");
return 0;
}
if (!k_delayed_work_pending(&sub_timer)) {
BT_DBG("Heartbeat subscription period expired");
return 0;
}
sub.min_hops = MIN(sub.min_hops, hops);
sub.max_hops = MAX(sub.max_hops, hops);
if (sub.count < 0xffff) {
sub.count++;
}
BT_DBG("src 0x%04x TTL %u InitTTL %u (%u hop%s) feat 0x%04x",
rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops,
(hops == 1U) ? "" : "s", feat);
notify_recv(hops, feat);
return 0;
}
static void pub_disable(void)
{
BT_DBG("");
pub.dst = BT_MESH_ADDR_UNASSIGNED;
pub.count = 0U;
pub.ttl = 0U;
pub.period = 0U;
k_delayed_work_cancel(&pub_timer);
}
uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *new_pub)
{
if (!new_pub || new_pub->dst == BT_MESH_ADDR_UNASSIGNED) {
pub_disable();
if (IS_ENABLED(CONFIG_BT_SETTINGS) &&
bt_mesh_is_provisioned()) {
bt_mesh_store_hb_pub();
}
return STATUS_SUCCESS;
}
if (!bt_mesh_subnet_get(new_pub->net_idx)) {
BT_ERR("Unknown NetKey 0x%04x", new_pub->net_idx);
return STATUS_INVALID_NETKEY;
}
new_pub->feat &= BT_MESH_FEAT_SUPPORTED;
pub = *new_pub;
if (!bt_mesh_is_provisioned()) {
return STATUS_SUCCESS;
}
/* The first Heartbeat message shall be published as soon as possible
* after the Heartbeat Publication Period state has been configured for
* periodic publishing.
*/
if (pub.period && pub.count) {
k_work_submit(&pub_timer.work);
} else {
k_delayed_work_cancel(&pub_timer);
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_store_hb_pub();
}
return STATUS_SUCCESS;
}
void bt_mesh_hb_pub_get(struct bt_mesh_hb_pub *get)
{
*get = pub;
}
uint8_t bt_mesh_hb_sub_set(uint16_t src, uint16_t dst, uint32_t period)
{
if (src != BT_MESH_ADDR_UNASSIGNED && !BT_MESH_ADDR_IS_UNICAST(src)) {
BT_WARN("Prohibited source address");
return STATUS_INVALID_ADDRESS;
}
if (BT_MESH_ADDR_IS_VIRTUAL(dst) || BT_MESH_ADDR_IS_RFU(dst) ||
(BT_MESH_ADDR_IS_UNICAST(dst) && dst != bt_mesh_primary_addr())) {
BT_WARN("Prohibited destination address");
return STATUS_INVALID_ADDRESS;
}
if (period > (1U << 16)) {
BT_WARN("Prohibited subscription period %u s", period);
return STATUS_CANNOT_SET;
}
/* Only an explicit address change to unassigned should trigger clearing
* of the values according to MESH/NODE/CFG/HBS/BV-02-C.
*/
if (src == BT_MESH_ADDR_UNASSIGNED || dst == BT_MESH_ADDR_UNASSIGNED) {
sub.src = BT_MESH_ADDR_UNASSIGNED;
sub.dst = BT_MESH_ADDR_UNASSIGNED;
sub.min_hops = 0U;
sub.max_hops = 0U;
sub.count = 0U;
sub.period = sub.period - sub_remaining();
if (!k_delayed_work_cancel(&sub_timer)) {
notify_sub_end();
}
} else if (period) {
sub.src = src;
sub.dst = dst;
sub.min_hops = BT_MESH_TTL_MAX;
sub.max_hops = 0U;
sub.count = 0U;
sub.period = period;
k_delayed_work_submit(&sub_timer, K_SECONDS(period));
} else {
/* Clearing the period should stop heartbeat subscription
* without clearing the parameters, so we can still read them.
*/
sub.period = sub.period - sub_remaining();
if (!k_delayed_work_cancel(&sub_timer)) {
notify_sub_end();
}
}
return STATUS_SUCCESS;
}
void bt_mesh_hb_sub_get(struct bt_mesh_hb_sub *get)
{
*get = sub;
get->remaining = sub_remaining();
}
void bt_mesh_hb_feature_changed(uint16_t features)
{
if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
return;
}
if (!(pub.feat & features)) {
return;
}
heartbeat_send(NULL, NULL);
}
void bt_mesh_hb_init(void)
{
pub.net_idx = BT_MESH_KEY_UNUSED;
k_delayed_work_init(&pub_timer, hb_publish);
k_delayed_work_init(&sub_timer, sub_end);
}
void bt_mesh_hb_start(void)
{
if (pub.count && pub.period) {
BT_DBG("Starting heartbeat publication");
k_work_submit(&pub_timer.work);
}
}
void bt_mesh_hb_suspend(void)
{
k_delayed_work_cancel(&pub_timer);
}
void bt_mesh_hb_resume(void)
{
if (pub.period && pub.count) {
BT_DBG("Starting heartbeat publication");
k_work_submit(&pub_timer.work);
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
static inline uint16_t bt_mesh_hb_pwr2(uint8_t val)
{
if (!val) {
return 0x0000;
} else if (val == 0xff || val == 0x11) {
return 0xffff;
} else {
return (1 << (val - 1));
}
}
static inline uint8_t bt_mesh_hb_log(uint32_t val)
{
if (!val) {
return 0x00;
} else if (val == 0xffff) {
return 0xff;
} else {
return 32 - __builtin_clz(val);
}
}
void bt_mesh_hb_init(void);
void bt_mesh_hb_start(void);
void bt_mesh_hb_suspend(void);
void bt_mesh_hb_resume(void);
int bt_mesh_hb_recv(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);
void bt_mesh_hb_feature_changed(uint16_t features);
uint8_t bt_mesh_hb_pub_set(struct bt_mesh_hb_pub *hb_pub);
uint8_t bt_mesh_hb_sub_set(uint16_t src, uint16_t dst, uint32_t period);

View file

@ -23,6 +23,7 @@
#include "mesh.h"
#include "net.h"
#include "transport.h"
#include "heartbeat.h"
#include "access.h"
#include "beacon.h"
#include "foundation.h"
@ -197,7 +198,6 @@ static int send_friend_clear(void)
static void clear_friendship(bool force, bool disable)
{
struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
BT_DBG("force %u disable %u", force, disable);
@ -246,9 +246,7 @@ static void clear_friendship(bool force, bool disable)
*/
lpn->groups_changed = 1U;
if (cfg->hb_pub.feat & BT_MESH_FEAT_LOW_POWER) {
(void)bt_mesh_heartbeat_send(NULL, NULL);
}
bt_mesh_hb_feature_changed(BT_MESH_FEAT_LOW_POWER);
if (disable) {
lpn_set_state(BT_MESH_LPN_DISABLED);
@ -972,8 +970,6 @@ int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
}
if (!lpn->established) {
struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
/* This is normally checked on the transport layer, however
* in this state we're also still accepting master
* credentials so we need to ensure the right ones (Friend
@ -988,9 +984,7 @@ int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
BT_INFO("Friendship established with 0x%04x", lpn->frnd);
if (cfg->hb_pub.feat & BT_MESH_FEAT_LOW_POWER) {
(void)bt_mesh_heartbeat_send(NULL, NULL);
}
bt_mesh_hb_feature_changed(BT_MESH_FEAT_LOW_POWER);
Z_STRUCT_SECTION_FOREACH(bt_mesh_lpn_cb, cb) {
if (cb->established) {

View file

@ -32,6 +32,7 @@
#include "lpn.h"
#include "friend.h"
#include "transport.h"
#include "heartbeat.h"
#include "access.h"
#include "foundation.h"
#include "proxy.h"
@ -244,7 +245,7 @@ int bt_mesh_suspend(void)
return err;
}
bt_mesh_hb_pub_disable();
bt_mesh_hb_suspend();
if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
bt_mesh_beacon_disable();
@ -287,6 +288,8 @@ int bt_mesh_resume(void)
return err;
}
bt_mesh_hb_resume();
if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
bt_mesh_beacon_enable();
}
@ -324,6 +327,7 @@ int bt_mesh_init(const struct bt_mesh_prov *prov,
bt_mesh_net_init();
bt_mesh_trans_init();
bt_mesh_hb_init();
bt_mesh_beacon_init();
bt_mesh_adv_init();
@ -373,6 +377,8 @@ int bt_mesh_start(void)
bt_mesh_prov_complete(sub->net_idx, addr);
}
bt_mesh_hb_start();
bt_mesh_model_foreach(model_start, NULL);
return 0;

View file

@ -31,6 +31,7 @@
#include "crypto.h"
#include "rpl.h"
#include "transport.h"
#include "heartbeat.h"
#include "access.h"
#include "foundation.h"
#include "proxy.h"
@ -397,43 +398,30 @@ static int app_key_set(const char *name, size_t len_rd,
static int hb_pub_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get();
struct bt_mesh_hb_pub pub;
struct hb_pub_val hb_val;
int err;
if (!pub) {
return -ENOENT;
}
if (len_rd == 0) {
pub->dst = BT_MESH_ADDR_UNASSIGNED;
pub->count = 0U;
pub->ttl = 0U;
pub->period = 0U;
pub->feat = 0U;
BT_DBG("Cleared heartbeat publication");
return 0;
}
err = mesh_x_set(read_cb, cb_arg, &hb_val, sizeof(hb_val));
if (err) {
BT_ERR("Failed to set \'hb_val\'");
return err;
}
pub->dst = hb_val.dst;
pub->period = hb_val.period;
pub->ttl = hb_val.ttl;
pub->feat = hb_val.feat;
pub->net_idx = hb_val.net_idx;
pub.dst = hb_val.dst;
pub.period = bt_mesh_hb_pwr2(hb_val.period);
pub.ttl = hb_val.ttl;
pub.feat = hb_val.feat;
pub.net_idx = hb_val.net_idx;
if (hb_val.indefinite) {
pub->count = 0xffff;
pub.count = 0xffff;
} else {
pub->count = 0U;
pub.count = 0U;
}
(void)bt_mesh_hb_pub_set(&pub);
BT_DBG("Restored heartbeat publication");
return 0;
@ -1006,7 +994,6 @@ static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
static int mesh_commit(void)
{
struct bt_mesh_hb_pub *hb_pub;
struct bt_mesh_cfg_srv *cfg;
if (!bt_mesh_subnet_next(NULL)) {
@ -1024,13 +1011,6 @@ static int mesh_commit(void)
bt_mesh_model_foreach(commit_mod, NULL);
hb_pub = bt_mesh_hb_pub_get();
if (hb_pub && hb_pub->dst != BT_MESH_ADDR_UNASSIGNED &&
hb_pub->count && hb_pub->period) {
BT_DBG("Starting heartbeat publication");
k_work_submit(&hb_pub->timer.work);
}
cfg = bt_mesh_cfg_get();
if (cfg && stored_cfg.valid) {
cfg->net_transmit = stored_cfg.cfg.net_transmit;
@ -1245,23 +1225,20 @@ static void store_pending_rpl(struct bt_mesh_rpl *rpl, void *user_data)
static void store_pending_hb_pub(void)
{
struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get();
struct bt_mesh_hb_pub pub;
struct hb_pub_val val;
int err;
if (!pub) {
return;
}
if (pub->dst == BT_MESH_ADDR_UNASSIGNED) {
bt_mesh_hb_pub_get(&pub);
if (pub.dst == BT_MESH_ADDR_UNASSIGNED) {
err = settings_delete("bt/mesh/HBPub");
} else {
val.indefinite = (pub->count == 0xffff);
val.dst = pub->dst;
val.period = pub->period;
val.ttl = pub->ttl;
val.feat = pub->feat;
val.net_idx = pub->net_idx;
val.indefinite = (pub.count == 0xffff);
val.dst = pub.dst;
val.period = bt_mesh_hb_log(pub.period);
val.ttl = pub.ttl;
val.feat = pub.feat;
val.net_idx = pub.net_idx;
err = settings_save_one("bt/mesh/HBPub", &val, sizeof(val));
}

View file

@ -35,6 +35,7 @@
#include "access.h"
#include "foundation.h"
#include "settings.h"
#include "heartbeat.h"
#include "transport.h"
#define AID_MASK ((uint8_t)(BIT_MASK(6)))
@ -124,13 +125,6 @@ K_MEM_SLAB_DEFINE(segs, BT_MESH_APP_SEG_SDU_MAX, CONFIG_BT_MESH_SEG_BUFS, 4);
static struct bt_mesh_va virtual_addrs[CONFIG_BT_MESH_LABEL_COUNT];
static uint16_t hb_sub_dst = BT_MESH_ADDR_UNASSIGNED;
void bt_mesh_set_hb_sub_dst(uint16_t addr)
{
hb_sub_dst = addr;
}
static int send_unseg(struct bt_mesh_net_tx *tx, struct net_buf_simple *sdu,
const struct bt_mesh_send_cb *cb, void *cb_data,
const uint8_t *ctl_op)
@ -862,36 +856,6 @@ static int trans_ack(struct bt_mesh_net_rx *rx, uint8_t hdr,
return 0;
}
static int trans_heartbeat(struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf)
{
uint8_t init_ttl, hops;
uint16_t feat;
if (buf->len < 3) {
BT_ERR("Too short heartbeat message");
return -EINVAL;
}
if (rx->ctx.recv_dst != hb_sub_dst) {
BT_WARN("Ignoring heartbeat to non-subscribed destination");
return 0;
}
init_ttl = (net_buf_simple_pull_u8(buf) & 0x7f);
feat = net_buf_simple_pull_be16(buf);
hops = (init_ttl - rx->ctx.recv_ttl + 1);
BT_DBG("src 0x%04x TTL %u InitTTL %u (%u hop%s) feat 0x%04x",
rx->ctx.addr, rx->ctx.recv_ttl, init_ttl, hops,
(hops == 1U) ? "" : "s", feat);
bt_mesh_heartbeat(rx->ctx.addr, rx->ctx.recv_dst, hops, feat);
return 0;
}
static int ctl_recv(struct bt_mesh_net_rx *rx, uint8_t hdr,
struct net_buf_simple *buf, uint64_t *seq_auth)
{
@ -903,7 +867,7 @@ static int ctl_recv(struct bt_mesh_net_rx *rx, uint8_t hdr,
case TRANS_CTL_OP_ACK:
return trans_ack(rx, hdr, buf, seq_auth);
case TRANS_CTL_OP_HEARTBEAT:
return trans_heartbeat(rx, buf);
return bt_mesh_hb_recv(rx, buf);
}
/* Only acks and heartbeats may need processing without local_match */
@ -1627,58 +1591,6 @@ void bt_mesh_trans_init(void)
}
}
int bt_mesh_heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data)
{
struct bt_mesh_cfg_srv *cfg = bt_mesh_cfg_get();
uint16_t feat = 0U;
struct __packed {
uint8_t init_ttl;
uint16_t feat;
} hb;
struct bt_mesh_msg_ctx ctx = {
.net_idx = cfg->hb_pub.net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = cfg->hb_pub.dst,
.send_ttl = cfg->hb_pub.ttl,
};
struct bt_mesh_net_tx tx = {
.sub = bt_mesh_subnet_get(cfg->hb_pub.net_idx),
.ctx = &ctx,
.src = bt_mesh_model_elem(cfg->model)->addr,
.xmit = bt_mesh_net_transmit_get(),
};
/* Do nothing if heartbeat publication is not enabled */
if (cfg->hb_pub.dst == BT_MESH_ADDR_UNASSIGNED) {
return 0;
}
hb.init_ttl = cfg->hb_pub.ttl;
if (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED) {
feat |= BT_MESH_FEAT_RELAY;
}
if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
feat |= BT_MESH_FEAT_PROXY;
}
if (bt_mesh_friend_get() == BT_MESH_FRIEND_ENABLED) {
feat |= BT_MESH_FEAT_FRIEND;
}
if (bt_mesh_lpn_established()) {
feat |= BT_MESH_FEAT_LOW_POWER;
}
hb.feat = sys_cpu_to_be16(feat);
BT_DBG("InitTTL %u feat 0x%04x", cfg->hb_pub.ttl, feat);
return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb),
cb, cb_data);
}
struct bt_mesh_va *bt_mesh_va_get(uint16_t index)
{
if (index >= ARRAY_SIZE(virtual_addrs)) {

View file

@ -86,8 +86,6 @@ struct bt_mesh_va {
uint8_t uuid[16];
};
void bt_mesh_set_hb_sub_dst(uint16_t addr);
bool bt_mesh_tx_in_progress(void);
void bt_mesh_rx_reset(void);
@ -115,8 +113,6 @@ void bt_mesh_trans_init(void);
void bt_mesh_trans_reset(void);
int bt_mesh_heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data);
struct bt_mesh_va *bt_mesh_va_get(uint16_t index);
struct bt_mesh_va *bt_mesh_va_find(uint8_t uuid[16]);