From 0e3d208469dea361ca971c62f76d11974754cf9e Mon Sep 17 00:00:00 2001 From: Aleksandr Khromykh Date: Tue, 13 Feb 2024 15:13:18 +0100 Subject: [PATCH] Bluetooth: Mesh: restore ttl in Time model after unsolicited statuses Commit fixes the known issue when the Time model changes the configured publishing ttl after sending any unsolicited Time status. Signed-off-by: Aleksandr Khromykh --- .../releases/release-notes-changelog.rst | 1 + include/bluetooth/mesh/time_srv.h | 5 +++ subsys/bluetooth/mesh/time_srv.c | 24 ++++++++++- .../bluetooth/mesh/time_model/src/main.c | 43 ++++++++++++++++++- 4 files changed, 71 insertions(+), 2 deletions(-) diff --git a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst index 1dcac5d3bb74..fedc6fd9ebe5 100644 --- a/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst +++ b/doc/nrf/releases_and_maturity/releases/release-notes-changelog.rst @@ -85,6 +85,7 @@ Bluetooth Mesh * :ref:`bt_mesh_ug_reserved_ids` with model ID and opcodes for the new :ref:`bt_mesh_le_pair_resp_readme` model. * :ref:`bt_mesh_light_ctrl_readme` APIs to match new Sensor APIs. * :ref:`ug_bt_mesh_configuring` with the recommendation on how to configure persistent storage to increase performance. + * Fixed an issue when the Time Server model rewrites TTL to zero forever during the unsolicited Time Status publication. Matter ------ diff --git a/include/bluetooth/mesh/time_srv.h b/include/bluetooth/mesh/time_srv.h index 079209fe0e0f..126f34cfd754 100644 --- a/include/bluetooth/mesh/time_srv.h +++ b/include/bluetooth/mesh/time_srv.h @@ -107,6 +107,11 @@ struct bt_mesh_time_srv { struct bt_mesh_time_srv_data data; /** Delayable work to randomize status relaying. */ struct k_work_delayable status_delay; + /** Property whether the Time status was unsolicited. */ + bool is_unsolicited; + /** Cached publishing TTL while server is sending the unsolicited Time Status with zero TTL. + */ + uint8_t cached_ttl; /** @brief Update callback. * diff --git a/subsys/bluetooth/mesh/time_srv.c b/subsys/bluetooth/mesh/time_srv.c index a3132a105e6b..4fbfada67c7a 100644 --- a/subsys/bluetooth/mesh/time_srv.c +++ b/subsys/bluetooth/mesh/time_srv.c @@ -416,6 +416,8 @@ static int bt_mesh_time_srv_init(const struct bt_mesh_model *model) srv->model = model; srv->data.timestamp = -STATUS_INTERVAL_MIN; net_buf_simple_init(srv->pub.msg, 0); + srv->is_unsolicited = false; + srv->cached_ttl = 0; k_work_init_delayable(&srv->status_delay, time_status_send_after_delay); @@ -429,6 +431,8 @@ static void bt_mesh_time_srv_reset(const struct bt_mesh_model *model) srv->data = data; net_buf_simple_reset(srv->pub.msg); + srv->is_unsolicited = false; + srv->cached_ttl = 0; (void)k_work_cancel_delayable(&srv->status_delay); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { @@ -489,6 +493,7 @@ const struct bt_mesh_model_cb _bt_mesh_time_setup_srv_cb = { int _bt_mesh_time_srv_update_handler(const struct bt_mesh_model *model) { struct bt_mesh_time_srv *srv = model->rt->user_data; + struct bt_mesh_model_pub *pub = srv->model->pub; struct bt_mesh_time_status status; int64_t uptime; int err; @@ -504,6 +509,15 @@ int _bt_mesh_time_srv_update_handler(const struct bt_mesh_model *model) return err; } + /* If sent as an unsolicited message, the Time Status message shall be sent + * with TTL=0 to avoid building up cumulative time errors resulting from delays + * in processing the messages by relays. + */ + if (!bt_mesh_model_pub_is_retransmission(pub->mod) && srv->is_unsolicited) { + pub->ttl = srv->cached_ttl; + srv->is_unsolicited = false; + } + srv->data.timestamp = uptime; /* Account for delay in TX processing: */ status.uncertainty += CONFIG_BT_MESH_TIME_MESH_HOP_UNCERTAINTY; @@ -517,6 +531,7 @@ int _bt_mesh_time_srv_update_handler(const struct bt_mesh_model *model) int bt_mesh_time_srv_time_status_send(struct bt_mesh_time_srv *srv, struct bt_mesh_msg_ctx *ctx) { + struct bt_mesh_model_pub *pub = srv->model->pub; int64_t uptime = k_uptime_get(); int err; @@ -528,7 +543,14 @@ int bt_mesh_time_srv_time_status_send(struct bt_mesh_time_srv *srv, return -EOPNOTSUPP; } - srv->model->pub->ttl = 0; + if (ctx) { + ctx->send_ttl = 0; + ctx->rnd_delay = false; + } else { + srv->cached_ttl = srv->is_unsolicited ? srv->cached_ttl : pub->ttl; + srv->is_unsolicited = true; + pub->ttl = 0; + } err = send_time_status(srv->model, ctx, uptime); if (!err) { diff --git a/tests/subsys/bluetooth/mesh/time_model/src/main.c b/tests/subsys/bluetooth/mesh/time_model/src/main.c index c4158b6565d7..ec813e7a92c6 100644 --- a/tests/subsys/bluetooth/mesh/time_model/src/main.c +++ b/tests/subsys/bluetooth/mesh/time_model/src/main.c @@ -40,6 +40,7 @@ static struct bt_mesh_time_status expected_status; static uint32_t time_status_rx_number; static bool is_randomized; static bool is_unsolicited; +static bool is_skipped; static void tc_setup(void *f) { @@ -50,7 +51,12 @@ static void tc_setup(void *f) .tm_hour = 0, .tm_min = 0, .tm_sec = 0, }; + is_randomized = false; + is_unsolicited = false; + is_skipped = false; time_status_rx_number = 0; + mock_elem[0].models->pub->mod = mock_elem[0].models; + mock_elem[0].models->pub->ttl = 10; zassert_not_null(_bt_mesh_time_srv_cb.init, "Init cb is null"); _bt_mesh_time_srv_cb.init(mock_elem[0].models); @@ -91,6 +97,10 @@ int bt_mesh_msg_send(const struct bt_mesh_model *model, struct bt_mesh_msg_ctx * zassert_equal_ptr(time_srv.model, model); + if (is_skipped) { + return 0; + } + if (is_randomized) { zassert_is_null(ctx); } else { @@ -185,7 +195,6 @@ ZTEST(time_model, test_time_set_timing) { int64_t start_time = k_uptime_get(); - is_randomized = false; is_unsolicited = true; while (k_uptime_get() - start_time < TEST_TIME) { @@ -247,4 +256,36 @@ ZTEST(time_model, test_time_periodic_pub_status_mix) zassert_equal(TEST_TIME / STATUS_INTERVAL_MIN, time_status_rx_number); } +/** Test scenario: update handler of the Time model is called once + * (emulation of publication by timer). TTL should be equal configured value. + * Unsolicited Time Status is published. TTL should be 0. + * Unsolicited Time Status is published again. TTL is still 0. + * Update handler is called. TTL should go back to the configured value. + * Update handler is called again. TTL should be still equal the configured value. + */ +ZTEST(time_model, test_time_pub_ttl) +{ + struct bt_mesh_model_pub *pub = mock_elem[0].models->pub; + + is_skipped = true; + + zassert_equal(10, pub->ttl); + zassert_ok(_bt_mesh_time_srv_update_handler(mock_elem[0].models)); + zassert_equal(10, pub->ttl); + + net_buf_simple_reset(pub->msg); + zassert_ok(bt_mesh_time_srv_time_status_send(&time_srv, NULL)); + zassert_equal(0, pub->ttl); + net_buf_simple_reset(pub->msg); + zassert_ok(bt_mesh_time_srv_time_status_send(&time_srv, NULL)); + zassert_equal(0, pub->ttl); + + net_buf_simple_reset(pub->msg); + zassert_ok(_bt_mesh_time_srv_update_handler(mock_elem[0].models)); + zassert_equal(10, pub->ttl); + net_buf_simple_reset(pub->msg); + zassert_ok(_bt_mesh_time_srv_update_handler(mock_elem[0].models)); + zassert_equal(10, pub->ttl); +} + ZTEST_SUITE(time_model, NULL, NULL, tc_setup, tc_teardown, NULL);