/* * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "mesh.h" #include "net.h" #include "rpl.h" #include "beacon.h" #include "settings.h" #include "heartbeat.h" #include "friend.h" #include "cfg.h" #include "od_priv_proxy.h" #include "priv_beacon.h" #define LOG_LEVEL CONFIG_BT_MESH_CFG_LOG_LEVEL #include LOG_MODULE_REGISTER(bt_mesh_cfg); /* Miscellaneous configuration server model states */ struct cfg_val { uint8_t net_transmit; uint8_t relay; uint8_t relay_retransmit; uint8_t beacon; uint8_t gatt_proxy; uint8_t frnd; uint8_t default_ttl; }; void bt_mesh_beacon_set(bool beacon) { if (atomic_test_bit(bt_mesh.flags, BT_MESH_BEACON) == beacon) { return; } atomic_set_bit_to(bt_mesh.flags, BT_MESH_BEACON, beacon); if (beacon) { bt_mesh_beacon_enable(); } else { /* Beacon timer will stop automatically when all beacons are disabled. */ } if (IS_ENABLED(CONFIG_BT_SETTINGS) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); } } bool bt_mesh_beacon_enabled(void) { return atomic_test_bit(bt_mesh.flags, BT_MESH_BEACON); } static int feature_set(int feature_flag, enum bt_mesh_feat_state state) { if (state != BT_MESH_FEATURE_DISABLED && state != BT_MESH_FEATURE_ENABLED) { return -EINVAL; } if (atomic_test_bit(bt_mesh.flags, feature_flag) == (state == BT_MESH_FEATURE_ENABLED)) { return -EALREADY; } atomic_set_bit_to(bt_mesh.flags, feature_flag, (state == BT_MESH_FEATURE_ENABLED)); return 0; } static enum bt_mesh_feat_state feature_get(int feature_flag) { return atomic_test_bit(bt_mesh.flags, feature_flag) ? BT_MESH_FEATURE_ENABLED : BT_MESH_FEATURE_DISABLED; } int bt_mesh_priv_beacon_set(enum bt_mesh_feat_state priv_beacon) { int err; if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { return -ENOTSUP; } err = feature_set(BT_MESH_PRIV_BEACON, priv_beacon); if (err) { return err; } if (priv_beacon == BT_MESH_FEATURE_ENABLED) { bt_mesh_beacon_enable(); } else { /* Beacon timer will stop automatically when all beacons are disabled. */ } if (IS_ENABLED(CONFIG_BT_SETTINGS) && IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACON_SRV) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_priv_beacon_srv_store_schedule(); } return 0; } enum bt_mesh_feat_state bt_mesh_priv_beacon_get(void) { if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { return BT_MESH_FEATURE_NOT_SUPPORTED; } return feature_get(BT_MESH_PRIV_BEACON); } void bt_mesh_priv_beacon_update_interval_set(uint8_t interval) { #if defined(CONFIG_BT_MESH_PRIV_BEACONS) bt_mesh.priv_beacon_int = interval; #endif } uint8_t bt_mesh_priv_beacon_update_interval_get(void) { #if defined(CONFIG_BT_MESH_PRIV_BEACONS) return bt_mesh.priv_beacon_int; #else return 0; #endif } int bt_mesh_od_priv_proxy_get(void) { #if defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) return bt_mesh.on_demand_state; #else return -ENOTSUP; #endif } int bt_mesh_od_priv_proxy_set(uint8_t on_demand_proxy) { #if !defined(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) return -ENOTSUP; #else if (bt_mesh_priv_gatt_proxy_get() != BT_MESH_FEATURE_NOT_SUPPORTED) { bt_mesh.on_demand_state = on_demand_proxy; } if (IS_ENABLED(CONFIG_BT_SETTINGS) && IS_ENABLED(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_od_priv_proxy_srv_store_schedule(); } return 0; #endif } static bool node_id_is_running(struct bt_mesh_subnet *sub, void *cb_data) { return sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING; } int bt_mesh_gatt_proxy_set(enum bt_mesh_feat_state gatt_proxy) { int err; if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { return -ENOTSUP; } err = feature_set(BT_MESH_GATT_PROXY, gatt_proxy); if (err) { return err; } /* The binding from section 4.2.45.1 disables Private GATT Proxy state when non-private * state is enabled. */ if (gatt_proxy == BT_MESH_FEATURE_ENABLED) { feature_set(BT_MESH_PRIV_GATT_PROXY, BT_MESH_FEATURE_DISABLED); } if ((gatt_proxy == BT_MESH_FEATURE_ENABLED) || (gatt_proxy == BT_MESH_FEATURE_DISABLED && !bt_mesh_subnet_find(node_id_is_running, NULL))) { bt_mesh_adv_gatt_update(); } bt_mesh_hb_feature_changed(BT_MESH_FEAT_PROXY); if (IS_ENABLED(CONFIG_BT_SETTINGS) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); } return 0; } enum bt_mesh_feat_state bt_mesh_gatt_proxy_get(void) { if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) { return BT_MESH_FEATURE_NOT_SUPPORTED; } return feature_get(BT_MESH_GATT_PROXY); } int bt_mesh_priv_gatt_proxy_set(enum bt_mesh_feat_state priv_gatt_proxy) { int err; if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) || !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { return BT_MESH_FEATURE_NOT_SUPPORTED; } /* Reverse binding from section 4.2.45.1 doesn't allow to enable private state if * non-private state is enabled. */ if (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) { return BT_MESH_FEATURE_DISABLED; } err = feature_set(BT_MESH_PRIV_GATT_PROXY, priv_gatt_proxy); if (err) { return err; } if (priv_gatt_proxy == BT_MESH_FEATURE_ENABLED) { /* Re-generate proxy beacon */ bt_mesh_adv_gatt_update(); } if (IS_ENABLED(CONFIG_BT_SETTINGS) && IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACON_SRV) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_priv_beacon_srv_store_schedule(); } return 0; } enum bt_mesh_feat_state bt_mesh_priv_gatt_proxy_get(void) { if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) || !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { return BT_MESH_FEATURE_NOT_SUPPORTED; } return feature_get(BT_MESH_PRIV_GATT_PROXY); } int bt_mesh_default_ttl_set(uint8_t default_ttl) { if (default_ttl == 1 || default_ttl > BT_MESH_TTL_MAX) { return -EINVAL; } if (default_ttl == bt_mesh.default_ttl) { return 0; } bt_mesh.default_ttl = default_ttl; if (IS_ENABLED(CONFIG_BT_SETTINGS) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); } return 0; } uint8_t bt_mesh_default_ttl_get(void) { return bt_mesh.default_ttl; } int bt_mesh_friend_set(enum bt_mesh_feat_state friendship) { int err; if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { return -ENOTSUP; } err = feature_set(BT_MESH_FRIEND, friendship); if (err) { return err; } bt_mesh_hb_feature_changed(BT_MESH_FEAT_FRIEND); if (IS_ENABLED(CONFIG_BT_SETTINGS) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); } if (friendship == BT_MESH_FEATURE_DISABLED) { bt_mesh_friends_clear(); } return 0; } enum bt_mesh_feat_state bt_mesh_friend_get(void) { if (!IS_ENABLED(CONFIG_BT_MESH_FRIEND)) { return BT_MESH_FEATURE_NOT_SUPPORTED; } return feature_get(BT_MESH_FRIEND); } void bt_mesh_net_transmit_set(uint8_t xmit) { if (bt_mesh.net_xmit == xmit) { return; } bt_mesh.net_xmit = xmit; if (IS_ENABLED(CONFIG_BT_SETTINGS) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); } } uint8_t bt_mesh_net_transmit_get(void) { return bt_mesh.net_xmit; } int bt_mesh_relay_set(enum bt_mesh_feat_state relay, uint8_t xmit) { int err; if (!IS_ENABLED(CONFIG_BT_MESH_RELAY)) { return -ENOTSUP; } err = feature_set(BT_MESH_RELAY, relay); if (err == -EINVAL) { return err; } if (err == -EALREADY && bt_mesh.relay_xmit == xmit) { return -EALREADY; } bt_mesh.relay_xmit = xmit; bt_mesh_hb_feature_changed(BT_MESH_FEAT_RELAY); if (IS_ENABLED(CONFIG_BT_SETTINGS) && atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); } return 0; } enum bt_mesh_feat_state bt_mesh_relay_get(void) { return feature_get(BT_MESH_RELAY); } uint8_t bt_mesh_relay_retransmit_get(void) { if (!IS_ENABLED(CONFIG_BT_MESH_RELAY)) { return 0; } return bt_mesh.relay_xmit; } bool bt_mesh_fixed_group_match(uint16_t addr) { /* Check for fixed group addresses */ switch (addr) { case BT_MESH_ADDR_ALL_NODES: return true; case BT_MESH_ADDR_PROXIES: return (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED); case BT_MESH_ADDR_FRIENDS: return (bt_mesh_friend_get() == BT_MESH_FEATURE_ENABLED); case BT_MESH_ADDR_RELAYS: return (bt_mesh_relay_get() == BT_MESH_FEATURE_ENABLED); default: return false; } } void bt_mesh_cfg_default_set(void) { bt_mesh.default_ttl = CONFIG_BT_MESH_DEFAULT_TTL; bt_mesh.net_xmit = BT_MESH_TRANSMIT(CONFIG_BT_MESH_NETWORK_TRANSMIT_COUNT, CONFIG_BT_MESH_NETWORK_TRANSMIT_INTERVAL); #if defined(CONFIG_BT_MESH_RELAY) bt_mesh.relay_xmit = BT_MESH_TRANSMIT(CONFIG_BT_MESH_RELAY_RETRANSMIT_COUNT, CONFIG_BT_MESH_RELAY_RETRANSMIT_INTERVAL); #endif if (IS_ENABLED(CONFIG_BT_MESH_RELAY_ENABLED)) { atomic_set_bit(bt_mesh.flags, BT_MESH_RELAY); } if (IS_ENABLED(CONFIG_BT_MESH_BEACON_ENABLED)) { atomic_set_bit(bt_mesh.flags, BT_MESH_BEACON); } if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY_ENABLED)) { atomic_set_bit(bt_mesh.flags, BT_MESH_GATT_PROXY); } if (IS_ENABLED(CONFIG_BT_MESH_FRIEND_ENABLED)) { atomic_set_bit(bt_mesh.flags, BT_MESH_FRIEND); } } static int cfg_set(const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg) { struct cfg_val cfg; int err; if (len_rd == 0) { LOG_DBG("Cleared configuration state"); return 0; } err = bt_mesh_settings_set(read_cb, cb_arg, &cfg, sizeof(cfg)); if (err) { LOG_ERR("Failed to set \'cfg\'"); return err; } bt_mesh_net_transmit_set(cfg.net_transmit); bt_mesh_relay_set(cfg.relay, cfg.relay_retransmit); bt_mesh_beacon_set(cfg.beacon); bt_mesh_gatt_proxy_set(cfg.gatt_proxy); bt_mesh_friend_set(cfg.frnd); bt_mesh_default_ttl_set(cfg.default_ttl); LOG_DBG("Restored configuration state"); return 0; } BT_MESH_SETTINGS_DEFINE(cfg, "Cfg", cfg_set); static void clear_cfg(void) { int err; err = settings_delete("bt/mesh/Cfg"); if (err) { LOG_ERR("Failed to clear configuration (err: %d)", err); } else { LOG_DBG("Cleared configuration"); } } static void store_pending_cfg(void) { struct cfg_val val; int err; val.net_transmit = bt_mesh_net_transmit_get(); val.relay = bt_mesh_relay_get(); val.relay_retransmit = bt_mesh_relay_retransmit_get(); val.beacon = bt_mesh_beacon_enabled(); val.gatt_proxy = bt_mesh_gatt_proxy_get(); val.frnd = bt_mesh_friend_get(); val.default_ttl = bt_mesh_default_ttl_get(); err = settings_save_one("bt/mesh/Cfg", &val, sizeof(val)); if (err) { LOG_ERR("Failed to store configuration value"); } else { LOG_DBG("Stored configuration value"); LOG_HEXDUMP_DBG(&val, sizeof(val), "raw value"); } } void bt_mesh_cfg_pending_store(void) { if (atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { store_pending_cfg(); } else { clear_cfg(); } }