mirror of
https://github.com/espressif/esp-nimble.git
synced 2026-06-06 05:14:45 +00:00
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
This commit is contained in:
committed by
Łukasz Rymanowski
parent
4435537603
commit
c06a03adfc
@@ -33,8 +33,11 @@ struct bt_mesh_cfg_srv {
|
||||
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 @@ struct bt_mesh_cfg_srv {
|
||||
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 @@ struct bt_mesh_cfg_srv {
|
||||
|
||||
/* 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[];
|
||||
|
||||
@@ -286,6 +286,7 @@ void net_buf_reserve(struct os_mbuf *om, size_t reserve);
|
||||
#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 @@ struct k_delayed_work {
|
||||
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);
|
||||
|
||||
@@ -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_ */
|
||||
+79
-298
@@ -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 @@ static void gatt_proxy_set(struct bt_mesh_model *model,
|
||||
|
||||
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 @@ 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->om_data[0]);
|
||||
goto done;
|
||||
@@ -1744,18 +1741,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 = 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 @@ 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);
|
||||
@@ -2317,17 +2300,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) {
|
||||
@@ -2341,17 +2313,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;
|
||||
@@ -2363,10 +2324,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)
|
||||
{
|
||||
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 @@ 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");
|
||||
}
|
||||
@@ -2399,9 +2352,13 @@ static void heartbeat_pub_get(struct bt_mesh_model *model,
|
||||
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 @@ static void heartbeat_pub_set(struct bt_mesh_model *model,
|
||||
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 @@ 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;
|
||||
}
|
||||
|
||||
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 @@ static void heartbeat_sub_get(struct bt_mesh_model *model,
|
||||
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 @@ 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 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 @@ static bool conf_is_valid(struct bt_mesh_cfg_srv *cfg)
|
||||
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 @@ 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.
|
||||
@@ -2786,11 +2622,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);
|
||||
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 @@ 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) {
|
||||
@@ -2935,16 +2726,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;
|
||||
|
||||
@@ -128,12 +128,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);
|
||||
|
||||
@@ -408,6 +408,12 @@ k_delayed_work_init(struct k_delayed_work *w, ble_npl_event_fn *f)
|
||||
#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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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 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);
|
||||
@@ -242,9 +242,7 @@ static void clear_friendship(bool force, bool disable)
|
||||
*/
|
||||
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 @@ 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
|
||||
@@ -975,9 +971,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);
|
||||
|
||||
if (lpn_cb) {
|
||||
lpn_cb(lpn->frnd, true);
|
||||
|
||||
@@ -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 @@ 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();
|
||||
@@ -303,6 +304,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();
|
||||
}
|
||||
@@ -340,6 +343,7 @@ int bt_mesh_init(uint8_t own_addr_type, 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();
|
||||
|
||||
@@ -403,6 +407,9 @@ 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;
|
||||
|
||||
@@ -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 app_key_set(int argc, char **argv, char *val)
|
||||
|
||||
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 @@ static int hb_pub_set(int argc, char **argv, char *val)
|
||||
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 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)) {
|
||||
@@ -1050,13 +1037,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;
|
||||
@@ -1305,24 +1285,20 @@ static void store_pending_rpl(struct bt_mesh_rpl *rpl, void *user_data)
|
||||
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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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 @@ 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 os_mbuf *sdu,
|
||||
const struct bt_mesh_send_cb *cb, void *cb_data,
|
||||
const uint8_t *ctl_op)
|
||||
@@ -860,36 +854,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 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 @@ 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 */
|
||||
@@ -1636,58 +1600,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)) {
|
||||
|
||||
@@ -89,8 +89,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);
|
||||
@@ -107,8 +105,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]);
|
||||
|
||||
@@ -722,6 +722,15 @@ syscfg.defs:
|
||||
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 @@ syscfg.logs:
|
||||
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
|
||||
|
||||
@@ -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__)
|
||||
|
||||
Reference in New Issue
Block a user