Skip to content

Commit

Permalink
Bluetooth: Mesh: Adds subnet bridge states
Browse files Browse the repository at this point in the history
The `brg_cfg` module implements the states needed for subnet bridge
feature. It provides two states - enable state, and bridging table
state. APIs are provided to access and modify the states. The module
handles responsibility of persistence of the states.

Signed-off-by: Omkar Kulkarni <omkar.kulkarni@nordicsemi.no>
  • Loading branch information
omkar3141 authored and nashif committed Sep 17, 2024
1 parent 709c006 commit 092f808
Show file tree
Hide file tree
Showing 12 changed files with 951 additions and 2 deletions.
4 changes: 4 additions & 0 deletions subsys/bluetooth/Kconfig.logging
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,10 @@ module = BT_MESH_NET
module-str = "Network layer"
source "subsys/logging/Kconfig.template.log_config_inherit"

module = BT_MESH_BRG
module-str = "Subnet Bridging layer"
source "subsys/logging/Kconfig.template.log_config_inherit"

module = BT_MESH_RPL
module-str = "Replay protection list"
source "subsys/logging/Kconfig.template.log_config_inherit"
Expand Down
2 changes: 1 addition & 1 deletion subsys/bluetooth/mesh/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_OD_PRIV_PROXY_SRV sol_pdu_rpl_srv.c)

zephyr_library_sources_ifdef(CONFIG_BT_MESH_BRG_CFG_CLI brg_cfg_cli.c)

zephyr_library_sources_ifdef(CONFIG_BT_MESH_BRG_CFG_SRV brg_cfg_srv.c)
zephyr_library_sources_ifdef(CONFIG_BT_MESH_BRG_CFG_SRV brg_cfg_srv.c brg_cfg.c)

zephyr_library_sources_ifdef(CONFIG_BT_MESH_SOLICITATION solicitation.c)

Expand Down
12 changes: 12 additions & 0 deletions subsys/bluetooth/mesh/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -1274,6 +1274,18 @@ config BT_MESH_BRG_CFG_SRV
The Bridge Configuration Server model is used to support the configuration
of the subnet bridge functionality of a node.

menu "Subnet Bridge configuration"
visible if BT_MESH_BRG_CFG_SRV

config BT_MESH_BRG_TABLE_ITEMS_MAX
int "Maximum number of entries in the bridging table"
default 16
range 16 255
help
The maximum number of entries in the bridging table.

endmenu

config BT_MESH_BRG_CFG_CLI
bool "Support for Bridge Configuration Client model"
help
Expand Down
307 changes: 307 additions & 0 deletions subsys/bluetooth/mesh/brg_cfg.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,307 @@
/*
* Copyright (c) 2024 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

/* Implementation for states of Subnet Bridge feature in Bluetooth Mesh Protocol v1.1
* specification
*/
#include <errno.h>
#include <zephyr/bluetooth/mesh.h>

#include "mesh.h"
#include "net.h"
#include "settings.h"
#include "brg_cfg.h"

#define LOG_LEVEL CONFIG_BT_MESH_BRG_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_brg_cfg);

/* Bridging table state and counter */
static struct bt_mesh_brg_cfg_row brg_tbl[CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX];
static uint32_t bt_mesh_brg_cfg_row_cnt;
/* Bridging enabled state */
static bool brg_enabled;

static void brg_tbl_compact(void)
{
int j = 0;

for (int k = 0; k < bt_mesh_brg_cfg_row_cnt; k++) {
if (brg_tbl[k].direction != 0) {
brg_tbl[j] = brg_tbl[k];
j++;
}
}
memset(&brg_tbl[j], 0, sizeof(brg_tbl[j]));
bt_mesh_brg_cfg_row_cnt--;
}

#if IS_ENABLED(CONFIG_BT_SETTINGS)
/* Set function for initializing bridging enable state from value stored in settings. */
static int brg_en_set(const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg)
{
int err;

if (len_rd == 0) {
brg_enabled = 0;
LOG_DBG("Cleared bridge enable state");
return 0;
}

err = bt_mesh_settings_set(read_cb, cb_arg, &brg_enabled, sizeof(brg_enabled));
if (err) {
LOG_ERR("Failed to set bridge enable state");
return err;
}

LOG_DBG("Restored bridge enable state");

return 0;
}

/* Define a setting for storing enable state */
BT_MESH_SETTINGS_DEFINE(brg_en, "brg_en", brg_en_set);

/* Set function for initializing bridging table rows from values stored in settings. */
static int brg_tbl_set(const char *name, size_t len_rd, settings_read_cb read_cb, void *cb_arg)
{
if (len_rd == 0) {
memset(brg_tbl, 0, sizeof(brg_tbl));
bt_mesh_brg_cfg_row_cnt = 0;
LOG_DBG("Cleared bridging table entries");
return 0;
}

int err = bt_mesh_settings_set(read_cb, cb_arg, brg_tbl, sizeof(brg_tbl));

if (err) {
LOG_ERR("Failed to set bridging table entries");
return err;
}

LOG_DBG("Restored bridging table");

return 0;
}

/* Define a setting for storing briging table rows */
BT_MESH_SETTINGS_DEFINE(brg_tbl, "brg_tbl", brg_tbl_set);
#endif

bool bt_mesh_brg_cfg_enable_get(void)
{
return brg_enabled;
}

int bt_mesh_brg_cfg_enable_set(bool enable)
{
if (brg_enabled == enable) {
return 0;
}

brg_enabled = enable;
#if IS_ENABLED(CONFIG_BT_SETTINGS)
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
#endif
return 0;
}

void bt_mesh_brg_cfg_pending_store(void)
{
#if CONFIG_BT_SETTINGS
char *path_en = "bt/mesh/brg_en";
char *path_tbl = "bt/mesh/brg_tbl";
int err;

if (brg_enabled) {
err = settings_save_one(path_en, &brg_enabled, sizeof(brg_enabled));
} else {
err = settings_delete(path_en);
}

if (err) {
LOG_ERR("Failed to store %s value", path_en);
}


if (bt_mesh_brg_cfg_row_cnt) {
err = settings_save_one(path_tbl, &brg_tbl,
bt_mesh_brg_cfg_row_cnt * sizeof(brg_tbl[0]));
} else {
err = settings_delete(path_tbl);
}

if (err) {
LOG_ERR("Failed to store %s value", path_tbl);
}
#endif
}

/* Remove the entry from the bridging table that corresponds with the NetKey Index of the removed
* subnet.
*/
static void brg_tbl_netkey_removed_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
if (evt != BT_MESH_KEY_DELETED) {
return;
}

for (int i = 0; i < CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX; i++) {
if (brg_tbl[i].direction && (
brg_tbl[i].net_idx1 == sub->net_idx ||
brg_tbl[i].net_idx2 == sub->net_idx)) {
memset(&brg_tbl[i], 0, sizeof(brg_tbl[i]));
brg_tbl_compact();
}
}

#if IS_ENABLED(CONFIG_BT_SETTINGS)
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
#endif
}

/* Add event hook for key deletion event */
BT_MESH_SUBNET_CB_DEFINE(sbr) = {
.evt_handler = brg_tbl_netkey_removed_evt,
};

int bt_mesh_brg_cfg_tbl_reset(void)
{
int err = 0;

brg_enabled = false;
bt_mesh_brg_cfg_row_cnt = 0;
memset(brg_tbl, 0, sizeof(brg_tbl));

#if CONFIG_BT_SETTINGS
err = settings_delete("bt/mesh/brg_en");

if (err) {
return err;
}

err = settings_delete("bt/mesh/brg_tbl");
#endif
return err;
}

int bt_mesh_brg_cfg_tbl_get(const struct bt_mesh_brg_cfg_row **rows)
{
*rows = brg_tbl;
return bt_mesh_brg_cfg_row_cnt;
}

int bt_mesh_brg_cfg_tbl_add(enum bt_mesh_brg_cfg_dir direction, uint16_t net_idx1,
uint16_t net_idx2, uint16_t addr1, uint16_t addr2)
{
/* Sanity checks */
if (!BT_MESH_ADDR_IS_UNICAST(addr1) || net_idx1 == net_idx2 || addr1 == addr2 ||
net_idx1 > 0x03FF || net_idx2 > 0x03FF) {
return -EINVAL;
}

if (direction != BT_MESH_BRG_CFG_DIR_ONEWAY && direction != BT_MESH_BRG_CFG_DIR_TWOWAY) {
return -EINVAL;
}

if ((direction == BT_MESH_BRG_CFG_DIR_ONEWAY &&
(addr2 == BT_MESH_ADDR_UNASSIGNED || addr2 == BT_MESH_ADDR_ALL_NODES)) ||
(direction == BT_MESH_BRG_CFG_DIR_TWOWAY &&
!BT_MESH_ADDR_IS_UNICAST(addr2))) {
return -EINVAL;
}

/* Check if entry already exists, if yes, then, update the direction field and it is a
* success.
* "If a Bridging Table state entry corresponding to the received message exists, the
* element shall set the Directions field in the entry to the value of the Directions field
* in the received message."
*/
for (int i = 0; i < bt_mesh_brg_cfg_row_cnt; i++) {
if (brg_tbl[i].net_idx1 == net_idx1 &&
brg_tbl[i].net_idx2 == net_idx2 && brg_tbl[i].addr1 == addr1 &&
brg_tbl[i].addr2 == addr2) {
brg_tbl[i].direction = direction;
goto store;
}
}

/* Empty element, is the current table row counter */
if (bt_mesh_brg_cfg_row_cnt == CONFIG_BT_MESH_BRG_TABLE_ITEMS_MAX) {
return -ENOMEM;
}

/* Update the row */
brg_tbl[bt_mesh_brg_cfg_row_cnt].direction = direction;
brg_tbl[bt_mesh_brg_cfg_row_cnt].net_idx1 = net_idx1;
brg_tbl[bt_mesh_brg_cfg_row_cnt].net_idx2 = net_idx2;
brg_tbl[bt_mesh_brg_cfg_row_cnt].addr1 = addr1;
brg_tbl[bt_mesh_brg_cfg_row_cnt].addr2 = addr2;
bt_mesh_brg_cfg_row_cnt++;

store:
#if IS_ENABLED(CONFIG_BT_SETTINGS)
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
#endif

return 0;
}

void bt_mesh_brg_cfg_tbl_foreach_subnet(uint16_t src, uint16_t dst, uint16_t net_idx,
bt_mesh_brg_cfg_cb_t cb, void *user_data)
{
for (int i = 0; i < bt_mesh_brg_cfg_row_cnt; i++) {
if ((brg_tbl[i].direction == BT_MESH_BRG_CFG_DIR_ONEWAY ||
brg_tbl[i].direction == BT_MESH_BRG_CFG_DIR_TWOWAY) &&
brg_tbl[i].net_idx1 == net_idx && brg_tbl[i].addr1 == src &&
brg_tbl[i].addr2 == dst) {
cb(brg_tbl[i].net_idx2, user_data);
} else if ((brg_tbl[i].direction == BT_MESH_BRG_CFG_DIR_TWOWAY &&
brg_tbl[i].net_idx2 == net_idx && brg_tbl[i].addr2 == src &&
brg_tbl[i].addr1 == dst)) {
cb(brg_tbl[i].net_idx1, user_data);
}
}
}

void bt_mesh_brg_cfg_tbl_remove(uint16_t net_idx1, uint16_t net_idx2, uint16_t addr1,
uint16_t addr2)
{
#if IS_ENABLED(CONFIG_BT_SETTINGS)
bool store = false;
#endif

/* Iterate over items and set matching row to 0, if nothing exist, or nothing matches, then
* it is success (similar to add)
*/
if (bt_mesh_brg_cfg_row_cnt == 0) {
return;
}

for (int i = 0; i < bt_mesh_brg_cfg_row_cnt; i++) {
/* Match according to remove behavior in Section 4.4.9.2.2 of MshPRT_v1.1 */
if (brg_tbl[i].direction) {
if (!(brg_tbl[i].net_idx1 == net_idx1 && brg_tbl[i].net_idx2 == net_idx2)) {
continue;
}

if ((brg_tbl[i].addr1 == addr1 && brg_tbl[i].addr2 == addr2) ||
(addr2 == BT_MESH_ADDR_UNASSIGNED && brg_tbl[i].addr1 == addr1) ||
(addr1 == BT_MESH_ADDR_UNASSIGNED && brg_tbl[i].addr2 == addr2)) {
memset(&brg_tbl[i], 0, sizeof(brg_tbl[i]));
brg_tbl_compact();
#if IS_ENABLED(CONFIG_BT_SETTINGS)
store = true;
#endif
}
}
}
#if IS_ENABLED(CONFIG_BT_SETTINGS)
if (store) {
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_BRG_PENDING);
}
#endif
}
Loading

0 comments on commit 092f808

Please sign in to comment.