diff --git a/nimble/host/mesh/include/mesh/main.h b/nimble/host/mesh/include/mesh/main.h index 8abb8f8de..515d1d527 100644 --- a/nimble/host/mesh/include/mesh/main.h +++ b/nimble/host/mesh/include/mesh/main.h @@ -281,6 +281,27 @@ int bt_mesh_init(u8_t own_addr_type, */ void bt_mesh_reset(void); +/** @brief Suspend the Mesh network temporarily. + * + * This API can be used for power saving purposes, but the user should be + * aware that leaving the local node suspended for a long period of time + * may cause it to become permanently disconnected from the Mesh network. + * If at all possible, the Friendship feature should be used instead, to + * make the node into a Low Power Node. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_suspend(void); + +/** @brief Resume a suspended Mesh network. + * + * This API resumes the local node, after it has been suspended using the + * bt_mesh_suspend() API. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_resume(void); + /** @brief Provision the local Mesh Node. * * This API should normally not be used directly by the application. The diff --git a/nimble/host/mesh/src/adv.c b/nimble/host/mesh/src/adv.c index 82c5c1bbf..6cb15ce6f 100644 --- a/nimble/host/mesh/src/adv.c +++ b/nimble/host/mesh/src/adv.c @@ -207,6 +207,11 @@ struct os_mbuf *bt_mesh_adv_create_from_pool(struct os_mbuf_pool *pool, struct bt_mesh_adv *adv; struct os_mbuf *buf; + if (atomic_test_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + BT_WARN("Refusing to allocate buffer while suspended"); + return NULL; + } + buf = os_mbuf_get_pkthdr(pool, BT_MESH_ADV_USER_DATA_SIZE); if (!buf) { return NULL; diff --git a/nimble/host/mesh/src/cfg_srv.c b/nimble/host/mesh/src/cfg_srv.c index f9f871ee6..faad75d8b 100644 --- a/nimble/host/mesh/src/cfg_srv.c +++ b/nimble/host/mesh/src/cfg_srv.c @@ -3574,6 +3574,13 @@ struct bt_mesh_hb_pub *bt_mesh_hb_pub_get(void) return &conf->hb_pub; } +void bt_mesh_hb_pub_disable(void) +{ + if (conf) { + hb_pub_disable(conf); + } +} + struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void) { return conf; diff --git a/nimble/host/mesh/src/foundation.h b/nimble/host/mesh/src/foundation.h index cd92c023f..2c257ee38 100644 --- a/nimble/host/mesh/src/foundation.h +++ b/nimble/host/mesh/src/foundation.h @@ -130,6 +130,7 @@ void bt_mesh_attention(struct bt_mesh_model *model, u8_t time); u8_t *bt_mesh_label_uuid_get(u16_t addr); 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); u8_t bt_mesh_net_transmit_get(void); diff --git a/nimble/host/mesh/src/mesh.c b/nimble/host/mesh/src/mesh.c index 82755b98c..538a2d278 100644 --- a/nimble/host/mesh/src/mesh.c +++ b/nimble/host/mesh/src/mesh.c @@ -206,6 +206,85 @@ static int bt_mesh_gap_event(struct ble_gap_event *event, void *arg) return 0; } +static void model_suspend(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + mod->pub->count = 0; + k_delayed_work_cancel(&mod->pub->timer); + } +} + +int bt_mesh_suspend(void) +{ + int err; + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (atomic_test_and_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_disable(); + if (err) { + atomic_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED); + BT_WARN("Disabling scanning failed (err %d)", err); + return err; + } + + bt_mesh_hb_pub_disable(); + + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_disable(); + } + + bt_mesh_model_foreach(model_suspend, NULL); + + return 0; +} + +static void model_resume(struct bt_mesh_model *mod, struct bt_mesh_elem *elem, + bool vnd, bool primary, void *user_data) +{ + if (mod->pub && mod->pub->update) { + s32_t period_ms = bt_mesh_model_pub_period_get(mod); + + if (period_ms) { + k_delayed_work_submit(&mod->pub->timer, period_ms); + } + } +} + +int bt_mesh_resume(void) +{ + int err; + + if (!atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + return -EINVAL; + } + + if (!atomic_test_and_clear_bit(bt_mesh.flags, BT_MESH_SUSPENDED)) { + return -EALREADY; + } + + err = bt_mesh_scan_enable(); + if (err) { + BT_WARN("Re-enabling scanning failed (err %d)", err); + atomic_set_bit(bt_mesh.flags, BT_MESH_SUSPENDED); + return err; + } + + if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED) { + bt_mesh_beacon_enable(); + } + + bt_mesh_model_foreach(model_resume, NULL); + + return err; +} + int bt_mesh_init(uint8_t own_addr_type, const struct bt_mesh_prov *prov, const struct bt_mesh_comp *comp) { diff --git a/nimble/host/mesh/src/net.h b/nimble/host/mesh/src/net.h index aec11500c..e55102c06 100644 --- a/nimble/host/mesh/src/net.h +++ b/nimble/host/mesh/src/net.h @@ -202,6 +202,7 @@ struct bt_mesh_lpn { /* bt_mesh_net.flags */ enum { BT_MESH_VALID, /* We have been provisioned */ + BT_MESH_SUSPENDED, /* Network is temporarily suspended */ BT_MESH_IVU_IN_PROGRESS, /* IV Update in Progress */ BT_MESH_IVU_INITIATOR, /* IV Update initiated by us */ BT_MESH_IVU_TEST, /* IV Update test mode */