mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-24 12:44:19 +08:00
31ce1e9a5b
req is allocated memory that must be freed. It appears all other error paths got fail which handles this. g_new0 allocates memory to data which must be freed. g_malloc0 allocates memory to comp which must be freed.
889 lines
17 KiB
C
889 lines
17 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2017 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <sys/uio.h>
|
|
#include <wordexp.h>
|
|
#include <inttypes.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#include "src/shared/util.h"
|
|
#include "src/shared/shell.h"
|
|
#include "gdbus/gdbus.h"
|
|
|
|
#include "tools/mesh/config-model.h"
|
|
|
|
#include "tools/mesh-gatt/mesh-net.h"
|
|
#include "tools/mesh-gatt/node.h"
|
|
#include "tools/mesh-gatt/keys.h"
|
|
#include "tools/mesh-gatt/gatt.h"
|
|
#include "tools/mesh-gatt/net.h"
|
|
#include "tools/mesh-gatt/prov-db.h"
|
|
#include "tools/mesh-gatt/util.h"
|
|
|
|
struct mesh_model {
|
|
struct mesh_model_ops cbs;
|
|
void *user_data;
|
|
GList *bindings;
|
|
GList *subscriptions;
|
|
uint32_t id;
|
|
struct mesh_publication *pub;
|
|
};
|
|
|
|
struct mesh_element {
|
|
GList *models;
|
|
uint16_t loc;
|
|
uint8_t index;
|
|
};
|
|
|
|
struct mesh_node {
|
|
const char *name;
|
|
GList *net_keys;
|
|
GList *app_keys;
|
|
void *prov;
|
|
GList *elements;
|
|
uint32_t iv_index;
|
|
uint32_t seq_number;
|
|
uint16_t primary_net_idx;
|
|
uint16_t primary;
|
|
uint16_t oob;
|
|
uint16_t features;
|
|
uint8_t dev_uuid[16];
|
|
uint8_t dev_key[16];
|
|
uint8_t num_ele;
|
|
uint8_t ttl;
|
|
bool provisioner;
|
|
struct mesh_node_composition *comp;
|
|
};
|
|
|
|
static GList *nodes;
|
|
|
|
static struct mesh_node *local_node;
|
|
|
|
static int match_node_unicast(const void *a, const void *b)
|
|
{
|
|
const struct mesh_node *node = a;
|
|
uint16_t dst = GPOINTER_TO_UINT(b);
|
|
|
|
if (dst >= node->primary &&
|
|
dst <= (node->primary + node->num_ele - 1))
|
|
return 0;
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int match_device_uuid(const void *a, const void *b)
|
|
{
|
|
const struct mesh_node *node = a;
|
|
const uint8_t *uuid = b;
|
|
|
|
return memcmp(node->dev_uuid, uuid, 16);
|
|
}
|
|
|
|
static int match_element_idx(const void *a, const void *b)
|
|
{
|
|
const struct mesh_element *element = a;
|
|
uint32_t index = GPOINTER_TO_UINT(b);
|
|
|
|
return (element->index == index) ? 0 : -1;
|
|
}
|
|
|
|
static int match_model_id(const void *a, const void *b)
|
|
{
|
|
const struct mesh_model *model = a;
|
|
uint32_t id = GPOINTER_TO_UINT(b);
|
|
|
|
return (model->id == id) ? 0 : -1;
|
|
}
|
|
|
|
struct mesh_node *node_find_by_addr(uint16_t addr)
|
|
{
|
|
GList *l;
|
|
|
|
if (!IS_UNICAST(addr))
|
|
return NULL;
|
|
|
|
l = g_list_find_custom(nodes, GUINT_TO_POINTER(addr),
|
|
match_node_unicast);
|
|
|
|
if (l)
|
|
return l->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
struct mesh_node *node_find_by_uuid(uint8_t uuid[16])
|
|
{
|
|
GList *l;
|
|
|
|
l = g_list_find_custom(nodes, uuid, match_device_uuid);
|
|
|
|
if (l)
|
|
return l->data;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
struct mesh_node *node_create_new(struct prov_svc_data *prov)
|
|
{
|
|
struct mesh_node *node;
|
|
|
|
if (node_find_by_uuid(prov->dev_uuid))
|
|
return NULL;
|
|
|
|
node = g_malloc0(sizeof(struct mesh_node));
|
|
if (!node)
|
|
return NULL;
|
|
|
|
memcpy(node->dev_uuid, prov->dev_uuid, 16);
|
|
node->oob = prov->oob;
|
|
nodes = g_list_append(nodes, node);
|
|
|
|
return node;
|
|
}
|
|
|
|
struct mesh_node *node_new(void)
|
|
{
|
|
struct mesh_node *node;
|
|
|
|
node = g_malloc0(sizeof(struct mesh_node));
|
|
if (!node)
|
|
return NULL;
|
|
|
|
nodes = g_list_append(nodes, node);
|
|
|
|
return node;
|
|
}
|
|
|
|
static void model_free(void *data)
|
|
{
|
|
struct mesh_model *model = data;
|
|
|
|
g_list_free(model->bindings);
|
|
g_list_free(model->subscriptions);
|
|
g_free(model->pub);
|
|
g_free(model);
|
|
}
|
|
|
|
static void element_free(void *data)
|
|
{
|
|
struct mesh_element *element = data;
|
|
|
|
g_list_free_full(element->models, model_free);
|
|
g_free(element);
|
|
}
|
|
|
|
static void free_node_resources(void *data)
|
|
{
|
|
struct mesh_node *node = data;
|
|
g_list_free(node->net_keys);
|
|
g_list_free(node->app_keys);
|
|
|
|
g_list_free_full(node->elements, element_free);
|
|
|
|
if(node->comp)
|
|
g_free(node->comp);
|
|
|
|
g_free(node);
|
|
}
|
|
|
|
void node_free(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return;
|
|
nodes = g_list_remove(nodes, node);
|
|
free_node_resources(node);
|
|
}
|
|
|
|
void node_cleanup(void)
|
|
{
|
|
g_list_free_full(nodes, free_node_resources);
|
|
local_node = NULL;
|
|
}
|
|
|
|
bool node_is_provisioned(struct mesh_node *node)
|
|
{
|
|
return (!IS_UNASSIGNED(node->primary));
|
|
}
|
|
|
|
void *node_get_prov(struct mesh_node *node)
|
|
{
|
|
return node->prov;
|
|
}
|
|
|
|
void node_set_prov(struct mesh_node *node, void *prov)
|
|
{
|
|
node->prov = prov;
|
|
}
|
|
|
|
bool node_app_key_add(struct mesh_node *node, uint16_t idx)
|
|
{
|
|
uint32_t index;
|
|
uint16_t net_idx;
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
net_idx = keys_app_key_get_bound(idx);
|
|
if (net_idx == NET_IDX_INVALID)
|
|
return false;
|
|
|
|
if (!g_list_find(node->net_keys, GUINT_TO_POINTER(net_idx)))
|
|
return false;
|
|
|
|
index = (net_idx << 16) + idx;
|
|
|
|
if (g_list_find(node->app_keys, GUINT_TO_POINTER(index)))
|
|
return false;
|
|
|
|
node->app_keys = g_list_append(node->app_keys, GUINT_TO_POINTER(index));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool node_net_key_add(struct mesh_node *node, uint16_t index)
|
|
{
|
|
if(!node)
|
|
return false;
|
|
|
|
if (g_list_find(node->net_keys, GUINT_TO_POINTER(index)))
|
|
return false;
|
|
|
|
node->net_keys = g_list_append(node->net_keys, GUINT_TO_POINTER(index));
|
|
return true;
|
|
}
|
|
|
|
bool node_net_key_delete(struct mesh_node *node, uint16_t index)
|
|
{
|
|
GList *l;
|
|
|
|
if(!node)
|
|
return false;
|
|
|
|
l = g_list_find(node->net_keys, GUINT_TO_POINTER(index));
|
|
if (!l)
|
|
return false;
|
|
|
|
node->net_keys = g_list_remove(node->net_keys,
|
|
GUINT_TO_POINTER(index));
|
|
/* TODO: remove all associated app keys and bindings */
|
|
return true;
|
|
}
|
|
|
|
bool node_app_key_delete(struct mesh_node *node, uint16_t net_idx,
|
|
uint16_t idx)
|
|
{
|
|
GList *l;
|
|
uint32_t index;
|
|
|
|
if(!node)
|
|
return false;
|
|
|
|
index = (net_idx << 16) + idx;
|
|
|
|
l = g_list_find(node->app_keys, GUINT_TO_POINTER(index));
|
|
if (!l)
|
|
return false;
|
|
|
|
node->app_keys = g_list_remove(node->app_keys,
|
|
GUINT_TO_POINTER(index));
|
|
/* TODO: remove all associated bindings */
|
|
return true;
|
|
}
|
|
|
|
void node_set_primary(struct mesh_node *node, uint16_t unicast)
|
|
{
|
|
node->primary = unicast;
|
|
}
|
|
|
|
uint16_t node_get_primary(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return UNASSIGNED_ADDRESS;
|
|
else
|
|
return node->primary;
|
|
}
|
|
|
|
void node_set_device_key(struct mesh_node *node, uint8_t *key)
|
|
|
|
{
|
|
if (!node || !key)
|
|
return;
|
|
|
|
memcpy(node->dev_key, key, 16);
|
|
}
|
|
|
|
uint8_t *node_get_device_key(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return NULL;
|
|
else
|
|
return node->dev_key;
|
|
}
|
|
|
|
void node_set_num_elements(struct mesh_node *node, uint8_t num_ele)
|
|
{
|
|
node->num_ele = num_ele;
|
|
}
|
|
|
|
uint8_t node_get_num_elements(struct mesh_node *node)
|
|
{
|
|
return node->num_ele;
|
|
}
|
|
|
|
GList *node_get_net_keys(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return NULL;
|
|
else
|
|
return node->net_keys;
|
|
}
|
|
|
|
GList *node_get_app_keys(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return NULL;
|
|
else
|
|
return node->app_keys;
|
|
}
|
|
|
|
bool node_parse_composition(struct mesh_node *node, uint8_t *data, uint16_t len)
|
|
{
|
|
struct mesh_node_composition *comp;
|
|
uint16_t features;
|
|
int i;
|
|
|
|
comp = g_malloc0(sizeof(struct mesh_node_composition));
|
|
if (!comp)
|
|
return false;
|
|
|
|
/* skip page -- We only support Page Zero */
|
|
data++;
|
|
len--;
|
|
|
|
comp->cid = get_le16(&data[0]);
|
|
comp->pid = get_le16(&data[2]);
|
|
comp->vid = get_le16(&data[4]);
|
|
comp->crpl = get_le16(&data[6]);
|
|
features = get_le16(&data[8]);
|
|
data += 10;
|
|
len -= 10;
|
|
|
|
comp->relay = !!(features & MESH_FEATURE_RELAY);
|
|
comp->proxy = !!(features & MESH_FEATURE_PROXY);
|
|
comp->friend = !!(features & MESH_FEATURE_FRIEND);
|
|
comp->lpn = !!(features & MESH_FEATURE_LPN);
|
|
|
|
for (i = 0; i< node->num_ele; i++) {
|
|
uint8_t m, v;
|
|
uint32_t mod_id;
|
|
uint16_t vendor_id;
|
|
struct mesh_element *ele;
|
|
ele = g_malloc0(sizeof(struct mesh_element));
|
|
if (!ele) {
|
|
g_free(comp);
|
|
return false;
|
|
}
|
|
|
|
ele->index = i;
|
|
ele->loc = get_le16(data);
|
|
data += 2;
|
|
node->elements = g_list_append(node->elements, ele);
|
|
|
|
m = *data++;
|
|
v = *data++;
|
|
len -= 2;
|
|
|
|
while (len >= 2 && m--) {
|
|
mod_id = get_le16(data);
|
|
/* initialize uppper 16 bits to 0xffff for SIG models */
|
|
mod_id |= 0xffff0000;
|
|
if (!node_set_model(node, ele->index, mod_id)) {
|
|
g_free(comp);
|
|
return false;
|
|
}
|
|
data += 2;
|
|
len -= 2;
|
|
}
|
|
while (len >= 4 && v--) {
|
|
mod_id = get_le16(data + 2);
|
|
vendor_id = get_le16(data);
|
|
mod_id |= (vendor_id << 16);
|
|
if (!node_set_model(node, ele->index, mod_id)) {
|
|
g_free(comp);
|
|
return false;
|
|
}
|
|
data += 4;
|
|
len -= 4;
|
|
}
|
|
|
|
}
|
|
|
|
node->comp = comp;
|
|
return true;
|
|
}
|
|
|
|
bool node_set_local_node(struct mesh_node *node)
|
|
{
|
|
if (local_node) {
|
|
bt_shell_printf("Local node already registered\n");
|
|
return false;
|
|
}
|
|
net_register_unicast(node->primary, node->num_ele);
|
|
|
|
local_node = node;
|
|
local_node->provisioner = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct mesh_node *node_get_local_node()
|
|
{
|
|
return local_node;
|
|
}
|
|
|
|
uint16_t node_get_primary_net_idx(struct mesh_node *node)
|
|
{
|
|
if (node == NULL)
|
|
return NET_IDX_INVALID;
|
|
|
|
return node->primary_net_idx;
|
|
}
|
|
|
|
static bool deliver_model_data(struct mesh_element* element, uint16_t src,
|
|
uint16_t app_idx, uint8_t *data, uint16_t len)
|
|
{
|
|
GList *l;
|
|
|
|
for(l = element->models; l; l = l->next) {
|
|
struct mesh_model *model = l->data;
|
|
|
|
if (!g_list_find(model->bindings, GUINT_TO_POINTER(app_idx)))
|
|
continue;
|
|
|
|
if (model->cbs.recv &&
|
|
model->cbs.recv(src, data, len, model->user_data))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void node_local_data_handler(uint16_t src, uint32_t dst,
|
|
uint32_t iv_index, uint32_t seq_num,
|
|
uint16_t app_idx, uint8_t *data, uint16_t len)
|
|
{
|
|
GList *l;
|
|
bool res;
|
|
uint64_t iv_seq;
|
|
uint64_t iv_seq_remote;
|
|
uint8_t ele_idx;
|
|
struct mesh_element *element;
|
|
struct mesh_node *remote;
|
|
bool loopback;
|
|
|
|
if (!local_node || seq_num > 0xffffff)
|
|
return;
|
|
|
|
iv_seq = iv_index << 24;
|
|
iv_seq |= seq_num;
|
|
|
|
remote = node_find_by_addr(src);
|
|
|
|
if (!remote) {
|
|
if (local_node->provisioner) {
|
|
bt_shell_printf("Remote node unknown (%4.4x)\n", src);
|
|
return;
|
|
}
|
|
|
|
remote = g_new0(struct mesh_node, 1);
|
|
if (!remote)
|
|
return;
|
|
|
|
/* Not Provisioner; Assume all SRC elements stand alone */
|
|
remote->primary = src;
|
|
remote->num_ele = 1;
|
|
nodes = g_list_append(nodes, remote);
|
|
}
|
|
|
|
loopback = (src < (local_node->primary + local_node->num_ele) &&
|
|
src >= local_node->primary);
|
|
|
|
if (!loopback) {
|
|
iv_seq_remote = remote->iv_index << 24;
|
|
iv_seq |= remote->seq_number;
|
|
|
|
if (iv_seq_remote >= iv_seq) {
|
|
bt_shell_printf("Replayed message detected "
|
|
"(%016" PRIx64 " >= %016" PRIx64 ")\n",
|
|
iv_seq_remote, iv_seq);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (IS_GROUP(dst) || IS_VIRTUAL(dst)) {
|
|
/* TODO: if subscription address, deliver to subscribers */
|
|
return;
|
|
}
|
|
|
|
if (IS_ALL_NODES(dst)) {
|
|
ele_idx = 0;
|
|
} else {
|
|
if (dst >= (local_node->primary + local_node->num_ele) ||
|
|
dst < local_node->primary)
|
|
return;
|
|
|
|
ele_idx = dst - local_node->primary;
|
|
}
|
|
|
|
l = g_list_find_custom(local_node->elements,
|
|
GUINT_TO_POINTER(ele_idx), match_element_idx);
|
|
|
|
/* This should not happen */
|
|
if (!l)
|
|
return;
|
|
|
|
element = l->data;
|
|
res = deliver_model_data(element, src, app_idx, data, len);
|
|
|
|
if (res && !loopback) {
|
|
/* TODO: Save remote in Replay Protection db */
|
|
remote->iv_index = iv_index;
|
|
remote->seq_number = seq_num;
|
|
prov_db_node_set_iv_seq(remote, iv_index, seq_num);
|
|
}
|
|
}
|
|
|
|
static gboolean restore_model_state(gpointer data)
|
|
{
|
|
struct mesh_model *model = data;
|
|
GList *l;
|
|
struct mesh_model_ops *ops;
|
|
|
|
ops = &model->cbs;
|
|
|
|
if (model->bindings && ops->bind) {
|
|
for (l = model->bindings; l; l = l->next) {
|
|
if (ops->bind(GPOINTER_TO_UINT(l->data), ACTION_ADD) !=
|
|
MESH_STATUS_SUCCESS)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (model->pub && ops->pub)
|
|
ops->pub(model->pub);
|
|
|
|
g_idle_remove_by_data(data);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool node_local_model_register(uint8_t ele_idx, uint16_t model_id,
|
|
struct mesh_model_ops *ops, void *user_data)
|
|
{
|
|
uint32_t id = 0xffff0000 | model_id;
|
|
|
|
return node_local_vendor_model_register(ele_idx, id, ops, user_data);
|
|
}
|
|
|
|
bool node_local_vendor_model_register(uint8_t ele_idx, uint32_t model_id,
|
|
struct mesh_model_ops *ops, void *user_data)
|
|
{
|
|
struct mesh_element *ele;
|
|
struct mesh_model *model;
|
|
GList *l;
|
|
|
|
if (!local_node)
|
|
return false;
|
|
|
|
l = g_list_find_custom(local_node->elements, GUINT_TO_POINTER(ele_idx),
|
|
match_element_idx);
|
|
if (!l)
|
|
return false;
|
|
|
|
ele = l->data;
|
|
|
|
l = g_list_find_custom(ele->models, GUINT_TO_POINTER(model_id),
|
|
match_model_id);
|
|
if (!l)
|
|
return false;
|
|
|
|
model = l->data;
|
|
model->cbs = *ops;
|
|
model->user_data = user_data;
|
|
|
|
if (model_id >= 0xffff0000)
|
|
model_id = model_id & 0xffff;
|
|
|
|
/* Silently assign device key binding to configuration models */
|
|
if (model_id == CONFIG_SERVER_MODEL_ID ||
|
|
model_id == CONFIG_CLIENT_MODEL_ID) {
|
|
model->bindings = g_list_append(model->bindings,
|
|
GUINT_TO_POINTER(APP_IDX_DEV));
|
|
} else {
|
|
g_idle_add(restore_model_state, model);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool node_set_element(struct mesh_node *node, uint8_t ele_idx)
|
|
{
|
|
struct mesh_element *ele;
|
|
GList *l;
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
|
|
match_element_idx);
|
|
if (l)
|
|
return false;
|
|
|
|
ele = g_malloc0(sizeof(struct mesh_element));
|
|
if (!ele)
|
|
return false;
|
|
|
|
ele->index = ele_idx;
|
|
node->elements = g_list_append(node->elements, ele);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool node_set_model(struct mesh_node *node, uint8_t ele_idx, uint32_t id)
|
|
{
|
|
struct mesh_element *ele;
|
|
struct mesh_model *model;
|
|
GList *l;
|
|
|
|
if (!node)
|
|
return false;
|
|
|
|
l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
|
|
match_element_idx);
|
|
if (!l)
|
|
return false;
|
|
|
|
ele = l->data;
|
|
|
|
l = g_list_find_custom(ele->models, GUINT_TO_POINTER(id),
|
|
match_model_id);
|
|
if (l)
|
|
return true;
|
|
|
|
model = g_malloc0(sizeof(struct mesh_model));
|
|
if (!model)
|
|
return false;
|
|
|
|
model->id = id;
|
|
ele->models = g_list_append(ele->models, model);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool node_set_composition(struct mesh_node *node,
|
|
struct mesh_node_composition *comp)
|
|
{
|
|
if (!node || !comp || node->comp)
|
|
return false;
|
|
|
|
node->comp = g_malloc0(sizeof(struct mesh_node_composition));
|
|
if (!node->comp)
|
|
return false;
|
|
|
|
*(node->comp) = *comp;
|
|
return true;
|
|
}
|
|
|
|
struct mesh_node_composition *node_get_composition(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return NULL;
|
|
|
|
return node->comp;
|
|
}
|
|
|
|
static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,
|
|
uint32_t model_id)
|
|
{
|
|
struct mesh_element *ele;
|
|
GList *l;
|
|
|
|
if (!node)
|
|
return NULL;
|
|
|
|
l = g_list_find_custom(node->elements, GUINT_TO_POINTER(ele_idx),
|
|
match_element_idx);
|
|
if (!l)
|
|
return NULL;
|
|
|
|
ele = l->data;
|
|
|
|
l = g_list_find_custom(ele->models, GUINT_TO_POINTER(model_id),
|
|
match_model_id);
|
|
if (!l)
|
|
return NULL;
|
|
|
|
return l->data;
|
|
|
|
}
|
|
|
|
bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
|
|
uint32_t model_id, uint16_t app_idx)
|
|
{
|
|
struct mesh_model *model;
|
|
GList *l;
|
|
|
|
model = get_model(node, ele_idx, model_id);
|
|
if (!model)
|
|
return false;
|
|
|
|
l = g_list_find(model->bindings, GUINT_TO_POINTER(app_idx));
|
|
if (l)
|
|
return false;
|
|
|
|
if ((node == local_node) && model->cbs.bind) {
|
|
if (!model->cbs.bind(app_idx, ACTION_ADD))
|
|
return false;
|
|
}
|
|
|
|
model->bindings = g_list_append(model->bindings,
|
|
GUINT_TO_POINTER(app_idx));
|
|
return true;
|
|
}
|
|
|
|
bool node_add_subscription(struct mesh_node *node, uint8_t ele_idx,
|
|
uint32_t model_id, uint16_t addr)
|
|
{
|
|
struct mesh_model *model;
|
|
GList *l;
|
|
|
|
model = get_model(node, ele_idx, model_id);
|
|
if (!model)
|
|
return false;
|
|
|
|
l = g_list_find(model->subscriptions, GUINT_TO_POINTER(addr));
|
|
if (l)
|
|
return false;
|
|
|
|
model->subscriptions = g_list_append(model->subscriptions,
|
|
GUINT_TO_POINTER(addr));
|
|
return true;
|
|
}
|
|
|
|
uint8_t node_get_default_ttl(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return DEFAULT_TTL;
|
|
else if (node == local_node)
|
|
return net_get_default_ttl();
|
|
else
|
|
return node->ttl;
|
|
}
|
|
|
|
bool node_set_default_ttl(struct mesh_node *node, uint8_t ttl)
|
|
{
|
|
if (!node)
|
|
return false;
|
|
|
|
node->ttl = ttl;
|
|
|
|
if (node == local_node || local_node == NULL)
|
|
return net_set_default_ttl(ttl);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
|
|
{
|
|
if (!node)
|
|
return false;
|
|
|
|
node->seq_number = seq;
|
|
|
|
if (node == local_node || local_node == NULL)
|
|
return net_set_seq_num(seq);
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32_t node_get_sequence_number(struct mesh_node *node)
|
|
{
|
|
if (!node)
|
|
return 0xffffffff;
|
|
else if (node == local_node)
|
|
return net_get_seq_num();
|
|
|
|
return node->seq_number;
|
|
}
|
|
|
|
bool node_set_iv_index(struct mesh_node *node, uint32_t iv_index)
|
|
{
|
|
if (!node)
|
|
return false;
|
|
|
|
node->iv_index = iv_index;
|
|
return true;
|
|
}
|
|
|
|
uint32_t node_get_iv_index(struct mesh_node *node)
|
|
{
|
|
bool update;
|
|
|
|
if (!node)
|
|
return 0xffffffff;
|
|
else if (node == local_node)
|
|
return net_get_iv_index(&update);
|
|
return node->iv_index;
|
|
}
|
|
|
|
bool node_model_pub_set(struct mesh_node *node, uint8_t ele, uint32_t model_id,
|
|
struct mesh_publication *pub)
|
|
{
|
|
struct mesh_model *model;
|
|
|
|
model = get_model(node, ele, model_id);
|
|
if(!model)
|
|
return false;
|
|
|
|
if (!model->pub)
|
|
model->pub = g_malloc0(sizeof(struct mesh_publication));
|
|
if (!model)
|
|
return false;
|
|
|
|
memcpy(model->pub, pub, (sizeof(struct mesh_publication)));
|
|
|
|
if((node == local_node) && model->cbs.pub)
|
|
model->cbs.pub(pub);
|
|
return true;
|
|
}
|
|
|
|
struct mesh_publication *node_model_pub_get(struct mesh_node *node, uint8_t ele,
|
|
uint32_t model_id)
|
|
{
|
|
struct mesh_model *model;
|
|
|
|
model = get_model(node, ele, model_id);
|
|
if(!model)
|
|
return NULL;
|
|
else
|
|
return model->pub;
|
|
}
|