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:
parent
ec0e737b0c
commit
0dc9e5cd96
14 changed files with 659 additions and 451 deletions
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
135
include/bluetooth/mesh/heartbeat.h
Normal file
135
include/bluetooth/mesh/heartbeat.h
Normal 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_ */
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
356
subsys/bluetooth/mesh/heartbeat.c
Normal file
356
subsys/bluetooth/mesh/heartbeat.c
Normal 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);
|
||||
}
|
||||
}
|
38
subsys/bluetooth/mesh/heartbeat.h
Normal file
38
subsys/bluetooth/mesh/heartbeat.h
Normal 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);
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue