| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/bluetooth/mesh.h> |
| #include <zephyr/bluetooth/bluetooth.h> |
| |
| #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 <zephyr/logging/log.h> |
| 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(); |
| } |
| } |