linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Brian Gix <brian.gix@intel.com>
To: linux-bluetooth@vger.kernel.org
Cc: johan.hedberg@gmail.com, inga.stotland@intel.com,
	marcel@holtmann.org, brian.gix@intel.com
Subject: [PATCH BlueZ v5 06/30] mesh: Rewrite Node handling for multiple nodes
Date: Tue, 18 Dec 2018 20:19:52 -0800	[thread overview]
Message-ID: <20181219042016.25538-7-brian.gix@intel.com> (raw)
In-Reply-To: <20181219042016.25538-1-brian.gix@intel.com>

From: Inga Stotland <inga.stotland@intel.com>

---
 mesh/node.c | 1338 +++++++++++++++++++++++++++++++++++++++++++++++++----------
 mesh/node.h |   42 +-
 2 files changed, 1158 insertions(+), 222 deletions(-)

diff --git a/mesh/node.c b/mesh/node.c
index 501ab8eea..d966f188c 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -22,21 +22,40 @@
 #include <config.h>
 #endif
 
+#include <stdio.h>
 #include <sys/time.h>
 #include <ell/ell.h>
 
 #include "mesh/mesh-defs.h"
 
 #include "mesh/mesh.h"
+#include "mesh/mesh-io.h"
 #include "mesh/net.h"
-#include "mesh/node.h"
+#include "mesh/mesh-db.h"
+#include "mesh/provision.h"
 #include "mesh/storage.h"
 #include "mesh/appkey.h"
 #include "mesh/model.h"
+#include "mesh/cfgmod.h"
+#include "mesh/util.h"
+#include "mesh/error.h"
+#include "mesh/dbus.h"
+#include "mesh/agent.h"
+#include "mesh/node.h"
 
 #define MIN_COMP_SIZE 14
 
+#define MESH_NODE_PATH_PREFIX "/node"
+#define MESH_ELEMENT_PATH_PREFIX "/ele"
+
+/* Default element location: unknown */
+#define DEFAULT_LOCATION 0x0000
+
+#define DEFAULT_CRPL 10
+#define DEFAULT_SEQUENCE_NUMBER 0
+
 struct node_element {
+	char *path;
 	struct l_queue *models;
 	uint16_t location;
 	uint8_t idx;
@@ -51,30 +70,45 @@ struct node_composition {
 
 struct mesh_node {
 	struct mesh_net *net;
-	struct l_queue *net_keys;
-	struct l_queue *app_keys;
 	struct l_queue *elements;
+	char *app_path;
+	char *owner;
+	char *path;
+	void *jconfig;
+	char *cfg_file;
+	uint32_t disc_watch;
 	time_t upd_sec;
 	uint32_t seq_number;
 	uint32_t seq_min_cache;
-	uint16_t primary;
-	uint16_t num_ele;
-	uint8_t dev_uuid[16];
-	uint8_t dev_key[16];
-	uint8_t ttl;
+	uint16_t id;
 	bool provisioner;
+	uint16_t primary;
 	struct node_composition *comp;
 	struct {
 		uint16_t interval;
 		uint8_t cnt;
 		uint8_t mode;
 	} relay;
+	uint8_t dev_uuid[16];
+	uint8_t dev_key[16];
+	uint8_t num_ele;
+	uint8_t ttl;
 	uint8_t lpn;
 	uint8_t proxy;
 	uint8_t friend;
 	uint8_t beacon;
 };
 
+struct attach_obj_request {
+	node_attach_ready_func_t cb;
+	struct mesh_node *node;
+};
+
+struct join_obj_request {
+	node_join_ready_func_t cb;
+	const uint8_t *uuid;
+};
+
 static struct l_queue *nodes;
 
 static bool match_node_unicast(const void *a, const void *b)
@@ -94,6 +128,14 @@ static bool match_device_uuid(const void *a, const void *b)
 	return (memcmp(node->dev_uuid, uuid, 16) == 0);
 }
 
+static bool match_token(const void *a, const void *b)
+{
+	const struct mesh_node *node = a;
+	const uint64_t *token = b;
+	const uint64_t tmp = l_get_u64(node->dev_key);
+	return *token == tmp;
+}
+
 static bool match_element_idx(const void *a, const void *b)
 {
 	const struct node_element *element = a;
@@ -102,17 +144,15 @@ static bool match_element_idx(const void *a, const void *b)
 	return (element->idx == index);
 }
 
-static bool match_key_idx(const void *a, const void *b)
+static bool match_element_path(const void *a, const void *b)
 {
-	return (L_PTR_TO_UINT(a) == L_PTR_TO_UINT(b));
-}
+	const struct node_element *element = a;
+	const char *path = b;
 
-static bool match_model_id(const void *a, const void *b)
-{
-	const struct mesh_model *model = a;
-	uint32_t id = L_PTR_TO_UINT(b);
+	if (!element->path)
+		return false;
 
-	return (mesh_model_get_model_id(model) == id);
+	return (!strcmp(element->path, path));
 }
 
 struct mesh_node *node_find_by_addr(uint16_t addr)
@@ -140,20 +180,30 @@ struct mesh_node *node_new(void)
 	struct mesh_node *node;
 
 	node = l_new(struct mesh_node, 1);
+	node->net = mesh_net_new(node);
 
-	if (!node)
-		return NULL;
+	if (!nodes)
+		nodes = l_queue_new();
 
 	l_queue_push_tail(nodes, node);
 
 	return node;
 }
 
+static void free_element_path(void *a, void *b)
+{
+	struct node_element *element = a;
+
+	l_free(element->path);
+	element->path = NULL;
+}
+
 static void element_free(void *data)
 {
 	struct node_element *element = data;
 
 	l_queue_destroy(element->models, mesh_model_free);
+	l_free(element->path);
 	l_free(element);
 }
 
@@ -161,13 +211,20 @@ static void free_node_resources(void *data)
 {
 	struct mesh_node *node = data;
 
-	l_queue_destroy(node->net_keys, NULL);
-	l_queue_destroy(node->app_keys, NULL);
+	/* Unregister io callbacks */
+	if(node->net)
+		mesh_net_detach(node->net);
+	mesh_net_free(node->net);
+
 	l_queue_destroy(node->elements, element_free);
 	l_free(node->comp);
+	l_free(node->app_path);
+	l_free(node->owner);
 
-	if (node->net)
-		mesh_net_unref(node->net);
+	if (node->path)
+		l_dbus_object_remove_interface(dbus_get_bus(), node->path,
+					MESH_NODE_INTERFACE);
+	l_free(node->path);
 
 	l_free(node);
 }
@@ -176,19 +233,18 @@ void node_free(struct mesh_node *node)
 {
 	if (!node)
 		return;
+
 	l_queue_remove(nodes, node);
 	free_node_resources(node);
 }
 
-static bool add_models(struct mesh_net *net, struct node_element *ele,
+static bool add_models(struct mesh_node *node, struct node_element *ele,
 						struct mesh_db_element *db_ele)
 {
 	const struct l_queue_entry *entry;
 
 	if (!ele->models)
 		ele->models = l_queue_new();
-	if (!ele->models)
-		return false;
 
 	entry = l_queue_get_entries(db_ele->models);
 	for (; entry; entry = entry->next) {
@@ -196,7 +252,7 @@ static bool add_models(struct mesh_net *net, struct node_element *ele,
 		struct mesh_db_model *db_mod;
 
 		db_mod = entry->data;
-		mod = mesh_model_init(net, ele->idx, db_mod);
+		mod = mesh_model_setup(node, ele->idx, db_mod);
 		if (!mod)
 			return false;
 
@@ -206,6 +262,32 @@ static bool add_models(struct mesh_net *net, struct node_element *ele,
 	return true;
 }
 
+static void add_internal_model(struct mesh_node *node, uint32_t mod_id,
+								uint8_t ele_idx)
+{
+	struct node_element *ele;
+	struct mesh_model *mod;
+	struct mesh_db_model db_mod;
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+
+	if (!ele)
+		return;
+
+	memset(&db_mod, 0, sizeof(db_mod));
+	db_mod.id = mod_id;
+
+	mod = mesh_model_setup(node, ele_idx, &db_mod);
+	if (!mod)
+		return;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	l_queue_push_tail(ele->models, mod);
+}
+
 static bool add_element(struct mesh_node *node, struct mesh_db_element *db_ele)
 {
 	struct node_element *ele;
@@ -217,7 +299,7 @@ static bool add_element(struct mesh_node *node, struct mesh_db_element *db_ele)
 	ele->idx = db_ele->index;
 	ele->location = db_ele->location;
 
-	if (!db_ele->models || !add_models(node->net, ele, db_ele))
+	if (!db_ele->models || !add_models(node, ele, db_ele))
 		return false;
 
 	l_queue_push_tail(node->elements, ele);
@@ -231,9 +313,6 @@ static bool add_elements(struct mesh_node *node, struct mesh_db_node *db_node)
 	if (!node->elements)
 		node->elements = l_queue_new();
 
-	if (!node->elements)
-		return false;
-
 	entry = l_queue_get_entries(db_node->elements);
 	for (; entry; entry = entry->next)
 		if (!add_element(node, entry->data))
@@ -242,26 +321,12 @@ static bool add_elements(struct mesh_node *node, struct mesh_db_node *db_node)
 	return true;
 }
 
-struct mesh_node *node_create_from_storage(struct mesh_net *net,
-						struct mesh_db_node *db_node,
-								bool local)
+bool node_init_from_storage(struct mesh_node *node, void *data)
 {
-	struct mesh_node *node;
+	struct mesh_db_node *db_node = data;
 	unsigned int num_ele;
 
-	if (local && !net)
-		return NULL;
-
-	node = node_new();
-	if (!node)
-		return NULL;
-
 	node->comp = l_new(struct node_composition, 1);
-	if (!node->comp) {
-		node_free(node);
-		return NULL;
-	}
-
 	node->comp->cid = db_node->cid;
 	node->comp->pid = db_node->pid;
 	node->comp->vid = db_node->vid;
@@ -276,107 +341,77 @@ struct mesh_node *node_create_from_storage(struct mesh_net *net,
 	node->relay.interval = db_node->modes.relay.interval;
 	node->beacon = db_node->modes.beacon;
 
-	l_info("relay %2.2x, proxy %2.2x, lpn %2.2x, friend %2.2x",
-	       node->relay.mode, node->proxy, node->friend, node->lpn);
+	l_debug("relay %2.2x, proxy %2.2x, lpn %2.2x, friend %2.2x",
+			node->relay.mode, node->proxy, node->friend, node->lpn);
 	node->ttl = db_node->ttl;
 	node->seq_number = db_node->seq_number;
 
 	num_ele = l_queue_length(db_node->elements);
-	if (num_ele > 0xff) {
-		node_free(node);
-		return NULL;
-	}
+	if (num_ele > 0xff)
+		return false;
 
 	node->num_ele = num_ele;
-	if (num_ele != 0 && !add_elements(node, db_node)) {
-		node_free(node);
-		return NULL;
-	}
+	if (num_ele != 0 && !add_elements(node, db_node))
+		return false;
 
 	node->primary = db_node->unicast;
 
 	memcpy(node->dev_uuid, db_node->uuid, 16);
 
-	if (local)
-		node->net = mesh_net_ref(net);
+	/* Initialize configuration server model */
+	mesh_config_srv_init(node, PRIMARY_ELE_IDX);
 
-	return node;
+	return true;
 }
 
-void node_cleanup(struct mesh_net *net)
+void node_cleanup(void *data)
 {
-	struct mesh_node *node;
+	struct mesh_node *node = data;
+	struct mesh_net *net = node->net;
 
-	if (!net)
-		return;
-	node = mesh_net_local_node_get(net);
-	if (node)
-		node_free(node);
+	/* Save local node configuration */
+	if (node->cfg_file) {
+
+		/* Preserve the last sequence number */
+		storage_write_sequence_number(net, mesh_net_get_seq_num(net));
 
-	l_queue_destroy(nodes, free_node_resources);
+		if (storage_save_config(node, true, NULL, NULL))
+			l_info("Saved final config to %s", node->cfg_file);
+	}
+
+	if (node->disc_watch)
+		l_dbus_remove_watch(dbus_get_bus(), node->disc_watch);
 
+	free_node_resources(node);
 }
 
-bool node_is_provisioned(struct mesh_node *node)
+void node_cleanup_all(void)
 {
-	return (!IS_UNASSIGNED(node->primary));
+	l_queue_destroy(nodes, node_cleanup);
+	l_dbus_unregister_interface(dbus_get_bus(), MESH_NODE_INTERFACE);
 }
 
-bool node_net_key_delete(struct mesh_node *node, uint16_t idx)
+bool node_is_provisioned(struct mesh_node *node)
 {
-	if (!node)
-		return false;
-
-	if (!l_queue_find(node->net_keys, match_key_idx, L_UINT_TO_PTR(idx)))
-		return false;
-
-	l_queue_remove(node->net_keys, L_UINT_TO_PTR(idx));
-	/* TODO: remove all associated app keys and bindings */
-	return true;
+	return (!IS_UNASSIGNED(node->primary));
 }
 
 bool node_app_key_delete(struct mesh_net *net, uint16_t addr,
 				uint16_t net_idx, uint16_t app_idx)
 {
 	struct mesh_node *node;
-	uint32_t index;
 	const struct l_queue_entry *entry;
 
 	node = node_find_by_addr(addr);
 	if (!node)
 		return false;
 
-	index = (net_idx << 16) + app_idx;
-
-	if (!l_queue_find(node->app_keys, match_key_idx, L_UINT_TO_PTR(index)))
-		return false;
-
-	l_queue_remove(node->app_keys, L_UINT_TO_PTR(index));
-
-	storage_local_app_key_del(net, net_idx, app_idx);
-
 	entry = l_queue_get_entries(node->elements);
 	for (; entry; entry = entry->next) {
 		struct node_element *ele = entry->data;
 
-		mesh_model_app_key_delete(net, ele->models, app_idx);
+		mesh_model_app_key_delete(node, ele->models, app_idx);
 	}
-
-	return true;
-}
-
-bool node_set_primary(struct mesh_node *node, uint16_t unicast)
-{
-	if (!node)
-		return false;
-
-	node->primary = unicast;
-
-	/* If local node, save to storage */
-	if (node->net)
-		return storage_local_set_unicast(node->net, unicast);
-
-	/* TODO: for provisioner, store remote node info */
 	return true;
 }
 
@@ -388,20 +423,9 @@ uint16_t node_get_primary(struct mesh_node *node)
 		return node->primary;
 }
 
-bool node_set_device_key(struct mesh_node *node, uint8_t key[16])
-
+void node_set_device_key(struct mesh_node *node, uint8_t key[16])
 {
-	if (!node || !key)
-		return false;
-
 	memcpy(node->dev_key, key, 16);
-
-	/* If local node, save to storage */
-	if (node->net)
-		return storage_local_set_device_key(node->net, key);
-
-	/* TODO: for provisioner, store remote node info */
-	return true;
 }
 
 const uint8_t *node_get_device_key(struct mesh_node *node)
@@ -417,22 +441,6 @@ uint8_t node_get_num_elements(struct mesh_node *node)
 	return node->num_ele;
 }
 
-struct l_queue *node_get_net_keys(struct mesh_node *node)
-{
-	if (!node)
-		return NULL;
-	else
-		return node->net_keys;
-}
-
-struct l_queue *node_get_app_keys(struct mesh_node *node)
-{
-	if (!node)
-		return NULL;
-	else
-		return node->app_keys;
-}
-
 struct l_queue *node_get_element_models(struct mesh_node *node,
 						uint8_t ele_idx, int *status)
 {
@@ -458,31 +466,6 @@ struct l_queue *node_get_element_models(struct mesh_node *node,
 	return ele->models;
 }
 
-struct mesh_model *node_get_model(struct mesh_node *node, uint8_t ele_idx,
-						uint32_t id, int *status)
-{
-	struct l_queue *models;
-	struct mesh_model *model;
-
-	if (!node) {
-		if (status)
-			*status = MESH_STATUS_INVALID_ADDRESS;
-		return NULL;
-	}
-
-	models = node_get_element_models(node, ele_idx, status);
-	if (!models)
-		return NULL;
-
-	model = l_queue_find(models, match_model_id, L_UINT_TO_PTR(id));
-
-	if (status)
-		*status = (model) ? MESH_STATUS_SUCCESS :
-						MESH_STATUS_INVALID_MODEL;
-
-	return model;
-}
-
 uint8_t node_default_ttl_get(struct mesh_node *node)
 {
 	if (!node)
@@ -492,20 +475,16 @@ uint8_t node_default_ttl_get(struct mesh_node *node)
 
 bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl)
 {
-	bool res, is_local;
+	bool res;
 
 	if (!node)
 		return false;
 
-	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
-		true : false;
-
-	res = storage_local_set_ttl(node->net, ttl);
+	res = storage_set_ttl(node->jconfig, ttl);
 
 	if (res) {
 		node->ttl = ttl;
-		if (is_local)
-			mesh_net_set_default_ttl(node->net, ttl);
+		mesh_net_set_default_ttl(node->net, ttl);
 	}
 
 	return res;
@@ -513,21 +492,13 @@ bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl)
 
 bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
 {
-	bool is_local;
 	struct timeval write_time;
 
-
 	if (!node)
 		return false;
 
 	node->seq_number = seq;
 
-	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
-		true : false;
-
-	if (!is_local)
-		return true;
-
 	/*
 	 * Holistically determine worst case 5 minute sequence consumption
 	 * so that we typically (once we reach a steady state) rewrite the
@@ -541,7 +512,7 @@ bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
 		if (elapsed < MIN_SEQ_CACHE_TIME) {
 			uint32_t ideal = node->seq_min_cache;
 
-			l_info("Old Seq Cache: %d", node->seq_min_cache);
+			l_debug("Old Seq Cache: %d", node->seq_min_cache);
 
 			ideal *= (MIN_SEQ_CACHE_TIME / elapsed);
 
@@ -550,14 +521,13 @@ bool node_set_sequence_number(struct mesh_node *node, uint32_t seq)
 			else
 				node->seq_min_cache += MIN_SEQ_CACHE;
 
-			l_info("New Seq Cache: %d", node->seq_min_cache);
+			l_debug("New Seq Cache: %d", node->seq_min_cache);
 		}
 	}
 
 	node->upd_sec = write_time.tv_sec;
 
-	l_info("Storage-Write");
-	return storage_local_write_sequence_number(node->net, seq);
+	return storage_write_sequence_number(node->net, seq);
 }
 
 uint32_t node_get_sequence_number(struct mesh_node *node)
@@ -629,24 +599,19 @@ uint8_t node_lpn_mode_get(struct mesh_node *node)
 bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
 							uint16_t interval)
 {
-	bool res, is_local;
+	bool res;
 
 	if (!node || node->relay.mode == MESH_MODE_UNSUPPORTED)
 		return false;
 
-	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
-		true : false;
-
-	res = storage_local_set_relay(node->net, enable, cnt, interval);
+	res = storage_set_relay(node->jconfig, enable, cnt, interval);
 
 	if (res) {
 		node->relay.mode = enable ? MESH_MODE_ENABLED :
 							MESH_MODE_DISABLED;
 		node->relay.cnt = cnt;
 		node->relay.interval = interval;
-		if (is_local)
-			mesh_net_set_relay_mode(node->net, enable, cnt,
-								interval);
+		mesh_net_set_relay_mode(node->net, enable, cnt, interval);
 	}
 
 	return res;
@@ -654,22 +619,18 @@ bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
 
 bool node_proxy_mode_set(struct mesh_node *node, bool enable)
 {
-	bool res, is_local;
+	bool res;
 	uint8_t proxy;
 
 	if (!node || node->proxy == MESH_MODE_UNSUPPORTED)
 		return false;
 
-	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
-		true : false;
-
 	proxy = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
-	res = storage_local_set_mode(node->net, proxy, "proxy");
+	res = storage_set_mode(node->jconfig, proxy, "proxy");
 
 	if (res) {
 		node->proxy = proxy;
-		if (is_local)
-			mesh_net_set_proxy_mode(node->net, enable);
+		mesh_net_set_proxy_mode(node->net, enable);
 	}
 
 	return res;
@@ -685,22 +646,18 @@ uint8_t node_proxy_mode_get(struct mesh_node *node)
 
 bool node_beacon_mode_set(struct mesh_node *node, bool enable)
 {
-	bool res, is_local;
+	bool res;
 	uint8_t beacon;
 
 	if (!node)
 		return false;
 
-	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
-		true : false;
-
 	beacon = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
-	res = storage_local_set_mode(node->net, beacon, "beacon");
+	res = storage_set_mode(node->jconfig, beacon, "beacon");
 
 	if (res) {
 		node->beacon = beacon;
-		if (is_local)
-			mesh_net_set_beacon_mode(node->net, enable);
+		mesh_net_set_beacon_mode(node->net, enable);
 	}
 
 	return res;
@@ -716,22 +673,18 @@ uint8_t node_beacon_mode_get(struct mesh_node *node)
 
 bool node_friend_mode_set(struct mesh_node *node, bool enable)
 {
-	bool res, is_local;
+	bool res;
 	uint8_t friend;
 
 	if (!node || node->friend == MESH_MODE_UNSUPPORTED)
 		return false;
 
-	is_local = (node->net && mesh_net_local_node_get(node->net) == node) ?
-		true : false;
-
 	friend = enable ? MESH_MODE_ENABLED : MESH_MODE_DISABLED;
-	res = storage_local_set_mode(node->net, friend, "friend");
+	res = storage_set_mode(node->jconfig, friend, "friend");
 
 	if (res) {
 		node->friend = friend;
-		if (is_local)
-			mesh_net_set_friend_mode(node->net, enable);
+		mesh_net_set_friend_mode(node->net, enable);
 	}
 
 	return res;
@@ -788,7 +741,7 @@ uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz)
 		/* At least fit location and zeros for number of models */
 		if ((n + 4) > sz)
 			return n;
-		l_info("ele->location %d", ele->location);
+
 		l_put_le16(ele->location, buf + n);
 		n += 2;
 
@@ -805,7 +758,7 @@ uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz)
 			mod_id = mesh_model_get_model_id(
 					(const struct mesh_model *) mod);
 
-			if ((mod_id >> 16) == 0xffff) {
+			if ((mod_id & VENDOR_ID_MASK) == VENDOR_ID_MASK) {
 				if (n + 2 > sz)
 					goto element_done;
 
@@ -849,3 +802,970 @@ element_done:
 
 	return n;
 }
+
+
+#define MIN_COMPOSITION_LEN 16
+
+bool node_parse_composition(struct mesh_node *node, uint8_t *data,
+								uint16_t len)
+{
+	struct node_composition *comp;
+	uint16_t features;
+	uint8_t num_ele;
+	bool mode;
+
+	if (!len)
+		return false;
+
+	/* Skip page -- We only support Page Zero */
+	data++;
+	len--;
+
+	if (len < MIN_COMPOSITION_LEN)
+		return false;
+
+	comp = l_new(struct node_composition, 1);
+	if (!comp)
+		return false;
+
+	node->elements = l_queue_new();
+	if (!node->elements) {
+		l_free(comp);
+		return false;
+	}
+
+	node->comp = l_new(struct node_composition, 1);
+	comp->cid = l_get_le16(&data[0]);
+	comp->pid = l_get_le16(&data[2]);
+	comp->vid = l_get_le16(&data[4]);
+	comp->crpl = l_get_le16(&data[6]);
+	features = l_get_le16(&data[8]);
+	data += 10;
+	len -= 10;
+
+	mode = !!(features & FEATURE_PROXY);
+	node->proxy = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	mode = !!(features & FEATURE_LPN);
+	node->lpn = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	mode = !!(features & FEATURE_FRIEND);
+	node->friend = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	mode = !!(features & FEATURE_RELAY);
+	node->relay.mode = mode ? MESH_MODE_DISABLED : MESH_MODE_UNSUPPORTED;
+
+	num_ele = 0;
+
+	do {
+		uint8_t m, v;
+		uint16_t mod_id;
+		uint16_t vendor_id;
+		struct node_element *ele;
+		struct mesh_model *mod;
+
+		ele = l_new(struct node_element, 1);
+		if (!ele)
+			return false;
+
+		ele->idx = num_ele;
+		ele->location = l_get_le16(data);
+		len -= 2;
+		data += 2;
+
+		m = *data++;
+		v = *data++;
+		len -= 2;
+
+		/* Parse SIG models */
+		while (len >= 2 && m--) {
+			mod_id = l_get_le16(data);
+			mod = mesh_model_new(ele->idx, mod_id);
+			if (!mod) {
+				element_free(ele);
+				goto fail;
+			}
+
+			l_queue_push_tail(ele->models, mod);
+			data += 2;
+			len -= 2;
+		}
+
+		if (v && len < 4) {
+			element_free(ele);
+			goto fail;
+		}
+
+		/* Parse vendor models */
+		while (len >= 4 && v--) {
+			mod_id = l_get_le16(data + 2);
+			vendor_id = l_get_le16(data);
+			mod_id |= (vendor_id << 16);
+			mod = mesh_model_vendor_new(ele->idx, vendor_id,
+									mod_id);
+			if (!mod) {
+				element_free(ele);
+				goto fail;
+			}
+
+			l_queue_push_tail(ele->models, mod);
+			data += 4;
+			len -= 4;
+		}
+
+		num_ele++;
+		l_queue_push_tail(node->elements, ele);
+
+	} while (len >= 6);
+
+	/* Check the consistency for the remote node */
+	if (node->num_ele > num_ele)
+		goto fail;
+
+	node->comp = comp;
+	node->num_ele = num_ele;
+
+	return true;
+
+fail:
+	l_queue_destroy(node->elements, element_free);
+	l_free(comp);
+
+	return false;
+}
+
+void node_id_set(struct mesh_node *node, uint16_t id)
+{
+	if (node)
+		node->id = id;
+}
+
+static void attach_io(void *a, void *b)
+{
+	struct mesh_node *node = a;
+	struct mesh_io *io = b;
+
+	if (node->net)
+		mesh_net_attach(node->net, io);
+}
+
+/* Register callbacks for io */
+void node_attach_io(struct mesh_io *io)
+{
+	l_queue_foreach(nodes, attach_io, io);
+}
+
+static bool register_node_object(struct mesh_node *node)
+{
+	node->path = l_malloc(strlen(MESH_NODE_PATH_PREFIX) + 5);
+
+	snprintf(node->path, 10, MESH_NODE_PATH_PREFIX "%4.4x", node->id);
+
+	if (!l_dbus_object_add_interface(dbus_get_bus(), node->path,
+					MESH_NODE_INTERFACE, node))
+		return false;
+
+	return true;
+}
+
+static void app_disc_cb(struct l_dbus *bus, void *user_data)
+{
+	struct mesh_node *node = user_data;
+
+	l_info("App %s disconnected (%u)", node->owner, node->disc_watch);
+
+	node->disc_watch = 0;
+
+	l_queue_foreach(node->elements, free_element_path, NULL);
+
+	l_free(node->owner);
+	node->owner = NULL;
+
+	l_free(node->app_path);
+	node->app_path = NULL;
+}
+
+static bool validate_element_properties(struct mesh_node *node,
+					const char *path,
+					struct l_dbus_message_iter *properties)
+{
+	uint8_t ele_idx;
+	struct node_element *ele;
+	const char *key;
+	struct l_dbus_message_iter variant;
+	bool have_index = false;
+
+	l_debug("path %s", path);
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+		if (!strcmp(key, "Index")) {
+			have_index = true;
+			break;
+		}
+	}
+
+	if (!have_index) {
+		l_debug("Mandatory property \"Index\" not found");
+		return false;
+	}
+
+	if (!l_dbus_message_iter_get_variant(&variant, "y", &ele_idx))
+		return false;
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+
+	if (!ele) {
+		l_debug("Element with index %u not found", ele_idx);
+		return false;
+	}
+
+	/* TODO: validate models */
+
+	ele->path = l_strdup(path);
+
+	return true;
+}
+
+static void get_managed_objects_attach_cb(struct l_dbus_message *msg,
+								void *user_data)
+{
+	struct l_dbus_message_iter objects, interfaces;
+	struct attach_obj_request *req = user_data;
+	struct mesh_node *node = req->node;
+	const char *path;
+	uint64_t token = l_get_u64(node->dev_key);
+	uint8_t num_ele;
+
+	if (l_dbus_message_is_error(msg)) {
+		l_error("Failed to get app's dbus objects");
+		goto fail;
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "a{oa{sa{sv}}}", &objects)) {
+		l_error("Failed to parse app's dbus objects");
+		goto fail;
+	}
+
+	num_ele = 0;
+
+	while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) {
+		struct l_dbus_message_iter properties;
+		const char *interface;
+
+		while (l_dbus_message_iter_next_entry(&interfaces, &interface,
+								&properties)) {
+			if (strcmp(MESH_ELEMENT_INTERFACE, interface))
+				continue;
+
+			if (!validate_element_properties(node, path,
+								&properties))
+				goto fail;
+
+			num_ele++;
+		}
+	}
+
+	/*
+	 * Check that the number of element objects matches the expected number
+	 * of elements on the node
+	 */
+	if (num_ele != node->num_ele)
+		goto fail;
+
+	/* Register node object with D-Bus */
+	register_node_object(node);
+
+	if (node->path) {
+		struct l_dbus *bus = dbus_get_bus();
+
+		node->disc_watch = l_dbus_add_disconnect_watch(bus, node->owner,
+						app_disc_cb, node, NULL);
+		req->cb(MESH_ERROR_NONE, node->path, token);
+
+		return;
+	}
+fail:
+	req->cb(MESH_ERROR_FAILED, NULL, token);
+
+	l_queue_foreach(node->elements, free_element_path, NULL);
+	l_free(node->app_path);
+	node->app_path = NULL;
+
+	l_free(node->owner);
+	node->owner = NULL;
+}
+
+/* Establish relationship between application and mesh node */
+int node_attach(const char *app_path, const char *sender, uint64_t token,
+						node_attach_ready_func_t cb)
+{
+	struct attach_obj_request *req;
+	struct mesh_node *node;
+
+	l_debug("");
+
+	node = l_queue_find(nodes, match_token, &token);
+	if (!node)
+		return MESH_ERROR_NOT_FOUND;
+
+	/* TODO: decide what to do if previous node->app_path is not NULL */
+	node->app_path = l_strdup(app_path);
+
+	node->owner = l_strdup(sender);
+
+	req = l_new(struct attach_obj_request, 1);
+	req->node = node;
+	req->cb = cb;
+
+	l_dbus_method_call(dbus_get_bus(), sender, app_path,
+					L_DBUS_INTERFACE_OBJECT_MANAGER,
+					"GetManagedObjects", NULL,
+					get_managed_objects_attach_cb,
+					req, l_free);
+	return MESH_ERROR_NONE;
+
+}
+
+static void add_model_from_properties(struct node_element *ele,
+					struct l_dbus_message_iter *property)
+{
+	struct l_dbus_message_iter ids;
+	uint16_t model_id;
+	int i = 0;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	if (!l_dbus_message_iter_get_variant(property, "aq", &ids))
+		return;
+
+	while (l_dbus_message_iter_next_entry(&ids, &model_id)) {
+		struct mesh_model *mod;
+		l_debug("model_id %4.4x", model_id);
+		mod = mesh_model_new(ele->idx, model_id);
+		l_queue_push_tail(ele->models, mod);
+		i++;
+		if (i > 3)
+			break;
+	}
+}
+
+static void add_vendor_model_from_properties(struct node_element *ele,
+					struct l_dbus_message_iter *property)
+{
+	struct {
+		uint16_t v;
+		uint16_t m;
+	} id_pair;
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	while (l_dbus_message_iter_next_entry(property, &id_pair)) {
+		struct mesh_model *mod;
+		mod = mesh_model_vendor_new(ele->idx, id_pair.v, id_pair.m);
+		l_queue_push_tail(ele->models, mod);
+	}
+}
+
+static bool get_element_properties(struct mesh_node *node, const char *path,
+					struct l_dbus_message_iter *properties)
+{
+	struct node_element *ele;
+	const char *key;
+	struct l_dbus_message_iter variant;
+	bool have_index = false;
+
+	l_debug("path %s", path);
+
+	ele = l_new(struct node_element, 1);
+	ele->location = DEFAULT_LOCATION;
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+		if (!strcmp(key, "Index")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "y",
+								&ele->idx))
+				return false;
+			have_index = true;
+		} else if (!strcmp(key, "Location")) {
+			l_dbus_message_iter_get_variant(&variant, "q",
+								&ele->location);
+		} else if (!strcmp(key, "Models")) {
+			add_model_from_properties(ele, &variant);
+		} else if (!strcmp(key, "VendorModels")) {
+			add_vendor_model_from_properties(ele, &variant);
+		}
+	}
+
+	if (!have_index) {
+		l_debug("Mandatory property \"Index\" not found");
+		return false;
+	}
+
+	l_queue_push_tail(node->elements, ele);
+
+	return true;
+}
+
+static bool get_app_properties(struct mesh_node *node, const char *path,
+					struct l_dbus_message_iter *properties)
+{
+	const char *key;
+	struct l_dbus_message_iter variant;
+
+	l_debug("path %s", path);
+
+	if (!node->comp)
+		node->comp = l_new(struct node_composition, 1);
+
+	while (l_dbus_message_iter_next_entry(properties, &key, &variant)) {
+
+		if (!strcmp(key, "CompanyID")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "q",
+							&node->comp->cid))
+				return false;
+		} else if (!strcmp(key, "ProductID")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "q",
+							&node->comp->pid))
+				return false;
+		} else if (!strcmp(key, "VersionID")) {
+			if (!l_dbus_message_iter_get_variant(&variant, "q",
+							&node->comp->vid))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static void convert_node_to_storage(struct mesh_node *node,
+						struct mesh_db_node *db_node)
+{
+	const struct l_queue_entry *entry;
+
+	db_node->cid = node->comp->cid;
+	db_node->pid = node->comp->pid;
+	db_node->vid = node->comp->vid;
+	db_node->crpl = node->comp->crpl;
+	db_node->modes.lpn = node->lpn;
+	db_node->modes.proxy = node->proxy;
+
+	memcpy(db_node->uuid, node->dev_uuid, 16);
+
+	node->friend = db_node->modes.friend;
+	db_node->modes.relay.state = node->relay.mode;
+	db_node->modes.relay.cnt = node->relay.cnt;
+	db_node->modes.relay.interval = node->relay.interval;
+	db_node->modes.beacon = node->beacon;
+
+	db_node->ttl = node->ttl;
+	db_node->seq_number = node->seq_number;
+
+	db_node->elements = l_queue_new();
+
+	entry = l_queue_get_entries(node->elements);
+
+	for (; entry; entry = entry->next) {
+		struct node_element *ele = entry->data;
+		struct mesh_db_element *db_ele;
+		const struct l_queue_entry *mod_entry;
+
+		db_ele = l_new(struct mesh_db_element, 1);
+
+		db_ele->index = ele->idx;
+		db_ele->location = ele->location;
+		db_ele->models = l_queue_new();
+
+		mod_entry = l_queue_get_entries(ele->models);
+
+		for (; mod_entry; mod_entry = mod_entry->next) {
+			struct mesh_model *mod = mod_entry->data;
+			struct mesh_db_model *db_mod;
+			uint32_t mod_id = mesh_model_get_model_id(mod);
+
+			db_mod = l_new(struct mesh_db_model, 1);
+			db_mod->id = mod_id;
+			db_mod->vendor = ((mod_id & VENDOR_ID_MASK)
+							!= VENDOR_ID_MASK);
+
+			l_queue_push_tail(db_ele->models, db_mod);
+		}
+		l_queue_push_tail(db_node->elements, db_ele);
+	}
+
+}
+
+static bool create_node_config(struct mesh_node *node)
+{
+	struct mesh_db_node db_node;
+	const struct l_queue_entry *entry;
+	bool res;
+
+	convert_node_to_storage(node, &db_node);
+	res = storage_create_node_config(node, &db_node);
+
+	/* Free temporarily allocated resources */
+	entry = l_queue_get_entries(db_node.elements);
+	for (; entry; entry = entry->next) {
+		struct mesh_db_element *db_ele = entry->data;
+
+		l_queue_destroy(db_ele->models, l_free);
+	}
+
+	l_queue_destroy(db_node.elements, l_free);
+
+	return res;
+}
+
+static void set_defaults(struct mesh_node *node)
+{
+	/* TODO: these values should come from mesh.conf */
+	if (!node->comp)
+		node->comp = l_new(struct node_composition, 1);
+
+	node->comp->crpl = DEFAULT_CRPL;
+	node->lpn = MESH_MODE_UNSUPPORTED;
+	node->proxy = MESH_MODE_UNSUPPORTED;
+	node->friend = MESH_MODE_UNSUPPORTED;
+	node->beacon = MESH_MODE_DISABLED;
+	node->relay.mode = MESH_MODE_DISABLED;
+	node->ttl = DEFAULT_TTL;
+	node->seq_number = DEFAULT_SEQUENCE_NUMBER;
+
+	/* Add configuration server model on primary element */
+	add_internal_model(node, CONFIG_SRV_MODEL, PRIMARY_ELE_IDX);
+}
+
+static void get_managed_objects_join_cb(struct l_dbus_message *msg,
+								void *user_data)
+{
+	struct l_dbus_message_iter objects, interfaces;
+	struct join_obj_request *req = user_data;
+	const char *path;
+	struct mesh_node *node = NULL;
+	void *agent = NULL;
+
+	if (l_dbus_message_is_error(msg)) {
+		l_error("Failed to get app's dbus objects");
+		goto fail;
+	}
+
+	if (!l_dbus_message_get_arguments(msg, "a{oa{sa{sv}}}", &objects)) {
+		l_error("Failed to parse app's dbus objects");
+		goto fail;
+	}
+
+	node = l_new(struct mesh_node, 1);
+	node->elements = l_queue_new();
+
+	while (l_dbus_message_iter_next_entry(&objects, &path, &interfaces)) {
+		struct l_dbus_message_iter properties;
+		const char *interface;
+
+		while (l_dbus_message_iter_next_entry(&interfaces, &interface,
+								&properties)) {
+			bool res;
+
+			if (!strcmp(MESH_ELEMENT_INTERFACE, interface)) {
+				res = get_element_properties(node, path,
+								&properties);
+				if (!res)
+					goto fail;
+
+				node->num_ele++;
+				continue;
+
+			}
+
+			if (!strcmp(MESH_APPLICATION_INTERFACE, interface)) {
+				res = get_app_properties(node, path,
+								&properties);
+				if (!res)
+					goto fail;
+
+				continue;
+			}
+
+			if (!strcmp(MESH_PROVISION_AGENT_INTERFACE,
+								interface)) {
+				const char *sender;
+
+				sender = l_dbus_message_get_sender(msg);
+				agent = mesh_agent_create(path, sender,
+								&properties);
+				if (!agent)
+					goto fail;
+			}
+		}
+	}
+
+	if (!node->comp){
+		l_error("Interface %s not found", MESH_APPLICATION_INTERFACE);
+		goto fail;
+	}
+
+	if (!agent) {
+		l_error("Interface %s not found",
+						MESH_PROVISION_AGENT_INTERFACE);
+		goto fail;
+	}
+
+	if (!node->num_ele) {
+		l_error("Interface %s not found", MESH_ELEMENT_INTERFACE);
+		goto fail;
+	}
+
+	if (!l_queue_find(node->elements, match_element_idx,
+				L_UINT_TO_PTR(PRIMARY_ELE_IDX))) {
+
+		l_debug("Primary element not detected");
+		goto fail;
+	}
+
+	set_defaults(node);
+	memcpy(node->dev_uuid, req->uuid, 16);
+
+	if (!create_node_config(node))
+		goto fail;
+
+	req->cb(node, agent);
+
+	return;
+fail:
+	if (agent)
+		free_node_resources(node);
+
+	if (node)
+		mesh_agent_remove(agent);
+
+	req->cb(NULL, NULL);
+}
+
+/* Create a temporary pre-provisioned node */
+void node_join(const char *app_path, const char *sender, const uint8_t *uuid,
+						node_join_ready_func_t cb)
+{
+	struct join_obj_request *req;
+
+	l_debug("");
+
+	req = l_new(struct join_obj_request, 1);
+	req->uuid = uuid;
+	req->cb = cb;
+
+	l_dbus_method_call(dbus_get_bus(), sender, app_path,
+					L_DBUS_INTERFACE_OBJECT_MANAGER,
+					"GetManagedObjects", NULL,
+					get_managed_objects_join_cb,
+					req, l_free);
+}
+
+static void build_element_config(void *a, void *b)
+{
+	struct node_element *ele = a;
+	struct l_dbus_message_builder *builder = b;
+
+	l_debug("Element %u", ele->idx);
+
+	l_dbus_message_builder_enter_struct(builder, "ya(qa{sv})");
+
+	/* Element index */
+	l_dbus_message_builder_append_basic(builder, 'y', &ele->idx);
+
+	l_dbus_message_builder_enter_array(builder, "(qa{sv})");
+
+	/* Iterate over models */
+	l_queue_foreach(ele->models, model_build_config, builder);
+
+	l_dbus_message_builder_leave_array(builder);
+
+	l_dbus_message_builder_leave_struct(builder);
+}
+
+void node_build_attach_reply(struct l_dbus_message *reply, uint64_t token)
+{
+	struct mesh_node *node;
+	struct l_dbus_message_builder *builder;
+
+	node = l_queue_find(nodes, match_token, &token);
+	if (!node)
+		return;
+
+	builder = l_dbus_message_builder_new(reply);
+
+	/* Node object path */
+	l_dbus_message_builder_append_basic(builder, 'o', node->path);
+
+	/* Array of element configurations "a*/
+	l_dbus_message_builder_enter_array(builder, "(ya(qa{sv}))");
+	l_queue_foreach(node->elements, build_element_config, builder);
+	l_dbus_message_builder_leave_array(builder);
+	l_dbus_message_builder_finalize(builder);
+	l_dbus_message_builder_destroy(builder);
+}
+
+static struct l_dbus_message *send_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	const char *sender, *ele_path;
+	struct l_dbus_message_iter iter_data;
+	struct node_element *ele;
+	uint16_t dst, app_idx, src;
+	uint8_t data[MESH_MAX_ACCESS_PAYLOAD];
+	uint32_t len;
+	struct l_dbus_message *reply;
+
+	l_debug("Send");
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (strcmp(sender, node->owner))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	if (!l_dbus_message_get_arguments(msg, "oqqay", &ele_path, &dst,
+							&app_idx, &iter_data))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	ele = l_queue_find(node->elements, match_element_path, ele_path);
+	if (!ele)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+							"Element not found");
+
+	src = node_get_primary(node) + ele->idx;
+
+	l_dbus_message_iter_get_fixed_array(&iter_data, data, &len);
+	if (!len)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+						"Mesh message is empty");
+
+	if (!mesh_model_send(node, src, dst, app_idx,
+				mesh_net_get_default_ttl(node->net), data, len))
+		return dbus_error(msg, MESH_ERROR_FAILED, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	return reply;
+}
+
+static struct l_dbus_message *publish_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	const char *sender, *ele_path;
+	struct l_dbus_message_iter iter_data;
+	uint16_t mod_id, src;
+	struct node_element *ele;
+	uint8_t data[MESH_MAX_ACCESS_PAYLOAD];
+	uint32_t len;
+	struct l_dbus_message *reply;
+	int result;
+
+	l_debug("Publish");
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (strcmp(sender, node->owner))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	if (!l_dbus_message_get_arguments(msg, "oqay", &ele_path, &mod_id,
+								&iter_data))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	ele = l_queue_find(node->elements, match_element_path, ele_path);
+	if (!ele)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+							"Element not found");
+
+	src = node_get_primary(node) + ele->idx;
+
+	l_dbus_message_iter_get_fixed_array(&iter_data, data, &len);
+	if (!len)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+						"Mesh message is empty");
+
+	result = mesh_model_publish(node, VENDOR_ID_MASK | mod_id, src,
+				mesh_net_get_default_ttl(node->net), data, len);
+
+	if (result != MESH_ERROR_NONE)
+		return dbus_error(msg, result, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	return reply;
+}
+
+static struct l_dbus_message *vendor_publish_call(struct l_dbus *dbus,
+						struct l_dbus_message *msg,
+						void *user_data)
+{
+	struct mesh_node *node = user_data;
+	const char *sender, *ele_path;
+	struct l_dbus_message_iter iter_data;
+	uint16_t src;
+	uint16_t model_id, vendor;
+	uint32_t vendor_mod_id;
+	struct node_element *ele;
+	uint8_t data[MESH_MAX_ACCESS_PAYLOAD];
+	uint32_t len;
+	struct l_dbus_message *reply;
+	int result;
+
+	l_debug("Publish");
+
+	sender = l_dbus_message_get_sender(msg);
+
+	if (strcmp(sender, node->owner))
+		return dbus_error(msg, MESH_ERROR_NOT_AUTHORIZED, NULL);
+
+	if (!l_dbus_message_get_arguments(msg, "oqqay", &ele_path, &vendor,
+							&model_id, &iter_data))
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
+
+	ele = l_queue_find(node->elements, match_element_path, ele_path);
+	if (!ele)
+		return dbus_error(msg, MESH_ERROR_NOT_FOUND,
+							"Element not found");
+
+	src = node_get_primary(node) + ele->idx;
+
+	l_dbus_message_iter_get_fixed_array(&iter_data, data, &len);
+	if (!len)
+		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
+						"Mesh message is empty");
+
+	vendor_mod_id = (vendor << 16) | model_id;
+	result = mesh_model_publish(node, vendor_mod_id, src,
+				mesh_net_get_default_ttl(node->net), data, len);
+
+	if (result != MESH_ERROR_NONE)
+		return dbus_error(msg, result, NULL);
+
+	reply = l_dbus_message_new_method_return(msg);
+	l_dbus_message_set_arguments(reply, "");
+
+	return reply;
+}
+
+static void setup_node_interface(struct l_dbus_interface *iface)
+{
+	l_dbus_interface_method(iface, "Send", 0, send_call, "", "oqqay",
+						"element_path", "destination",
+						"key", "data");
+	l_dbus_interface_method(iface, "Publish", 0, publish_call, "", "oqay",
+					"element_path", "model_id", "data");
+	l_dbus_interface_method(iface, "VendorPublish", 0, vendor_publish_call,
+						"", "oqqay", "element_path",
+						"vendor", "model_id", "data");
+
+	/*TODO: Properties */
+}
+
+bool node_dbus_init(struct l_dbus *bus)
+{
+	if (!l_dbus_register_interface(bus, MESH_NODE_INTERFACE,
+						setup_node_interface,
+						NULL, false)) {
+		l_info("Unable to register %s interface", MESH_NODE_INTERFACE);
+		return false;
+	}
+
+	return true;
+}
+
+const char *node_get_owner(struct mesh_node *node)
+{
+	return node->owner;
+}
+
+const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx)
+{
+	struct node_element *ele;
+
+	ele = l_queue_find(node->elements, match_element_idx,
+							L_UINT_TO_PTR(ele_idx));
+
+	if (!ele)
+		return NULL;
+
+	return ele->path;
+}
+
+bool node_add_pending_local(struct mesh_node *node, void *prov_node_info,
+							struct mesh_io *io)
+{
+	struct mesh_prov_node_info *info = prov_node_info;
+	bool kr = !!(info->flags & PROV_FLAG_KR);
+	bool ivu = !!(info->flags & PROV_FLAG_IVU);
+
+	node->net = mesh_net_new(node);
+
+	if (!nodes)
+		nodes = l_queue_new();
+
+	l_queue_push_tail(nodes, node);
+
+	if (!storage_set_iv_index(node->net, info->iv_index, ivu))
+		return false;
+
+	mesh_net_set_iv_index(node->net, info->iv_index, ivu);
+
+	if (!mesh_db_write_uint16_hex(node->jconfig, "unicastAddress",
+								info->unicast))
+		return false;
+
+	node->primary = info->unicast;
+	mesh_net_register_unicast(node->net, info->unicast, node->num_ele);
+
+	memcpy(node->dev_key, info->device_key, 16);
+	if (!mesh_db_write_device_key(node->jconfig, info->device_key))
+		return false;
+
+	if (mesh_net_add_key(node->net, kr, info->net_index,
+			info->net_key) != MESH_STATUS_SUCCESS)
+		return false;
+
+	if (!storage_net_key_add(node->net, info->net_index, info->net_key,
+			kr ? KEY_REFRESH_PHASE_TWO : KEY_REFRESH_PHASE_NONE))
+		return false;
+
+	if (!storage_save_config(node, true, NULL, NULL))
+		return false;
+
+	/* Initialize configuration server model */
+	mesh_config_srv_init(node, PRIMARY_ELE_IDX);
+
+	mesh_net_attach(node->net, io);
+
+	return true;
+}
+
+void node_jconfig_set(struct mesh_node *node, void *jconfig)
+{
+	node->jconfig = jconfig;
+}
+
+void *node_jconfig_get(struct mesh_node *node)
+{
+	return  node->jconfig;
+}
+
+void node_cfg_file_set(struct mesh_node *node, char *cfg)
+{
+	node->cfg_file = cfg;
+}
+
+char *node_cfg_file_get(struct mesh_node *node)
+{
+	return node->cfg_file;
+}
+
+struct mesh_net *node_get_net(struct mesh_node *node)
+{
+	return node->net;
+}
diff --git a/mesh/node.h b/mesh/node.h
index f417fe503..196b54c4d 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -18,35 +18,40 @@
  *
  */
 
-#include "mesh/mesh-db.h"
-
 struct mesh_net;
 struct mesh_node;
+struct mesh_io;
+struct mesh_agent;
 
 /* To prevent local node JSON cache thrashing, minimum update times */
 #define MIN_SEQ_TRIGGER	32
 #define MIN_SEQ_CACHE		(2*MIN_SEQ_TRIGGER)
 #define MIN_SEQ_CACHE_TIME	(5*60)
 
+typedef void (*node_attach_ready_func_t) (int status, char *node_path,
+								uint64_t token);
+
+typedef void (*node_join_ready_func_t) (struct mesh_node *node,
+						struct mesh_agent *agent);
+
 struct mesh_node *node_new(void);
 void node_free(struct mesh_node *node);
+void node_join(const char *app_path, const char *sender, const uint8_t *uuid,
+						node_join_ready_func_t cb);
 uint8_t *node_uuid_get(struct mesh_node *node);
+struct mesh_net *node_get_net(struct mesh_node *node);
 struct mesh_node *node_find_by_addr(uint16_t addr);
 struct mesh_node *node_find_by_uuid(uint8_t uuid[16]);
 bool node_is_provisioned(struct mesh_node *node);
 bool node_app_key_delete(struct mesh_net *net, uint16_t addr,
 				uint16_t net_idx, uint16_t idx);
-bool node_net_key_delete(struct mesh_node *node, uint16_t index);
-bool node_set_primary(struct mesh_node *node, uint16_t unicast);
 uint16_t node_get_primary(struct mesh_node *node);
 uint16_t node_get_primary_net_idx(struct mesh_node *node);
-bool node_set_device_key(struct mesh_node *node, uint8_t key[16]);
+void node_set_device_key(struct mesh_node *node, uint8_t key[16]);
 const uint8_t *node_get_device_key(struct mesh_node *node);
 void node_set_num_elements(struct mesh_node *node, uint8_t num_ele);
 uint8_t node_get_num_elements(struct mesh_node *node);
 bool node_parse_composition(struct mesh_node *node, uint8_t *buf, uint16_t len);
-struct l_queue *node_get_net_keys(struct mesh_node *node);
-struct l_queue *node_get_app_keys(struct mesh_node *node);
 bool node_add_binding(struct mesh_node *node, uint8_t ele_idx,
 			uint32_t model_id, uint16_t app_idx);
 bool node_del_binding(struct mesh_node *node, uint8_t ele_idx,
@@ -58,12 +63,8 @@ uint32_t node_get_sequence_number(struct mesh_node *node);
 int node_get_element_idx(struct mesh_node *node, uint16_t ele_addr);
 struct l_queue *node_get_element_models(struct mesh_node *node, uint8_t ele_idx,
 								int *status);
-struct mesh_model *node_get_model(struct mesh_node *node, uint8_t ele_idx,
-						uint32_t id, int *status);
 uint16_t node_get_crpl(struct mesh_node *node);
-struct mesh_node *node_create_from_storage(struct mesh_net *net,
-						struct mesh_db_node *db_node,
-								bool local);
+bool node_init_from_storage(struct mesh_node *node, void *data);
 uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf, uint16_t sz);
 uint8_t node_lpn_mode_get(struct mesh_node *node);
 bool node_relay_mode_set(struct mesh_node *node, bool enable, uint8_t cnt,
@@ -77,4 +78,19 @@ uint8_t node_beacon_mode_get(struct mesh_node *node);
 bool node_friend_mode_set(struct mesh_node *node, bool enable);
 uint8_t node_friend_mode_get(struct mesh_node *node);
 uint32_t node_seq_cache(struct mesh_node *node);
-void node_cleanup(struct mesh_net *net);
+const char *node_get_element_path(struct mesh_node *node, uint8_t ele_idx);
+const char *node_get_owner(struct mesh_node *node);
+bool node_add_pending_local(struct mesh_node *node, void *info,
+							struct mesh_io *io);
+void node_attach_io(struct mesh_io *io);
+int node_attach(const char *app_path, const char *sender, uint64_t token,
+						node_attach_ready_func_t cb);
+void node_build_attach_reply(struct l_dbus_message *reply, uint64_t token);
+void node_id_set(struct mesh_node *node, uint16_t node_id);
+bool node_dbus_init(struct l_dbus *bus);
+void node_cleanup(void *node);
+void node_cleanup_all(void);
+void node_jconfig_set(struct mesh_node *node, void *jconfig);
+void *node_jconfig_get(struct mesh_node *node);
+void node_cfg_file_set(struct mesh_node *node, char *cfg);
+char *node_cfg_file_get(struct mesh_node *node);
-- 
2.14.5


  parent reply	other threads:[~2018-12-19  4:20 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-12-19  4:19 [PATCH BlueZ v5 00/30] Major rewrite for Multi-Node and DBus Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 01/30] mesh: Staging for Mesh DBus API rewrite Brian Gix
2018-12-24 20:56   ` Marcel Holtmann
2018-12-19  4:19 ` [PATCH BlueZ v5 02/30] mesh: Delete obsolete files Brian Gix
2018-12-24 20:57   ` Marcel Holtmann
2018-12-19  4:19 ` [PATCH BlueZ v5 03/30] mesh: Utilities for DBus support Brian Gix
2018-12-24 21:01   ` Marcel Holtmann
2018-12-19  4:19 ` [PATCH BlueZ v5 04/30] mesh: Internal errors Brian Gix
2018-12-24 21:03   ` Marcel Holtmann
2018-12-19  4:19 ` [PATCH BlueZ v5 05/30] mesh: Rewrite storage for Multiple Nodes Brian Gix
2018-12-24 21:05   ` Marcel Holtmann
2018-12-19  4:19 ` Brian Gix [this message]
2018-12-19  4:19 ` [PATCH BlueZ v5 07/30] mesh: Rewrite Network layer for multiple nodes Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 08/30] mesh: Direction agnostic PB-Adv implementation Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 09/30] mesh: Acceptor side provisioning implementation Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 10/30] mesh: Initiator " Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 11/30] mesh: Rewrite Controler interface for full init Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 12/30] mesh: Unchanged variables set to const Brian Gix
2018-12-19  4:19 ` [PATCH BlueZ v5 13/30] mesh: centralize generic utilities Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 14/30] mesh: re-arrange provisioning for DBus API Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 15/30] mesh: Re-architect " Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 16/30] mesh: Make config model handle multiple nodes Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 18/30] mesh: restructure I/O for " Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 19/30] mesh: Restructure DB to support " Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 20/30] mesh: restructure model services for " Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 21/30] mesh: DBUS interface for Provisioning Agent Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 22/30] mesh: restructure App Key storage Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 23/30] mesh: Clean-up Comment style Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 24/30] mesh: Update for DBus API and multi-node support Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 25/30] mesh: Add default location for Mesh Node storage Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 26/30] mesh: Add structural changes for mesh Brian Gix
2018-12-19  4:20 ` [PATCH BlueZ v5 27/30] mesh: Sample Provisioning Agent Brian Gix
2018-12-24 21:09   ` Marcel Holtmann
2018-12-27 19:22     ` Gix, Brian
2018-12-19  4:20 ` [PATCH BlueZ v5 28/30] mesh: Sample On/Off Client and Server Brian Gix
2018-12-24 21:10   ` Marcel Holtmann
2018-12-19  4:20 ` [PATCH BlueZ v5 30/30] mesh: Enable building Mesh Daemon Brian Gix
2018-12-24 21:12   ` Marcel Holtmann
2018-12-23 17:54 ` [PATCH BlueZ v5 00/30] Major rewrite for Multi-Node and DBus Gix, Brian
     [not found] ` <20181219042016.25538-30-brian.gix@intel.com>
2018-12-24 21:10   ` [PATCH BlueZ v5 29/30] mesh: Sample Mesh Joiner (provision acceptor) Marcel Holtmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20181219042016.25538-7-brian.gix@intel.com \
    --to=brian.gix@intel.com \
    --cc=inga.stotland@intel.com \
    --cc=johan.hedberg@gmail.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=marcel@holtmann.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).