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.
this is port of 0dc9e5cd9695e4ab403bf13f4d51d84de6a5ebb9
diff --git a/nimble/host/mesh/include/mesh/cfg_srv.h b/nimble/host/mesh/include/mesh/cfg_srv.h
index f7adcec..c9045bc 100644
--- a/nimble/host/mesh/include/mesh/cfg_srv.h
+++ b/nimble/host/mesh/include/mesh/cfg_srv.h
@@ -33,8 +33,11 @@
uint8_t frnd; /* Friend state */
uint8_t default_ttl; /* Default TTL */
- /* Heartbeat Publication */
- 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;
uint16_t dst;
@@ -43,10 +46,13 @@
uint8_t ttl;
uint16_t feat;
uint16_t net_idx;
- } hb_pub;
+ } hb_pub; /* _deprectated */
- /* Heartbeat Subscription */
- struct bt_mesh_hb_sub {
+ /** Heartbeat Subscription parameters.
+ *
+ * @deprecated in favor of standalone API in bluetooth/mesh/heartbeat.h.
+ */
+ struct {
int64_t expiry;
uint16_t src;
@@ -57,7 +63,7 @@
/* Optional subscription tracking function */
void (*func)(uint8_t hops, uint16_t feat);
- } hb_sub;
+ } hb_sub; /* _deprectated */
};
extern const struct bt_mesh_model_op bt_mesh_cfg_srv_op[];
diff --git a/nimble/host/mesh/include/mesh/glue.h b/nimble/host/mesh/include/mesh/glue.h
index 6d080f3..237bd05 100644
--- a/nimble/host/mesh/include/mesh/glue.h
+++ b/nimble/host/mesh/include/mesh/glue.h
@@ -286,6 +286,7 @@
#define BT_GATT_CCC_NOTIFY BLE_GATT_CHR_PROP_NOTIFY
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
/** Description of different data types that can be encoded into
* advertising data. Used to form arrays that are passed to the
* bt_le_adv_start() function.
@@ -336,6 +337,7 @@
void k_work_init(struct ble_npl_callout *work, ble_npl_event_fn handler);
void k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f);
void k_delayed_work_cancel(struct k_delayed_work *w);
+bool k_delayed_work_pending(struct k_delayed_work *w);
void k_delayed_work_submit(struct k_delayed_work *w, uint32_t ms);
int64_t k_uptime_get(void);
uint32_t k_uptime_get_32(void);
diff --git a/nimble/host/mesh/include/mesh/heartbeat.h b/nimble/host/mesh/include/mesh/heartbeat.h
new file mode 100644
index 0000000..b9990f6
--- /dev/null
+++ b/nimble/host/mesh/include/mesh/heartbeat.h
@@ -0,0 +1,123 @@
+/** @file
+ * @brief Bluetooth Mesh Heartbeat API.
+ */
+
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef _BLUETOOTH_MESH_HEARTBEAT_H_
+#define _BLUETOOTH_MESH_HEARTBEAT_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);
+};
+
+/** @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);
+
+extern struct bt_mesh_hb_cb hb_cb;
+
+#ifdef __cplusplus
+}
+#endif
+/**
+ * @}
+ */
+
+#endif /* _BLUETOOTH_MESH_HEARTBEAT_H_ */
\ No newline at end of file
diff --git a/nimble/host/mesh/src/cfg_srv.c b/nimble/host/mesh/src/cfg_srv.c
index 08595fe..82ae608 100644
--- a/nimble/host/mesh/src/cfg_srv.c
+++ b/nimble/host/mesh/src/cfg_srv.c
@@ -22,6 +22,7 @@
#include "rpl.h"
#include "lpn.h"
#include "transport.h"
+#include "heartbeat.h"
#include "crypto.h"
#include "access.h"
#include "beacon.h"
@@ -643,9 +644,7 @@
bt_mesh_adv_update();
- 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);
@@ -757,9 +756,7 @@
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->om_data[0]);
goto done;
@@ -1744,18 +1741,6 @@
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 = 0;
- cfg->hb_pub.ttl = 0;
- cfg->hb_pub.period = 0;
-
- 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 os_mbuf *buf)
@@ -2201,9 +2186,7 @@
}
}
- 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);
@@ -2317,17 +2300,6 @@
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) {
@@ -2341,17 +2313,6 @@
}
}
-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;
@@ -2363,10 +2324,9 @@
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)
{
struct os_mbuf *msg = BT_MESH_MODEL_BUF(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);
@@ -2374,20 +2334,13 @@
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");
}
@@ -2399,9 +2352,13 @@
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *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,
@@ -2409,27 +2366,32 @@
struct os_mbuf *buf)
{
struct hb_pub_param *param = (void *)buf->om_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) {
@@ -2437,84 +2399,32 @@
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;
- }
-
- 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) * 1000);
-
- /* 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);
+ status = bt_mesh_hb_pub_set(&pub);
+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)
{
struct os_mbuf *msg = BT_MESH_MODEL_BUF(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 = 0;
- } 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)) {
@@ -2528,92 +2438,58 @@
struct bt_mesh_msg_ctx *ctx,
struct os_mbuf *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 os_mbuf *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 = 0;
- cfg->hb_sub.count = 0;
- }
-
- 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 = 0;
- cfg->hb_sub.count = 0;
- period_ms = hb_pwr2(sub_period, 1) * 1000;
+ 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", (unsigned) 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 = 0;
+ 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[] = {
@@ -2667,59 +2543,6 @@
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 ble_npl_event *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 = ble_npl_event_get_arg(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 == 0) {
- 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) {
@@ -2745,6 +2568,14 @@
return true;
}
+static void (*hb_sub_cb)(uint8_t hops, uint16_t features);
+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;
@@ -2768,6 +2599,11 @@
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.
@@ -2786,11 +2622,6 @@
cfg->gatt_proxy = BT_MESH_GATT_PROXY_NOT_SUPPORTED;
}
- k_delayed_work_init(&cfg->hb_pub.timer, hb_publish);
- k_delayed_work_add_arg(&cfg->hb_pub.timer, cfg);
- cfg->hb_pub.net_idx = BT_MESH_KEY_UNUSED;
- cfg->hb_sub.expiry = 0;
-
cfg->model = model;
conf = cfg;
@@ -2828,49 +2659,9 @@
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) {
@@ -2935,16 +2726,6 @@
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;
diff --git a/nimble/host/mesh/src/foundation.h b/nimble/host/mesh/src/foundation.h
index b424e04..4444763 100644
--- a/nimble/host/mesh/src/foundation.h
+++ b/nimble/host/mesh/src/foundation.h
@@ -128,12 +128,8 @@
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);
diff --git a/nimble/host/mesh/src/glue.c b/nimble/host/mesh/src/glue.c
index e1c2ae3..2651199 100644
--- a/nimble/host/mesh/src/glue.c
+++ b/nimble/host/mesh/src/glue.c
@@ -408,6 +408,12 @@
#endif
}
+bool
+k_delayed_work_pending(struct k_delayed_work *w)
+{
+ return ble_npl_callout_is_active(&w->work);
+}
+
void
k_delayed_work_cancel(struct k_delayed_work *w)
{
diff --git a/nimble/host/mesh/src/heartbeat.c b/nimble/host/mesh/src/heartbeat.c
new file mode 100644
index 0000000..d4defdf
--- /dev/null
+++ b/nimble/host/mesh/src/heartbeat.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define MESH_LOG_MODULE BLE_MESH_HEARTBEAT_LOG
+
+#include "mesh_priv.h"
+#include "net.h"
+#include "rpl.h"
+#include "access.h"
+#include "lpn.h"
+#include "settings.h"
+#include "transport.h"
+#include "heartbeat.h"
+#include "foundation.h"
+#include "mesh/glue.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();
+
+ if (hb_cb.recv != NULL) {
+ hb_cb.recv(&sub, hops, feat);
+ }
+}
+
+static void notify_sub_end(void)
+{
+ sub.remaining = 0;
+
+ if (hb_cb.sub_end != NULL) {
+ hb_cb.sub_end(&sub);
+ }
+}
+
+static void sub_end(struct ble_npl_event *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 ble_npl_event *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 os_mbuf *buf)
+{
+ uint8_t init_ttl, hops;
+ uint16_t feat;
+
+ if (buf->om_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();
+ 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();
+ 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);
+ }
+}
diff --git a/nimble/host/mesh/src/heartbeat.h b/nimble/host/mesh/src/heartbeat.h
new file mode 100644
index 0000000..225fa03
--- /dev/null
+++ b/nimble/host/mesh/src/heartbeat.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Nordic Semiconductor ASA
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include "mesh/heartbeat.h"
+
+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 os_mbuf *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);
\ No newline at end of file
diff --git a/nimble/host/mesh/src/lpn.c b/nimble/host/mesh/src/lpn.c
index df0c6c2..947d20f 100644
--- a/nimble/host/mesh/src/lpn.c
+++ b/nimble/host/mesh/src/lpn.c
@@ -19,6 +19,7 @@
#include "adv.h"
#include "net.h"
#include "transport.h"
+#include "heartbeat.h"
#include "access.h"
#include "beacon.h"
#include "foundation.h"
@@ -195,7 +196,6 @@
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);
@@ -242,9 +242,7 @@
*/
lpn->groups_changed = 1;
- 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);
@@ -959,8 +957,6 @@
}
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
@@ -975,9 +971,7 @@
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);
if (lpn_cb) {
lpn_cb(lpn->frnd, true);
diff --git a/nimble/host/mesh/src/mesh.c b/nimble/host/mesh/src/mesh.c
index 2f9d3e3..01b8811 100644
--- a/nimble/host/mesh/src/mesh.c
+++ b/nimble/host/mesh/src/mesh.c
@@ -30,6 +30,7 @@
#include "access.h"
#include "foundation.h"
#include "proxy.h"
+#include "heartbeat.h"
#include "shell.h"
#include "mesh_priv.h"
#include "settings.h"
@@ -261,7 +262,7 @@
return err;
}
- bt_mesh_hb_pub_disable();
+ bt_mesh_hb_suspend();
if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
bt_mesh_beacon_disable();
@@ -303,6 +304,8 @@
return err;
}
+ bt_mesh_hb_resume();
+
if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) {
bt_mesh_beacon_enable();
}
@@ -340,6 +343,7 @@
bt_mesh_net_init();
bt_mesh_trans_init();
+ bt_mesh_hb_init();
bt_mesh_beacon_init();
bt_mesh_adv_init();
@@ -403,6 +407,9 @@
bt_mesh_prov_complete(sub->net_idx, addr);
}
+
+ bt_mesh_hb_start();
+
bt_mesh_model_foreach(model_start, NULL);
return 0;
diff --git a/nimble/host/mesh/src/settings.c b/nimble/host/mesh/src/settings.c
index 7f60ad9..6aa6701 100644
--- a/nimble/host/mesh/src/settings.c
+++ b/nimble/host/mesh/src/settings.c
@@ -18,6 +18,7 @@
#include "rpl.h"
#include "crypto.h"
#include "transport.h"
+#include "heartbeat.h"
#include "access.h"
#include "foundation.h"
#include "proxy.h"
@@ -420,27 +421,12 @@
static int hb_pub_set(int argc, char **argv, char *val)
{
- struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get();
+ struct bt_mesh_hb_pub pub;
struct hb_pub_val hb_val;
int len, err;
BT_DBG("val %s", val ? val : "(null)");
- if (!pub) {
- return -ENOENT;
- }
-
- if (!val) {
- pub->dst = BT_MESH_ADDR_UNASSIGNED;
- pub->count = 0;
- pub->ttl = 0;
- pub->period = 0;
- pub->feat = 0;
-
- BT_DBG("Cleared heartbeat publication");
- return 0;
- }
-
len = sizeof(hb_val);
err = settings_bytes_from_str(val, &hb_val, &len);
if (err) {
@@ -454,18 +440,20 @@
return -EINVAL;
}
- 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 = 0;
+ pub.count = 0;
}
+ (void)bt_mesh_hb_pub_set(&pub);
+
BT_DBG("Restored heartbeat publication");
return 0;
@@ -1032,7 +1020,6 @@
static int mesh_commit(void)
{
- struct bt_mesh_hb_pub *hb_pub;
struct bt_mesh_cfg_srv *cfg;
if (!bt_mesh_subnet_next(NULL)) {
@@ -1050,13 +1037,6 @@
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;
@@ -1305,24 +1285,20 @@
static void store_pending_hb_pub(void)
{
char buf[BT_SETTINGS_SIZE(sizeof(struct hb_pub_val))];
- struct bt_mesh_hb_pub *pub = bt_mesh_hb_pub_get();
struct hb_pub_val val;
char *str;
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) {
str = NULL;
} 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;
str = settings_str_from_bytes(&val, sizeof(val),
buf, sizeof(buf));
diff --git a/nimble/host/mesh/src/subnet.h b/nimble/host/mesh/src/subnet.h
index da0a1df..154b5d4 100644
--- a/nimble/host/mesh/src/subnet.h
+++ b/nimble/host/mesh/src/subnet.h
@@ -9,7 +9,7 @@
#include <stdint.h>
#include <sys/types.h>
-
+#include "mesh/glue.h"
#define BT_MESH_NET_FLAG_KR BIT(0)
#define BT_MESH_NET_FLAG_IVU BIT(1)
diff --git a/nimble/host/mesh/src/transport.c b/nimble/host/mesh/src/transport.c
index 42ec8ea..c4ed595 100644
--- a/nimble/host/mesh/src/transport.c
+++ b/nimble/host/mesh/src/transport.c
@@ -26,6 +26,7 @@
#include "access.h"
#include "foundation.h"
#include "settings.h"
+#include "heartbeat.h"
#include "transport.h"
#include "testing.h"
@@ -116,13 +117,6 @@
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 os_mbuf *sdu,
const struct bt_mesh_send_cb *cb, void *cb_data,
const uint8_t *ctl_op)
@@ -860,36 +854,6 @@
return 0;
}
-static int trans_heartbeat(struct bt_mesh_net_rx *rx,
- struct os_mbuf *buf)
-{
- uint8_t init_ttl, hops;
- uint16_t feat;
-
- if (buf->om_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 == 1) ? "" : "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 os_mbuf *buf, uint64_t *seq_auth)
{
@@ -901,7 +865,7 @@
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 */
@@ -1636,58 +1600,6 @@
}
}
-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)) {
diff --git a/nimble/host/mesh/src/transport.h b/nimble/host/mesh/src/transport.h
index 13fc9bc..4a9f7e3 100644
--- a/nimble/host/mesh/src/transport.h
+++ b/nimble/host/mesh/src/transport.h
@@ -89,8 +89,6 @@
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);
@@ -107,8 +105,6 @@
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]);
diff --git a/nimble/host/mesh/syscfg.yml b/nimble/host/mesh/syscfg.yml
index d31238f..ec14d98 100644
--- a/nimble/host/mesh/syscfg.yml
+++ b/nimble/host/mesh/syscfg.yml
@@ -722,6 +722,15 @@
Minimum level for the BLE Mesh Replay protection list log.
value: 1
+ BLE_MESH_HEARTBEAT_LOG_MOD:
+ description: >
+ Numeric module ID to use for BLE Mesh Replay protection list messages.
+ value: 26
+ BLE_MESH_HEARTBEAT_LOG_LVL:
+ description: >
+ Minimum level for the BLE Mesh Replay protection list log.
+ value: 1
+
syscfg.logs:
BLE_MESH_LOG:
module: MYNEWT_VAL(BLE_MESH_LOG_MOD)
@@ -791,6 +800,10 @@
module: MYNEWT_VAL(BLE_MESH_PROVISIONER_LOG_MOD)
level: MYNEWT_VAL(BLE_MESH_PROVISIONER_LOG_LVL)
+ BLE_MESH_HEARTBEAT_LOG:
+ module: MYNEWT_VAL(BLE_MESH_HEARTBEAT_LOG_MOD)
+ level: MYNEWT_VAL(BLE_MESH_HEARTBEAT_LOG_LVL)
+
syscfg.vals.BLE_MESH_SHELL:
BLE_MESH_CFG_CLI: 1
BLE_MESH_HEALTH_CLI: 1
diff --git a/porting/examples/linux_blemesh/include/logcfg/logcfg.h b/porting/examples/linux_blemesh/include/logcfg/logcfg.h
index d2903ed..798418d 100644
--- a/porting/examples/linux_blemesh/include/logcfg/logcfg.h
+++ b/porting/examples/linux_blemesh/include/logcfg/logcfg.h
@@ -127,6 +127,13 @@
#define BLE_MESH_PROV_DEVICE_LOG_CRITICAL(...) MODLOG_CRITICAL(24, __VA_ARGS__)
#define BLE_MESH_PROV_DEVICE_LOG_DISABLED(...) MODLOG_DISABLED(24, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_INFO(...) MODLOG_INFO(25, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_WARN(...) MODLOG_WARN(25, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_ERROR(...) MODLOG_ERROR(25, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_CRITICAL(...) MODLOG_CRITICAL(25, __VA_ARGS__)
+#define BLE_MESH_HEARTBEAT_LOG_DISABLED(...) MODLOG_DISABLED(25, __VA_ARGS__)
+
#define DFLT_LOG_DEBUG(...) IGNORE(__VA_ARGS__)
#define DFLT_LOG_INFO(...) MODLOG_INFO(0, __VA_ARGS__)
#define DFLT_LOG_WARN(...) MODLOG_WARN(0, __VA_ARGS__)