499 lines
11 KiB
C
499 lines
11 KiB
C
/*
|
|
* 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 IS_ENABLED(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 !IS_ENABLED(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();
|
|
}
|
|
}
|