Bluetooth: Mesh: Fix IV Update duration tracking
When the IV Update state enters Normal operation or IV Update in Progress, we need to keep track of how many hours has passed in the state, since the specification requires us to remain in the state at least for 96 hours (Update in Progress has an additional upper limit of 144 hours). In order to fulfil the above requirement, even if the node might be powered off once in a while, we need to store persistently how many hours the node has been in the state. This doesn't necessarily need to happen every hour (thanks to the flexible duration range). The exact cadence will depend a lot on the ways that the node will be used and what kind of power source it has. Since there is no single optimal answer, this patch adds a new configuration option, which allows specifying a divider, i.e. how many intervals the 96 hour minimum gets split into. After each interval the duration that the node has been in the current state gets stored to flash. E.g. the default value of 4 means that the state is saved every 24 hours (96 / 4). Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
parent
b98388c67f
commit
2b73c97d68
6 changed files with 90 additions and 71 deletions
|
@ -162,6 +162,32 @@ config BT_MESH_ADV_BUF_COUNT
|
|||
which leaves 56 bytes for application layer data using a
|
||||
4-byte MIC and 52 bytes using an 8-byte MIC.
|
||||
|
||||
config BT_MESH_IVU_DIVIDER
|
||||
int "Divider for IV Update state refresh timer"
|
||||
default 4
|
||||
range 2 96
|
||||
help
|
||||
When the IV Update state enters Normal operation or IV Update
|
||||
in Progress, we need to keep track of how many hours has passed
|
||||
in the state, since the specification requires us to remain in
|
||||
the state at least for 96 hours (Update in Progress has an
|
||||
additional upper limit of 144 hours).
|
||||
|
||||
In order to fulfil the above requirement, even if the node might
|
||||
be powered off once in a while, we need to store persistently
|
||||
how many hours the node has been in the state. This doesn't
|
||||
necessarily need to happen every hour (thanks to the flexible
|
||||
duration range). The exact cadence will depend a lot on the
|
||||
ways that the node will be used and what kind of power source it
|
||||
has.
|
||||
|
||||
Since there is no single optimal answer, this configuration
|
||||
option allows specifying a divider, i.e. how many intervals
|
||||
the 96 hour minimum gets split into. After each interval the
|
||||
duration that the node has been in the current state gets
|
||||
stored to flash. E.g. the default value of 4 means that the
|
||||
state is saved every 24 hours (96 / 4).
|
||||
|
||||
config BT_MESH_TX_SEG_MSG_COUNT
|
||||
int "Maximum number of simultaneous outgoing segmented messages"
|
||||
default 1
|
||||
|
|
|
@ -65,7 +65,7 @@ int bt_mesh_provision(const u8_t net_key[16], u16_t net_idx,
|
|||
BT_DBG("Storing network information persistently");
|
||||
bt_mesh_store_net();
|
||||
bt_mesh_store_subnet(&bt_mesh.sub[0]);
|
||||
bt_mesh_store_iv();
|
||||
bt_mesh_store_iv(false);
|
||||
}
|
||||
|
||||
bt_mesh_net_start();
|
||||
|
@ -86,10 +86,10 @@ void bt_mesh_reset(void)
|
|||
bt_mesh.iv_update = 0;
|
||||
bt_mesh.pending_update = 0;
|
||||
bt_mesh.valid = 0;
|
||||
bt_mesh.last_update = 0;
|
||||
bt_mesh.ivu_duration = 0;
|
||||
bt_mesh.ivu_initiator = 0;
|
||||
|
||||
k_delayed_work_cancel(&bt_mesh.ivu_complete);
|
||||
k_delayed_work_cancel(&bt_mesh.ivu_timer);
|
||||
|
||||
bt_mesh_cfg_reset();
|
||||
|
||||
|
|
|
@ -482,15 +482,11 @@ int bt_mesh_net_create(u16_t idx, u8_t flags, const u8_t key[16],
|
|||
|
||||
bt_mesh.iv_index = iv_index;
|
||||
bt_mesh.iv_update = BT_MESH_IV_UPDATE(flags);
|
||||
bt_mesh.ivu_duration = 0;
|
||||
bt_mesh.ivu_unknown = 1;
|
||||
|
||||
/* Set initial IV Update procedure state time-stamp */
|
||||
bt_mesh.last_update = BT_MESH_NET_IVU_UNKNOWN;
|
||||
|
||||
/* Set a timer to transition back to normal mode */
|
||||
if (bt_mesh.iv_update) {
|
||||
k_delayed_work_submit(&bt_mesh.ivu_complete,
|
||||
BT_MESH_NET_IVU_TIMEOUT);
|
||||
}
|
||||
/* Set a timer to keep refreshing the stored duration */
|
||||
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
||||
|
||||
/* Make sure we have valid beacon data to be sent */
|
||||
bt_mesh_net_beacon_update(sub);
|
||||
|
@ -586,6 +582,8 @@ void bt_mesh_rpl_reset(void)
|
|||
void bt_mesh_iv_update_test(bool enable)
|
||||
{
|
||||
bt_mesh.ivu_test = enable;
|
||||
/* Clear the "unknown duration" variable - needed for some PTS tests */
|
||||
bt_mesh.ivu_unknown = 0;
|
||||
}
|
||||
|
||||
bool bt_mesh_iv_update(void)
|
||||
|
@ -620,13 +618,6 @@ void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub)
|
|||
}
|
||||
}
|
||||
|
||||
static void update_ivu_timestamp(void)
|
||||
{
|
||||
if (bt_mesh.last_update == BT_MESH_NET_IVU_UNKNOWN) {
|
||||
bt_mesh.last_update = k_uptime_get();
|
||||
}
|
||||
}
|
||||
|
||||
bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update)
|
||||
{
|
||||
int i;
|
||||
|
@ -643,7 +634,6 @@ bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update)
|
|||
if (iv_update) {
|
||||
/* Nothing to do */
|
||||
BT_DBG("Already in IV Update in Progress state");
|
||||
update_ivu_timestamp();
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
|
@ -651,7 +641,6 @@ bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update)
|
|||
|
||||
if (iv_index == bt_mesh.iv_index) {
|
||||
BT_DBG("Same IV Index in normal mode");
|
||||
update_ivu_timestamp();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -678,16 +667,13 @@ bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update)
|
|||
if (!iv_update) {
|
||||
/* Nothing to do */
|
||||
BT_DBG("Already in Normal state");
|
||||
update_ivu_timestamp();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (bt_mesh.last_update != BT_MESH_NET_IVU_UNKNOWN &&
|
||||
if (!bt_mesh.ivu_unknown &&
|
||||
!(IS_ENABLED(CONFIG_BT_MESH_IV_UPDATE_TEST) && bt_mesh.ivu_test)) {
|
||||
s64_t delta = k_uptime_get() - bt_mesh.last_update;
|
||||
|
||||
if (delta < K_HOURS(96)) {
|
||||
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
|
||||
BT_WARN("IV Update before minimum duration");
|
||||
return false;
|
||||
}
|
||||
|
@ -702,6 +688,8 @@ bool bt_mesh_net_iv_update(u32_t iv_index, bool iv_update)
|
|||
|
||||
do_update:
|
||||
bt_mesh.iv_update = iv_update;
|
||||
bt_mesh.ivu_duration = 0;
|
||||
bt_mesh.ivu_unknown = 0;
|
||||
|
||||
if (bt_mesh.iv_update) {
|
||||
bt_mesh.iv_index = iv_index;
|
||||
|
@ -709,21 +697,12 @@ do_update:
|
|||
bt_mesh.iv_index);
|
||||
|
||||
bt_mesh_rpl_reset();
|
||||
|
||||
k_delayed_work_submit(&bt_mesh.ivu_complete,
|
||||
BT_MESH_NET_IVU_TIMEOUT);
|
||||
} else {
|
||||
BT_DBG("Normal mode entered");
|
||||
bt_mesh.seq = 0;
|
||||
k_delayed_work_cancel(&bt_mesh.ivu_complete);
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
bt_mesh_store_seq();
|
||||
}
|
||||
}
|
||||
|
||||
/* Store time-stamp of the IV procedure state change */
|
||||
bt_mesh.last_update = k_uptime_get();
|
||||
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
|
||||
if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
|
||||
|
@ -732,7 +711,7 @@ do_update:
|
|||
}
|
||||
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
bt_mesh_store_iv();
|
||||
bt_mesh_store_iv(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1358,12 +1337,29 @@ void bt_mesh_net_recv(struct net_buf_simple *data, s8_t rssi,
|
|||
}
|
||||
}
|
||||
|
||||
static void ivu_complete(struct k_work *work)
|
||||
static void ivu_refresh(struct k_work *work)
|
||||
{
|
||||
BT_DBG("");
|
||||
bt_mesh.ivu_duration += BT_MESH_IVU_HOURS;
|
||||
|
||||
BT_DBG("%s for %u hour%s",
|
||||
bt_mesh.iv_update ? "IVU in Progress" : "IVU Normal mode",
|
||||
bt_mesh.ivu_duration, bt_mesh.ivu_duration == 1 ? "" : "s");
|
||||
|
||||
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
|
||||
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
bt_mesh_store_iv(true);
|
||||
}
|
||||
|
||||
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bt_mesh.iv_update) {
|
||||
bt_mesh_beacon_ivu_initiator(true);
|
||||
bt_mesh_net_iv_update(bt_mesh.iv_index, false);
|
||||
} else if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
|
||||
bt_mesh_store_iv(true);
|
||||
}
|
||||
}
|
||||
|
||||
void bt_mesh_net_start(void)
|
||||
|
@ -1400,7 +1396,7 @@ void bt_mesh_net_start(void)
|
|||
|
||||
void bt_mesh_net_init(void)
|
||||
{
|
||||
k_delayed_work_init(&bt_mesh.ivu_complete, ivu_complete);
|
||||
k_delayed_work_init(&bt_mesh.ivu_timer, ivu_refresh);
|
||||
|
||||
k_work_init(&bt_mesh.local_work, bt_mesh_net_local);
|
||||
}
|
||||
|
|
|
@ -17,18 +17,11 @@
|
|||
#define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01)
|
||||
#define BT_MESH_KEY_REFRESH(flags) (flags & 0x01)
|
||||
|
||||
/* Special time-stamp to indicate that we don't know when the last IV
|
||||
* Update happened.
|
||||
*/
|
||||
#define BT_MESH_NET_IVU_UNKNOWN -1
|
||||
|
||||
#if defined(CONFIG_BT_MESH_IV_UPDATE_TEST)
|
||||
/* Small test timeout for IV Update Procedure testing */
|
||||
#define BT_MESH_NET_IVU_TIMEOUT K_SECONDS(120)
|
||||
#else
|
||||
/* Maximum time to stay in IV Update mode (96 < time < 144) */
|
||||
#define BT_MESH_NET_IVU_TIMEOUT K_HOURS(120)
|
||||
#endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */
|
||||
/* How many hours in between updating IVU duration */
|
||||
#define BT_MESH_IVU_MIN_HOURS 96
|
||||
#define BT_MESH_IVU_HOURS (BT_MESH_IVU_MIN_HOURS / \
|
||||
CONFIG_BT_MESH_IVU_DIVIDER)
|
||||
#define BT_MESH_IVU_TIMEOUT K_HOURS(BT_MESH_IVU_HOURS)
|
||||
|
||||
struct bt_mesh_app_key {
|
||||
u16_t net_idx;
|
||||
|
@ -218,11 +211,10 @@ struct bt_mesh_net {
|
|||
iv_update:1, /* 1 if IV Update in Progress */
|
||||
ivu_initiator:1, /* IV Update initiated by us */
|
||||
ivu_test:1, /* IV Update test mode */
|
||||
ivu_unknown:1, /* Set to 1 right after provisioning */
|
||||
pending_update:1, /* Update blocked by SDU in progress */
|
||||
valid:1; /* 0 if unused */
|
||||
|
||||
s64_t last_update; /* Time since last IV Update change */
|
||||
|
||||
ATOMIC_DEFINE(flags, BT_MESH_FLAG_COUNT);
|
||||
|
||||
/* Local network interface */
|
||||
|
@ -238,8 +230,11 @@ struct bt_mesh_net {
|
|||
struct bt_mesh_lpn lpn; /* Low Power Node state */
|
||||
#endif
|
||||
|
||||
/* Timer to transition IV Update in Progress state */
|
||||
struct k_delayed_work ivu_complete;
|
||||
/* Number of hours in current IV Update state */
|
||||
u8_t ivu_duration;
|
||||
|
||||
/* Timer to track duration in current IV Update state */
|
||||
struct k_delayed_work ivu_timer;
|
||||
|
||||
u8_t dev_key[16];
|
||||
|
||||
|
|
|
@ -82,7 +82,8 @@ struct cfg_val {
|
|||
/* IV Index & IV Update storage */
|
||||
struct iv_val {
|
||||
u32_t iv_index;
|
||||
bool iv_update;
|
||||
u8_t iv_update:1,
|
||||
iv_duration:7;
|
||||
} __packed;
|
||||
|
||||
/* Replay Protection List storage */
|
||||
|
@ -184,9 +185,11 @@ static int iv_set(int argc, char **argv, char *val)
|
|||
|
||||
bt_mesh.iv_index = iv.iv_index;
|
||||
bt_mesh.iv_update = iv.iv_update;
|
||||
bt_mesh.ivu_duration = iv.iv_duration;
|
||||
bt_mesh.ivu_unknown = 0;
|
||||
|
||||
BT_DBG("IV Index 0x%04x (IV Update Flag %u)", bt_mesh.iv_index,
|
||||
bt_mesh.iv_update);
|
||||
BT_DBG("IV Index 0x%04x (IV Update Flag %u) duration %u hours",
|
||||
bt_mesh.iv_index, bt_mesh.iv_update, bt_mesh.ivu_duration);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -804,13 +807,8 @@ static int mesh_commit(void)
|
|||
}
|
||||
}
|
||||
|
||||
/* Set initial IV Update procedure state time-stamp */
|
||||
bt_mesh.last_update = BT_MESH_NET_IVU_UNKNOWN;
|
||||
|
||||
/* Set a timer to transition back to normal mode */
|
||||
if (bt_mesh.iv_update) {
|
||||
k_delayed_work_submit(&bt_mesh.ivu_complete,
|
||||
BT_MESH_NET_IVU_TIMEOUT);
|
||||
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
|
||||
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
|
||||
}
|
||||
|
||||
bt_mesh_model_foreach(commit_mod, NULL);
|
||||
|
@ -912,6 +910,7 @@ static void store_pending_iv(void)
|
|||
|
||||
iv.iv_index = bt_mesh.iv_index;
|
||||
iv.iv_update = bt_mesh.iv_update;
|
||||
iv.iv_duration = bt_mesh.ivu_duration;
|
||||
|
||||
str = settings_str_from_bytes(&iv, sizeof(iv), buf, sizeof(buf));
|
||||
if (!str) {
|
||||
|
@ -923,12 +922,15 @@ static void store_pending_iv(void)
|
|||
settings_save_one("bt/mesh/IV", str);
|
||||
}
|
||||
|
||||
void bt_mesh_store_iv(void)
|
||||
void bt_mesh_store_iv(bool only_duration)
|
||||
{
|
||||
schedule_store(BT_MESH_IV_PENDING);
|
||||
|
||||
if (!only_duration) {
|
||||
/* Always update Seq whenever IV changes */
|
||||
schedule_store(BT_MESH_SEQ_PENDING);
|
||||
}
|
||||
}
|
||||
|
||||
static void store_pending_seq(void)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
void bt_mesh_store_net(void);
|
||||
void bt_mesh_store_iv(void);
|
||||
void bt_mesh_store_iv(bool only_duration);
|
||||
void bt_mesh_store_seq(void);
|
||||
void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
|
||||
void bt_mesh_store_subnet(struct bt_mesh_subnet *sub);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue