diff mbox series

[BlueZ,v3] mesh: Move model functionality out of node.c to model.c

Message ID 20200722181449.314370-1-inga.stotland@intel.com (mailing list archive)
State Superseded
Headers show
Series [BlueZ,v3] mesh: Move model functionality out of node.c to model.c | expand

Commit Message

Stotland, Inga July 22, 2020, 6:14 p.m. UTC
This moves the model related code out of node.c to model.c providing
for better functional separation of modules.

Also, use the value of 0xFFFF for internal representation of vendor ID
for BT SIG defined models. This allows to maintain a single internal
vendor ID / model ID space.
---
 mesh/cfgmod-server.c    | 185 +++++++-------
 mesh/cfgmod.h           |   4 +-
 mesh/mesh-config-json.c |   5 +-
 mesh/model.c            | 543 +++++++++++++++++++++++++++-------------
 mesh/model.h            |  74 +++---
 mesh/node.c             | 318 ++++-------------------
 mesh/node.h             |   4 +-
 7 files changed, 555 insertions(+), 578 deletions(-)
diff mbox series

Patch

diff --git a/mesh/cfgmod-server.c b/mesh/cfgmod-server.c
index 9046a1ad9..e98782484 100644
--- a/mesh/cfgmod-server.c
+++ b/mesh/cfgmod-server.c
@@ -2,7 +2,7 @@ 
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2018-2019  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2018-2020  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -42,7 +42,7 @@  static uint8_t msg[MAX_MSG_LEN];
 
 static void send_pub_status(struct mesh_node *node, uint16_t net_idx,
 			uint16_t src, uint16_t dst,
-			uint8_t status, uint16_t ele_addr, uint32_t mod_id,
+			uint8_t status, uint16_t ele_addr, uint32_t id,
 			uint16_t pub_addr, uint16_t idx, bool cred_flag,
 			uint8_t ttl, uint8_t period, uint8_t retransmit)
 {
@@ -59,12 +59,12 @@  static void send_pub_status(struct mesh_node *node, uint16_t net_idx,
 	msg[n++] = period;
 	msg[n++] = retransmit;
 
-	if (mod_id >= VENDOR_ID_MASK) {
-		l_put_le16(mod_id, msg + n);
+	if (!IS_VENDOR(id)) {
+		l_put_le16(MODEL_ID(id), msg + n);
 		n += 2;
 	} else {
-		l_put_le16(mod_id >> 16, msg + n);
-		l_put_le16(mod_id, msg + n + 2);
+		l_put_le16(VENDOR_ID(id), msg + n);
+		l_put_le16(MODEL_ID(id), msg + n + 2);
 		n += 4;
 	}
 
@@ -76,37 +76,37 @@  static void config_pub_get(struct mesh_node *node, uint16_t net_idx,
 					uint16_t src, uint16_t dst,
 					const uint8_t *pkt, uint16_t size)
 {
-	uint32_t mod_id;
+	uint32_t id;
 	uint16_t ele_addr;
 	struct mesh_model_pub *pub;
 	int status;
 
 	if (size == 4) {
-		mod_id = l_get_le16(pkt + 2);
-		mod_id |= VENDOR_ID_MASK;
+		id = l_get_le16(pkt + 2);
+		id |= SIG_VENDOR;
 	} else if (size == 6) {
-		mod_id = l_get_le16(pkt + 2) << 16;
-		mod_id |= l_get_le16(pkt + 4);
+		id = l_get_le16(pkt + 2) << 16;
+		id |= l_get_le16(pkt + 4);
 	} else
 		return;
 
 	ele_addr = l_get_le16(pkt);
-	pub = mesh_model_pub_get(node, ele_addr, mod_id, &status);
+	pub = mesh_model_pub_get(node, ele_addr, id, &status);
 
 	if (pub && status == MESH_STATUS_SUCCESS)
 		send_pub_status(node, net_idx, src, dst, status, ele_addr,
-				mod_id, pub->addr, pub->idx, pub->credential,
+				id, pub->addr, pub->idx, pub->credential,
 				pub->ttl, pub->period, pub->retransmit);
 	else
 		send_pub_status(node, net_idx, src, dst, status, ele_addr,
-				mod_id, 0, 0, 0, 0, 0, 0);
+				id, 0, 0, 0, 0, 0, 0);
 }
 
 static void config_pub_set(struct mesh_node *node, uint16_t net_idx,
 				uint16_t src, uint16_t dst,
 				const uint8_t *pkt, bool virt, bool vendor)
 {
-	uint32_t mod_id;
+	uint32_t id;
 	uint16_t ele_addr, idx, ota = UNASSIGNED_ADDRESS;
 	const uint8_t *pub_addr;
 	uint16_t test_addr;
@@ -124,12 +124,12 @@  static void config_pub_set(struct mesh_node *node, uint16_t net_idx,
 	ttl = pkt[6];
 	period = pkt[7];
 	retransmit = pkt[8];
-	mod_id = l_get_le16(pkt + 9);
+	id = l_get_le16(pkt + 9);
 
 	if (!vendor)
-		mod_id |= VENDOR_ID_MASK;
+		id |= SIG_VENDOR;
 	else
-		mod_id = (mod_id << 16) | l_get_le16(pkt + 11);
+		id = SET_ID(id, l_get_le16(pkt + 11));
 
 	/* Don't accept virtual seeming addresses */
 	test_addr = l_get_le16(pub_addr);
@@ -139,16 +139,16 @@  static void config_pub_set(struct mesh_node *node, uint16_t net_idx,
 	cred_flag = !!(CREDFLAG_MASK & idx);
 	idx &= APP_IDX_MASK;
 
-	status = mesh_model_pub_set(node, ele_addr, mod_id, pub_addr, idx,
+	status = mesh_model_pub_set(node, ele_addr, id, pub_addr, idx,
 					cred_flag, ttl, period, retransmit,
 					virt, &ota);
 
 	l_debug("pub_set: status %d, ea %4.4x, ota: %4.4x, mod: %x, idx: %3.3x",
-					status, ele_addr, ota, mod_id, idx);
+					status, ele_addr, ota, id, idx);
 
 	if (status != MESH_STATUS_SUCCESS) {
 		send_pub_status(node, net_idx, src, dst, status, ele_addr,
-						mod_id, 0, 0, 0, 0, 0, 0);
+						id, 0, 0, 0, 0, 0, 0);
 
 		return;
 	}
@@ -158,7 +158,7 @@  static void config_pub_set(struct mesh_node *node, uint16_t net_idx,
 
 		/* Remove model publication from config file */
 		if (!mesh_config_model_pub_del(node_config_get(node), ele_addr,
-				vendor ? mod_id : mod_id & ~VENDOR_ID_MASK,
+						vendor ? id : MODEL_ID(id),
 									vendor))
 			status = MESH_STATUS_STORAGE_FAIL;
 	} else {
@@ -178,19 +178,19 @@  static void config_pub_set(struct mesh_node *node, uint16_t net_idx,
 
 		/* Save model publication to config file */
 		if (!mesh_config_model_pub_add(node_config_get(node), ele_addr,
-				vendor ? mod_id : mod_id & ~VENDOR_ID_MASK,
-					vendor, &db_pub))
+						vendor ? id : MODEL_ID(id),
+							vendor, &db_pub))
 			status = MESH_STATUS_STORAGE_FAIL;
 	}
 
-	send_pub_status(node, net_idx, src, dst, status, ele_addr, mod_id, ota,
+	send_pub_status(node, net_idx, src, dst, status, ele_addr, id, ota,
 				idx, cred_flag, ttl, period, retransmit);
 }
 
 static void send_sub_status(struct mesh_node *node, uint16_t net_idx,
 					uint16_t src, uint16_t dst,
 					uint8_t status, uint16_t ele_addr,
-					uint16_t addr, uint32_t mod)
+					uint16_t addr, uint32_t id)
 {
 	int n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_STATUS, msg);
 
@@ -199,12 +199,13 @@  static void send_sub_status(struct mesh_node *node, uint16_t net_idx,
 	n += 2;
 	l_put_le16(addr, msg + n);
 	n += 2;
-	if (mod >= 0x10000 && mod < VENDOR_ID_MASK) {
-		l_put_le16(mod >> 16, msg + n);
-		l_put_le16(mod, msg + n + 2);
+
+	if (IS_VENDOR(id)) {
+		l_put_le16(VENDOR_ID(id), msg + n);
+		l_put_le16(MODEL_ID(id), msg + n + 2);
 		n += 4;
 	} else {
-		l_put_le16(mod, msg + n);
+		l_put_le16(MODEL_ID(id), msg + n);
 		n += 2;
 	}
 
@@ -217,7 +218,7 @@  static bool config_sub_get(struct mesh_node *node, uint16_t net_idx,
 					const uint8_t *pkt, uint16_t size)
 {
 	uint16_t ele_addr;
-	uint32_t mod_id;
+	uint32_t id;
 	uint16_t n = 0;
 	int status;
 	uint8_t *msg_status;
@@ -232,34 +233,34 @@  static bool config_sub_get(struct mesh_node *node, uint16_t net_idx,
 		return false;
 
 	case 4:
-		mod_id = l_get_le16(pkt + 2);
+		id = l_get_le16(pkt + 2);
 		n = mesh_model_opcode_set(OP_CONFIG_MODEL_SUB_LIST, msg);
 		msg_status = msg + n;
 		msg[n++] = 0;
 		l_put_le16(ele_addr, msg + n);
 		n += 2;
-		l_put_le16(mod_id, msg + n);
+		l_put_le16(id, msg + n);
 		n += 2;
-		mod_id |= VENDOR_ID_MASK;
+		id |= SIG_VENDOR;
 		break;
 
 	case 6:
-		mod_id = l_get_le16(pkt + 2) << 16;
-		mod_id |= l_get_le16(pkt + 4);
+		id = l_get_le16(pkt + 2) << 16;
+		id |= l_get_le16(pkt + 4);
 		n = mesh_model_opcode_set(OP_CONFIG_VEND_MODEL_SUB_LIST, msg);
 		msg_status = msg + n;
 		msg[n++] = 0;
 		l_put_le16(ele_addr, msg + n);
 		n += 2;
-		l_put_le16(mod_id >> 16, msg + n);
+		l_put_le16(VENDOR_ID(id), msg + n);
 		n += 2;
-		l_put_le16(mod_id, msg + n);
+		l_put_le16(MODEL_ID(id), msg + n);
 		n += 2;
 		break;
 	}
 
 	buf_size = sizeof(uint16_t) * MAX_GRP_PER_MOD;
-	status = mesh_model_sub_get(node, ele_addr, mod_id, msg + n, buf_size,
+	status = mesh_model_sub_get(node, ele_addr, id, msg + n, buf_size,
 									&size);
 
 	if (status == MESH_STATUS_SUCCESS)
@@ -273,7 +274,7 @@  static bool config_sub_get(struct mesh_node *node, uint16_t net_idx,
 }
 
 static bool save_config_sub(struct mesh_node *node, uint16_t ele_addr,
-					uint32_t mod_id, bool vendor,
+					uint32_t id, bool vendor,
 					const uint8_t *addr, bool virt,
 					uint16_t grp, uint32_t opcode)
 {
@@ -287,20 +288,18 @@  static bool save_config_sub(struct mesh_node *node, uint16_t ele_addr,
 
 	if (opcode == OP_CONFIG_MODEL_SUB_VIRT_OVERWRITE ||
 					opcode == OP_CONFIG_MODEL_SUB_OVERWRITE)
-		mesh_config_model_sub_del_all(node_config_get(node),
-				ele_addr, vendor ? mod_id : mod_id & 0x0000ffff,
+		mesh_config_model_sub_del_all(node_config_get(node), ele_addr,
+						vendor ? id : MODEL_ID(id),
 									vendor);
 
 	if (opcode != OP_CONFIG_MODEL_SUB_VIRT_DELETE &&
 			opcode != OP_CONFIG_MODEL_SUB_DELETE)
 		return mesh_config_model_sub_add(node_config_get(node),
-					ele_addr,
-					vendor ? mod_id : mod_id & 0x0000ffff,
+					ele_addr, vendor ? id : MODEL_ID(id),
 					vendor, &db_sub);
 	else
 		return mesh_config_model_sub_del(node_config_get(node),
-					ele_addr,
-					vendor ? mod_id : mod_id & 0x0000ffff,
+					ele_addr, vendor ? id : MODEL_ID(id),
 					vendor, &db_sub);
 }
 
@@ -310,7 +309,7 @@  static void config_sub_set(struct mesh_node *node, uint16_t net_idx,
 					bool virt, uint32_t opcode)
 {
 	uint16_t grp, ele_addr;
-	uint32_t mod_id;
+	uint32_t id;
 	const uint8_t *addr = NULL;
 	int status = MESH_STATUS_SUCCESS;
 	bool vendor = false;
@@ -322,40 +321,47 @@  static void config_sub_set(struct mesh_node *node, uint16_t net_idx,
 	case 4:
 		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL)
 			return;
-		mod_id = l_get_le16(pkt + 2);
-		mod_id |= VENDOR_ID_MASK;
+
+		id = l_get_le16(pkt + 2);
+		id |= SIG_VENDOR;
 		break;
 	case 6:
 		if (virt)
 			return;
+
 		if (opcode != OP_CONFIG_MODEL_SUB_DELETE_ALL) {
-			mod_id = l_get_le16(pkt + 4);
-			mod_id |= VENDOR_ID_MASK;
+			id = l_get_le16(pkt + 4);
+			id |= SIG_VENDOR;
 		} else {
-			mod_id = l_get_le16(pkt + 2) << 16;
-			mod_id |= l_get_le16(pkt + 4);
+			id = l_get_le16(pkt + 2) << 16;
+			id |= l_get_le16(pkt + 4);
 			vendor = true;
 		}
+
 		break;
 	case 8:
 		if (virt)
 			return;
-		mod_id = l_get_le16(pkt + 4) << 16;
-		mod_id |= l_get_le16(pkt + 6);
+
+		id = l_get_le16(pkt + 4) << 16;
+		id |= l_get_le16(pkt + 6);
 		vendor = true;
 		break;
 	case 20:
 		if (!virt)
 			return;
-		mod_id = l_get_le16(pkt + 18);
-		mod_id |= VENDOR_ID_MASK;
+
+		id = l_get_le16(pkt + 18);
+		id |= SIG_VENDOR;
 		break;
 	case 22:
 		if (!virt)
 			return;
+
 		vendor = true;
-		mod_id = l_get_le16(pkt + 18) << 16;
-		mod_id |= l_get_le16(pkt + 20);
+		id = l_get_le16(pkt + 18) << 16;
+		id |= l_get_le16(pkt + 20);
+
 		break;
 	}
 
@@ -373,11 +379,11 @@  static void config_sub_set(struct mesh_node *node, uint16_t net_idx,
 		return;
 
 	case OP_CONFIG_MODEL_SUB_DELETE_ALL:
-		status = mesh_model_sub_del_all(node, ele_addr, mod_id);
+		status = mesh_model_sub_del_all(node, ele_addr, id);
 
 		if (status == MESH_STATUS_SUCCESS)
 			mesh_config_model_sub_del_all(node_config_get(node),
-				ele_addr, vendor ? mod_id : mod_id & 0x0000ffff,
+					ele_addr, vendor ? id : MODEL_ID(id),
 									vendor);
 		break;
 
@@ -385,22 +391,22 @@  static void config_sub_set(struct mesh_node *node, uint16_t net_idx,
 		grp = UNASSIGNED_ADDRESS;
 		/* Fall Through */
 	case OP_CONFIG_MODEL_SUB_OVERWRITE:
-		status = mesh_model_sub_ovr(node, ele_addr, mod_id,
+		status = mesh_model_sub_ovr(node, ele_addr, id,
 							addr, virt, &grp);
 
 		if (status == MESH_STATUS_SUCCESS)
-			save_config_sub(node, ele_addr, mod_id, vendor, addr,
+			save_config_sub(node, ele_addr, id, vendor, addr,
 							virt, grp, opcode);
 		break;
 	case OP_CONFIG_MODEL_SUB_VIRT_ADD:
 		grp = UNASSIGNED_ADDRESS;
 		/* Fall Through */
 	case OP_CONFIG_MODEL_SUB_ADD:
-		status = mesh_model_sub_add(node, ele_addr, mod_id,
+		status = mesh_model_sub_add(node, ele_addr, id,
 							addr, virt, &grp);
 
 		if (status == MESH_STATUS_SUCCESS &&
-				!save_config_sub(node, ele_addr, mod_id, vendor,
+				!save_config_sub(node, ele_addr, id, vendor,
 						addr, virt, grp, opcode))
 			status = MESH_STATUS_STORAGE_FAIL;
 
@@ -409,17 +415,17 @@  static void config_sub_set(struct mesh_node *node, uint16_t net_idx,
 		grp = UNASSIGNED_ADDRESS;
 		/* Fall Through */
 	case OP_CONFIG_MODEL_SUB_DELETE:
-		status = mesh_model_sub_del(node, ele_addr, mod_id, addr, virt,
+		status = mesh_model_sub_del(node, ele_addr, id, addr, virt,
 									&grp);
 
 		if (status == MESH_STATUS_SUCCESS)
-			save_config_sub(node, ele_addr, mod_id, vendor, addr,
+			save_config_sub(node, ele_addr, id, vendor, addr,
 							virt, grp, opcode);
 
 		break;
 	}
 
-	send_sub_status(node, net_idx, src, dst, status, ele_addr, grp, mod_id);
+	send_sub_status(node, net_idx, src, dst, status, ele_addr, grp, id);
 }
 
 static void send_model_app_status(struct mesh_node *node, uint16_t net_idx,
@@ -434,11 +440,13 @@  static void send_model_app_status(struct mesh_node *node, uint16_t net_idx,
 	n += 2;
 	l_put_le16(idx, msg + n);
 	n += 2;
-	if (id >= 0x10000 && id < VENDOR_ID_MASK) {
-		l_put_le16(id >> 16, msg + n);
+
+	if (IS_VENDOR(id)) {
+		l_put_le16(VENDOR_ID(id), msg + n);
 		n += 2;
 	}
-	l_put_le16(id, msg + n);
+
+	l_put_le16(MODEL_ID(id), msg + n);
 	n += 2;
 
 	mesh_model_send(node, dst, src, APP_IDX_DEV_LOCAL, net_idx, DEFAULT_TTL,
@@ -450,7 +458,7 @@  static void model_app_list(struct mesh_node *node, uint16_t net_idx,
 					const uint8_t *pkt, uint16_t size)
 {
 	uint16_t ele_addr;
-	uint32_t mod_id = 0xffff;
+	uint32_t id;
 	uint8_t *status;
 	uint16_t n;
 	int result;
@@ -463,27 +471,26 @@  static void model_app_list(struct mesh_node *node, uint16_t net_idx,
 	case 4:
 		n = mesh_model_opcode_set(OP_MODEL_APP_LIST, msg);
 		status = msg + n;
-		mod_id = l_get_le16(pkt + 2);
+		id = l_get_le16(pkt + 2);
 		l_put_le16(ele_addr, msg + 1 + n);
-		l_put_le16(mod_id, msg + 3 + n);
-		mod_id |= VENDOR_ID_MASK;
+		l_put_le16((uint16_t) id, msg + 3 + n);
+		id |= SIG_VENDOR;
 		n += 5;
 		break;
 	case 6:
 		n = mesh_model_opcode_set(OP_VEND_MODEL_APP_LIST, msg);
 		status = msg + n;
-		mod_id = l_get_le16(pkt + 2) << 16;
-		mod_id |= l_get_le16(pkt + 4);
+		id = l_get_le16(pkt + 2) << 16;
+		id |= l_get_le16(pkt + 4);
 
 		l_put_le16(ele_addr, msg + 1 + n);
-		l_put_le16(mod_id >> 16, msg + 3 + n);
-		l_put_le16(mod_id, msg + 5 + n);
+		l_put_le16((uint16_t) VENDOR_ID(id), msg + 3 + n);
+		l_put_le16((uint16_t) MODEL_ID(id), msg + 5 + n);
 		n += 7;
 		break;
 	}
 
-
-	result = mesh_model_get_bindings(node, ele_addr, mod_id, msg + n,
+	result = mesh_model_get_bindings(node, ele_addr, id, msg + n,
 						MAX_MSG_LEN - n, &size);
 	n += size;
 
@@ -500,7 +507,7 @@  static bool model_app_bind(struct mesh_node *node, uint16_t net_idx,
 					bool unbind)
 {
 	uint16_t ele_addr;
-	uint32_t mod_id;
+	uint32_t id;
 	uint16_t idx;
 	int result;
 
@@ -509,12 +516,12 @@  static bool model_app_bind(struct mesh_node *node, uint16_t net_idx,
 		return false;
 
 	case 6:
-		mod_id = l_get_le16(pkt + 4);
-		mod_id |= VENDOR_ID_MASK;
+		id = l_get_le16(pkt + 4);
+		id |= SIG_VENDOR;
 		break;
 	case 8:
-		mod_id = l_get_le16(pkt + 4) << 16;
-		mod_id |= l_get_le16(pkt + 6);
+		id = l_get_le16(pkt + 4) << 16;
+		id |= l_get_le16(pkt + 6);
 		break;
 	}
 
@@ -525,12 +532,12 @@  static bool model_app_bind(struct mesh_node *node, uint16_t net_idx,
 		return false;
 
 	if (unbind)
-		result = mesh_model_binding_del(node, ele_addr, mod_id, idx);
+		result = mesh_model_binding_del(node, ele_addr, id, idx);
 	else
-		result = mesh_model_binding_add(node, ele_addr, mod_id, idx);
+		result = mesh_model_binding_add(node, ele_addr, id, idx);
 
 	send_model_app_status(node, net_idx, src, dst, result, ele_addr,
-								mod_id, idx);
+								id, idx);
 
 	return true;
 }
diff --git a/mesh/cfgmod.h b/mesh/cfgmod.h
index 383fdbf6b..9759b01ea 100644
--- a/mesh/cfgmod.h
+++ b/mesh/cfgmod.h
@@ -17,8 +17,8 @@ 
  *
  */
 
-#define CONFIG_SRV_MODEL	(VENDOR_ID_MASK | 0x0000)
-#define CONFIG_CLI_MODEL	(VENDOR_ID_MASK | 0x0001)
+#define CONFIG_SRV_MODEL	(0x0000 | SIG_VENDOR)
+#define CONFIG_CLI_MODEL	(0x0001 | SIG_VENDOR)
 
 /* New List */
 #define OP_APPKEY_ADD				0x00
diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index 88f715fc1..deb0019f9 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -1126,18 +1126,15 @@  static bool parse_models(json_object *jmodels, struct mesh_config_element *ele)
 			if (sscanf(str, "%04x", &id) != 1)
 				goto fail;
 
-			id |= VENDOR_ID_MASK;
 		} else if (len == 8) {
 			if (sscanf(str, "%08x", &id) != 1)
 				goto fail;
+			mod->vendor = true;
 		} else
 			goto fail;
 
 		mod->id = id;
 
-		if (len == 8)
-			mod->vendor = true;
-
 		if (json_object_object_get_ex(jmodel, "bind", &jarray)) {
 			if (json_object_get_type(jarray) != json_type_array ||
 					!parse_bindings(jarray, mod))
diff --git a/mesh/model.c b/mesh/model.c
index afac6ec69..ca1009d8a 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -2,7 +2,7 @@ 
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2018-2019  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2018-2020  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -54,7 +54,6 @@  struct mesh_model {
 	bool sub_enabled;
 	bool pub_enabled;
 	uint32_t id;
-	uint8_t ele_idx;
 };
 
 struct mesh_virtual {
@@ -131,44 +130,42 @@  static bool find_virt_by_label(const void *a, const void *b)
 
 static bool match_model_id(const void *a, const void *b)
 {
-	const struct mesh_model *model = a;
+	const struct mesh_model *mod = a;
 	uint32_t id = L_PTR_TO_UINT(b);
 
-	return (mesh_model_get_model_id(model) == id);
+	return (mod->id == id);
 }
 
-static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,
-						uint32_t id, int *status)
+static int compare_model_id(const void *a, const void *b, void *user_data)
 {
-	struct l_queue *models;
-	struct mesh_model *model;
+	const struct mesh_model *mod_a = a;
+	const struct mesh_model *mod_b = b;
 
-	models = node_get_element_models(node, ele_idx, status);
-	if (!models) {
-		*status = MESH_STATUS_INVALID_MODEL;
-		return NULL;
-	}
-
-	model = l_queue_find(models, match_model_id, L_UINT_TO_PTR(id));
+	if (mod_a->id < mod_b->id)
+		return -1;
 
-	*status = (model) ? MESH_STATUS_SUCCESS : MESH_STATUS_INVALID_MODEL;
+	if (mod_a->id > mod_b->id)
+		return 1;
 
-	return model;
+	return 0;
 }
 
-static struct mesh_model *find_model(struct mesh_node *node, uint16_t addr,
-						uint32_t mod_id, int *status)
+static struct mesh_model *get_model(struct mesh_node *node, uint8_t ele_idx,
+								uint32_t id)
 {
-	int ele_idx;
-
-	ele_idx = node_get_element_idx(node, addr);
+	struct l_queue *mods;
+	struct mesh_model *mod;
 
-	if (ele_idx < 0) {
-		*status = MESH_STATUS_INVALID_ADDRESS;
+	mods = node_get_element_models(node, ele_idx);
+	if (!mods)
 		return NULL;
-	}
 
-	return get_model(node, (uint8_t) ele_idx, mod_id, status);
+	mod = l_queue_find(mods, match_model_id, L_UINT_TO_PTR(id));
+
+	if (!mod)
+		l_debug("Model not found");
+
+	return mod;
 }
 
 static uint32_t pub_period_to_ms(uint8_t pub_period)
@@ -212,15 +209,17 @@  static struct l_dbus_message *create_config_update_msg(struct mesh_node *node,
 
 	*builder = l_dbus_message_builder_new(msg);
 
-	model_id = (uint16_t) id;
+	model_id = (uint16_t) MODEL_ID(id);
 
 	l_dbus_message_builder_append_basic(*builder, 'q', &model_id);
 
 	l_dbus_message_builder_enter_array(*builder, "{sv}");
 
-	if ((id & VENDOR_ID_MASK) != VENDOR_ID_MASK) {
-		uint16_t vendor = id >> 16;
-		dbus_append_dict_entry_basic(*builder, "Vendor", "q", &vendor);
+	if (IS_VENDOR(id)) {
+		uint16_t vendor_id = (uint16_t) VENDOR_ID(id);
+
+		dbus_append_dict_entry_basic(*builder, "Vendor", "q",
+								&vendor_id);
 	}
 
 	return msg;
@@ -268,15 +267,14 @@  static void append_dict_uint16_array(struct l_dbus_message_builder *builder,
 	l_dbus_message_builder_leave_dict(builder);
 }
 
-static void config_update_model_bindings(struct mesh_node *node,
+static void cfg_update_mod_bindings(struct mesh_node *node, uint16_t ele_idx,
 							struct mesh_model *mod)
 {
 	struct l_dbus *dbus = dbus_get_bus();
 	struct l_dbus_message *msg;
 	struct l_dbus_message_builder *builder;
 
-	msg = create_config_update_msg(node, mod->ele_idx, mod->id,
-								&builder);
+	msg = create_config_update_msg(node, ele_idx, mod->id, &builder);
 	if (!msg)
 		return;
 
@@ -331,15 +329,14 @@  done:
 	l_dbus_message_builder_leave_dict(builder);
 }
 
-static void config_update_model_subscriptions(struct mesh_node *node,
+static void cfg_update_model_subs(struct mesh_node *node, uint16_t ele_idx,
 							struct mesh_model *mod)
 {
 	struct l_dbus *dbus = dbus_get_bus();
 	struct l_dbus_message *msg;
 	struct l_dbus_message_builder *builder;
 
-	msg = create_config_update_msg(node, mod->ele_idx, mod->id,
-								&builder);
+	msg = create_config_update_msg(node, ele_idx, mod->id, &builder);
 	if (!msg)
 		return;
 
@@ -572,7 +569,8 @@  done:
 	return ret;
 }
 
-static void remove_pub(struct mesh_node *node, struct mesh_model *mod)
+static void remove_pub(struct mesh_node *node, uint16_t ele_idx,
+							struct mesh_model *mod)
 {
 	if (mod->pub) {
 		if (mod->pub->virt)
@@ -584,31 +582,31 @@  static void remove_pub(struct mesh_node *node, struct mesh_model *mod)
 
 	if (!mod->cbs)
 		/* External models */
-		config_update_model_pub_period(node, mod->ele_idx, mod->id, 0);
+		config_update_model_pub_period(node, ele_idx, mod->id, 0);
 	else if (mod->cbs && mod->cbs->pub)
 		/* Internal models */
 		mod->cbs->pub(NULL);
 }
 
-static void model_unbind_idx(struct mesh_node *node, struct mesh_model *mod,
-								uint16_t idx)
+static void model_unbind_idx(struct mesh_node *node, uint16_t ele_idx,
+					struct mesh_model *mod, uint16_t idx)
 {
 	l_queue_remove(mod->bindings, L_UINT_TO_PTR(idx));
 
 	if (!mod->cbs)
 		/* External model */
-		config_update_model_bindings(node, mod);
+		cfg_update_mod_bindings(node, ele_idx, mod);
 	else if (mod->cbs->bind)
 		/* Internal model */
 		mod->cbs->bind(idx, ACTION_DELETE);
 
 	/* Remove model publication if the publication key is unbound */
 	if (mod->pub && idx == mod->pub->idx)
-		remove_pub(node, mod);
+		remove_pub(node, ele_idx, mod);
 }
 
-static void model_bind_idx(struct mesh_node *node, struct mesh_model *mod,
-								uint16_t idx)
+static void model_bind_idx(struct mesh_node *node, uint16_t ele_idx,
+					struct mesh_model *mod, uint16_t idx)
 {
 	if (!mod->bindings)
 		mod->bindings = l_queue_new();
@@ -619,7 +617,7 @@  static void model_bind_idx(struct mesh_node *node, struct mesh_model *mod,
 
 	if (!mod->cbs)
 		/* External model */
-		config_update_model_bindings(node, mod);
+		cfg_update_mod_bindings(node, ele_idx, mod);
 	else if (mod->cbs->bind)
 		/* Internal model */
 		mod->cbs->bind(idx, ACTION_ADD);
@@ -628,19 +626,18 @@  static void model_bind_idx(struct mesh_node *node, struct mesh_model *mod,
 static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id,
 						uint16_t app_idx, bool unbind)
 {
-	int status;
 	struct mesh_model *mod;
-	bool is_present, is_vendor;
+	bool is_present;
+	int ele_idx = node_get_element_idx(node, addr);
 
-	mod = find_model(node, addr, id, &status);
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
+
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod) {
-		l_debug("Model not found");
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 	}
 
-	is_vendor = id < VENDOR_ID_MASK && id > 0xffff;
-	id = !is_vendor ? (id & 0xffff) : id;
-
 	if (id == CONFIG_SRV_MODEL || id == CONFIG_CLI_MODEL)
 		return MESH_STATUS_INVALID_MODEL;
 
@@ -656,9 +653,10 @@  static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id,
 		return MESH_STATUS_SUCCESS;
 
 	if (unbind) {
-		model_unbind_idx(node, mod, app_idx);
+		model_unbind_idx(node, ele_idx, mod, app_idx);
 		if (!mesh_config_model_binding_del(node_config_get(node),
-					addr, is_vendor, id, app_idx))
+							addr, IS_VENDOR(id),
+							id, app_idx))
 			return MESH_STATUS_STORAGE_FAIL;
 
 		l_debug("Unbind key %4.4x to model %8.8x", app_idx, mod->id);
@@ -668,11 +666,11 @@  static int update_binding(struct mesh_node *node, uint16_t addr, uint32_t id,
 	if (l_queue_length(mod->bindings) >= MAX_BINDINGS)
 		return MESH_STATUS_INSUFF_RESOURCES;
 
-	if (!mesh_config_model_binding_add(node_config_get(node),
-					addr, is_vendor, id, app_idx))
+	if (!mesh_config_model_binding_add(node_config_get(node), addr,
+						IS_VENDOR(id), id, app_idx))
 		return MESH_STATUS_STORAGE_FAIL;
 
-	model_bind_idx(node, mod, app_idx);
+	model_bind_idx(node, ele_idx, mod, app_idx);
 
 	return MESH_STATUS_SUCCESS;
 
@@ -964,7 +962,6 @@  bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
 
 	is_subscription = !(IS_UNICAST(dst));
 
-
 	for (i = 0; i < num_ele; i++) {
 		struct l_queue *models;
 
@@ -974,7 +971,7 @@  bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
 		forward.unicast = addr + i;
 		forward.has_dst = false;
 
-		models = node_get_element_models(node, i, NULL);
+		models = node_get_element_models(node, i);
 
 		/* Internal models */
 		l_queue_foreach(models, forward_model, &forward);
@@ -1023,16 +1020,15 @@  done:
 	return result;
 }
 
-int mesh_model_publish(struct mesh_node *node, uint32_t mod_id,
-				uint16_t src, uint8_t ttl,
-				const void *msg, uint16_t msg_len)
+int mesh_model_publish(struct mesh_node *node, uint32_t id, uint16_t src,
+				uint8_t ttl, const void *msg, uint16_t msg_len)
 {
 	struct mesh_net *net = node_get_net(node);
 	struct mesh_model *mod;
 	uint8_t *label = NULL;
 	uint16_t net_idx;
 	bool result;
-	int status;
+	int ele_idx;
 
 	if (!net || msg_len > 380)
 		return MESH_ERROR_INVALID_ARGS;
@@ -1041,14 +1037,16 @@  int mesh_model_publish(struct mesh_node *node, uint32_t mod_id,
 	if (src == 0)
 		src = mesh_net_get_address(net);
 
-	mod = find_model(node, src, mod_id, &status);
-	if (!mod) {
-		l_debug("model %x not found", mod_id);
+	ele_idx = node_get_element_idx(node, src);
+	if (ele_idx < 0)
+		return MESH_ERROR_NOT_FOUND;
+
+	mod = get_model(node, (uint8_t) ele_idx, id);
+	if (!mod)
 		return MESH_ERROR_NOT_FOUND;
-	}
 
 	if (!mod->pub) {
-		l_debug("publication doesn't exist (model %x)", mod_id);
+		l_debug("publication doesn't exist (model %x)", id);
 		return MESH_ERROR_DOES_NOT_EXIST;
 	}
 
@@ -1093,11 +1091,14 @@  int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id,
 			bool is_virt, uint16_t *dst)
 {
 	struct mesh_model *mod;
-	int status;
+	int status, ele_idx = node_get_element_idx(node, addr);
 
-	mod = find_model(node, addr, id, &status);
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
+
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod)
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 
 	if (!mod->pub_enabled || (mod->cbs && !(mod->cbs->pub)))
 		return MESH_STATUS_INVALID_PUB_PARAM;
@@ -1110,7 +1111,7 @@  int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id,
 	 * remove the publication
 	 */
 	if (!is_virt && IS_UNASSIGNED(l_get_le16(pub_addr))) {
-		remove_pub(node, mod);
+		remove_pub(node, ele_idx, mod);
 		return MESH_STATUS_SUCCESS;
 	}
 
@@ -1134,7 +1135,7 @@  int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id,
 
 	if (!mod->cbs)
 		/* External model */
-		config_update_model_pub_period(node, mod->ele_idx, id,
+		config_update_model_pub_period(node, ele_idx, id,
 						pub_period_to_ms(period));
 	else {
 		/* Internal model, call registered callbacks */
@@ -1149,10 +1150,18 @@  struct mesh_model_pub *mesh_model_pub_get(struct mesh_node *node, uint16_t addr,
 						uint32_t mod_id, int *status)
 {
 	struct mesh_model *mod;
+	int ele_idx = node_get_element_idx(node, addr);
 
-	mod = find_model(node, addr, mod_id, status);
-	if (!mod)
+	if (ele_idx < 0) {
+		*status = MESH_STATUS_INVALID_ADDRESS;
+		return NULL;
+	}
+
+	mod = get_model(node, (uint8_t) ele_idx, mod_id);
+	if (!mod) {
+		*status = MESH_STATUS_INVALID_MODEL;
 		return NULL;
+	}
 
 	if (!mod->pub_enabled || (mod->cbs && !(mod->cbs->pub)))
 		*status = MESH_STATUS_INVALID_PUB_PARAM;
@@ -1173,12 +1182,25 @@  void mesh_model_free(void *data)
 	l_free(mod);
 }
 
-struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id)
+static void remove_subs(struct mesh_node *node, struct mesh_model *mod)
+{
+	const struct l_queue_entry *entry;
+	struct mesh_net *net = node_get_net(node);
+
+	entry = l_queue_get_entries(mod->subs);
+
+	for (; entry; entry = entry->next)
+		mesh_net_dst_unreg(net, (uint16_t) L_PTR_TO_UINT(entry->data));
+
+	l_queue_clear(mod->subs, NULL);
+	l_queue_clear(mod->virtuals, unref_virt);
+}
+
+static struct mesh_model *model_new(uint32_t id)
 {
 	struct mesh_model *mod = l_new(struct mesh_model, 1);
 
 	mod->id = id;
-	mod->ele_idx = ele_idx;
 	mod->virtuals = l_queue_new();
 
 	/*
@@ -1190,6 +1212,75 @@  struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t id)
 	return mod;
 }
 
+static void model_enable_pub(struct mesh_model *mod, bool enable)
+{
+	mod->pub_enabled = enable;
+
+	if (!mod->pub_enabled && mod->pub) {
+		if (mod->pub->virt)
+			unref_virt(mod->pub->virt);
+
+		l_free(mod->pub);
+		mod->pub = NULL;
+	}
+}
+
+static void model_enable_sub(struct mesh_node *node, struct mesh_model *mod,
+								bool enable)
+{
+	mod->sub_enabled = enable;
+
+	if (!mod->sub_enabled)
+		remove_subs(node, mod);
+}
+
+static bool get_model_options(struct mesh_model *mod,
+					struct l_dbus_message_iter *opts)
+{
+	const char *key;
+	struct l_dbus_message_iter var;
+	bool opt;
+
+	while (l_dbus_message_iter_next_entry(opts, &key, &var)) {
+
+		if (!strcmp(key, "Publish")) {
+			if (!l_dbus_message_iter_get_variant(&var, "b", &opt))
+				return false;
+
+			mod->pub_enabled = opt;
+		} else if (!strcmp(key, "Subscribe")) {
+			if (!l_dbus_message_iter_get_variant(&var, "b", &opt))
+				return false;
+
+			mod->sub_enabled = opt;
+		} else
+			return false;
+	}
+
+	return true;
+}
+
+bool mesh_model_add(struct mesh_node *node, struct l_queue *mods,
+			uint32_t id, struct l_dbus_message_iter *opts)
+{
+	struct mesh_model *mod;
+
+	/* Disallow duplicates */
+	mod = l_queue_find(mods, match_model_id, L_UINT_TO_PTR(id));
+	if (mod)
+		return false;
+
+	mod = model_new(id);
+
+	if (opts && !get_model_options(mod, opts)) {
+		mesh_model_free(mod);
+		return false;
+	}
+
+	l_queue_insert(mods, mod, compare_model_id, NULL);
+	return true;
+}
+
 /* Internal models only */
 static void restore_model_state(struct mesh_model *mod)
 {
@@ -1213,26 +1304,16 @@  static void restore_model_state(struct mesh_model *mod)
 
 }
 
-uint32_t mesh_model_get_model_id(const struct mesh_model *model)
-{
-	return model->id;
-}
-
 /* This registers an internal model, i.e. implemented within meshd */
-bool mesh_model_register(struct mesh_node *node, uint8_t ele_idx,
-					uint32_t mod_id,
+bool mesh_model_register(struct mesh_node *node, uint8_t ele_idx, uint32_t id,
 					const struct mesh_model_ops *cbs,
 					void *user_data)
 {
 	struct mesh_model *mod;
-	int status;
-
-	/* Internal models are always SIG models */
-	mod_id = VENDOR_ID_MASK | mod_id;
 
-	mod = get_model(node, ele_idx, mod_id, &status);
+	mod = get_model(node, ele_idx, id);
 	if (!mod)
-		return false;
+		return MESH_STATUS_INVALID_MODEL;
 
 	mod->cbs = cbs;
 	mod->user_data = user_data;
@@ -1242,15 +1323,15 @@  bool mesh_model_register(struct mesh_node *node, uint8_t ele_idx,
 	return true;
 }
 
-void mesh_model_app_key_delete(struct mesh_node *node, struct l_queue *models,
-							uint16_t app_idx)
+void mesh_model_app_key_delete(struct mesh_node *node, uint16_t ele_idx,
+				struct l_queue *models, uint16_t app_idx)
 {
 	const struct l_queue_entry *entry = l_queue_get_entries(models);
 
 	for (; entry; entry = entry->next) {
-		struct mesh_model *model = entry->data;
+		struct mesh_model *mod = entry->data;
 
-		model_unbind_idx(node, model, app_idx);
+		model_unbind_idx(node, ele_idx, mod, app_idx);
 	}
 }
 
@@ -1269,18 +1350,20 @@  int mesh_model_binding_add(struct mesh_node *node, uint16_t addr, uint32_t id,
 int mesh_model_get_bindings(struct mesh_node *node, uint16_t addr, uint32_t id,
 				uint8_t *buf, uint16_t buf_size, uint16_t *size)
 {
-	int status;
 	struct mesh_model *mod;
 	const struct l_queue_entry *entry;
 	uint16_t n;
 	uint32_t idx_pair;
-	int i;
+	int i, ele_idx = node_get_element_idx(node, addr);
+
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
 
-	mod = find_model(node, addr, id, &status);
+	mod = get_model(node, (uint8_t) ele_idx, id);
 
 	if (!mod) {
 		*size = 0;
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 	}
 
 	entry = l_queue_get_entries(mod->bindings);
@@ -1325,14 +1408,17 @@  done:
 int mesh_model_sub_get(struct mesh_node *node, uint16_t addr, uint32_t id,
 			uint8_t *buf, uint16_t buf_size, uint16_t *size)
 {
-	int status;
 	int16_t n;
 	struct mesh_model *mod;
 	const struct l_queue_entry *entry;
+	int ele_idx = node_get_element_idx(node, addr);
 
-	mod = find_model(node, addr, id, &status);
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
+
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod)
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 
 	if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
 		return MESH_STATUS_NOT_SUB_MOD;
@@ -1370,12 +1456,15 @@  int mesh_model_sub_get(struct mesh_node *node, uint16_t addr, uint32_t id,
 int mesh_model_sub_add(struct mesh_node *node, uint16_t addr, uint32_t id,
 			const uint8_t *group, bool is_virt, uint16_t *dst)
 {
-	int status;
 	struct mesh_model *mod;
+	int status, ele_idx = node_get_element_idx(node, addr);
+
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
 
-	mod = find_model(node, addr, id, &status);
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod)
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 
 	if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
 		return MESH_STATUS_NOT_SUB_MOD;
@@ -1387,7 +1476,7 @@  int mesh_model_sub_add(struct mesh_node *node, uint16_t addr, uint32_t id,
 
 	if (!mod->cbs)
 		/* External models */
-		config_update_model_subscriptions(node, mod);
+		cfg_update_model_subs(node, ele_idx, mod);
 
 	return MESH_STATUS_SUCCESS;
 }
@@ -1395,13 +1484,16 @@  int mesh_model_sub_add(struct mesh_node *node, uint16_t addr, uint32_t id,
 int mesh_model_sub_ovr(struct mesh_node *node, uint16_t addr, uint32_t id,
 			const uint8_t *group, bool is_virt, uint16_t *dst)
 {
-	int status;
 	struct l_queue *virtuals, *subs;
 	struct mesh_model *mod;
+	int status, ele_idx = node_get_element_idx(node, addr);
+
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
 
-	mod = find_model(node, addr, id, &status);
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod)
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 
 	if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
 		return MESH_STATUS_NOT_SUB_MOD;
@@ -1439,7 +1531,7 @@  int mesh_model_sub_ovr(struct mesh_node *node, uint16_t addr, uint32_t id,
 
 	if (!mod->cbs)
 		/* External models */
-		config_update_model_subscriptions(node, mod);
+		cfg_update_model_subs(node, ele_idx, mod);
 
 	return status;
 }
@@ -1447,13 +1539,16 @@  int mesh_model_sub_ovr(struct mesh_node *node, uint16_t addr, uint32_t id,
 int mesh_model_sub_del(struct mesh_node *node, uint16_t addr, uint32_t id,
 			const uint8_t *group, bool is_virt, uint16_t *dst)
 {
-	int status;
 	uint16_t grp;
 	struct mesh_model *mod;
+	int ele_idx = node_get_element_idx(node, addr);
 
-	mod = find_model(node, addr, id, &status);
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
+
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod)
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 
 	if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
 		return MESH_STATUS_NOT_SUB_MOD;
@@ -1481,34 +1576,23 @@  int mesh_model_sub_del(struct mesh_node *node, uint16_t addr, uint32_t id,
 
 		if (!mod->cbs)
 			/* External models */
-			config_update_model_subscriptions(node, mod);
+			cfg_update_model_subs(node, ele_idx, mod);
 	}
 
 	return MESH_STATUS_SUCCESS;
 }
 
-static void remove_subs(struct mesh_node *node, struct mesh_model *mod)
-{
-	const struct l_queue_entry *entry;
-	struct mesh_net *net = node_get_net(node);
-
-	entry = l_queue_get_entries(mod->subs);
-
-	for (; entry; entry = entry->next)
-		mesh_net_dst_unreg(net, (uint16_t) L_PTR_TO_UINT(entry->data));
-
-	l_queue_clear(mod->subs, NULL);
-	l_queue_clear(mod->virtuals, unref_virt);
-}
-
 int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id)
 {
-	int status;
 	struct mesh_model *mod;
+	int ele_idx = node_get_element_idx(node, addr);
 
-	mod = find_model(node, addr, id, &status);
+	if (ele_idx < 0)
+		return MESH_STATUS_INVALID_ADDRESS;
+
+	mod = get_model(node, (uint8_t) ele_idx, id);
 	if (!mod)
-		return status;
+		return MESH_STATUS_INVALID_MODEL;
 
 	if (!mod->sub_enabled || (mod->cbs && !(mod->cbs->sub)))
 		return MESH_STATUS_NOT_SUB_MOD;
@@ -1517,17 +1601,15 @@  int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id)
 
 	if (!mod->cbs)
 		/* External models */
-		config_update_model_subscriptions(node, mod);
+		cfg_update_model_subs(node, ele_idx, mod);
 
 	return MESH_STATUS_SUCCESS;
 }
 
-struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
-								void *data)
+static struct mesh_model *model_setup(struct mesh_net *net, uint8_t ele_idx,
+					struct mesh_config_model *db_mod)
 {
-	struct mesh_config_model *db_mod = data;
 	struct mesh_model *mod;
-	struct mesh_net *net;
 	struct mesh_config_pub *pub = db_mod->pub;
 	uint32_t i;
 
@@ -1537,8 +1619,8 @@  struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
 		return NULL;
 	}
 
-	mod = mesh_model_new(ele_idx, db_mod->vendor ? db_mod->id :
-						db_mod->id | VENDOR_ID_MASK);
+	mod = model_new(db_mod->vendor ? db_mod->id :
+						db_mod->id | SIG_VENDOR);
 
 	/* Implicitly bind config server model to device key */
 	if (db_mod->id == CONFIG_SRV_MODEL) {
@@ -1557,17 +1639,18 @@  struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
 		return mod;
 	}
 
-	net = node_get_net(node);
-
 	/* Add application key bindings if present */
 	if (db_mod->bindings) {
 		mod->bindings = l_queue_new();
 		for (i = 0; i < db_mod->num_bindings; i++)
-			model_bind_idx(node, mod, db_mod->bindings[i]);
+			l_queue_push_tail(mod->bindings,
+					L_UINT_TO_PTR(db_mod->bindings[i]));
 	}
 
-	/* Add publication if present */
-	if (pub) {
+	mod->pub_enabled = db_mod->pub_enabled;
+
+	/* Add publication if enabled and present */
+	if (mod->pub_enabled && pub) {
 		uint8_t retransmit = pub->count +
 					((pub->interval / 50 - 1) << 3);
 		if (pub->virt)
@@ -1579,8 +1662,10 @@  struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
 				pub->ttl, pub->period, retransmit);
 	}
 
-	/* Add subscriptions if present */
-	if (!db_mod->subs)
+	mod->sub_enabled = db_mod->sub_enabled;
+
+	/* Add subscriptions if enabled and present */
+	if (!db_mod->subs || !mod->sub_enabled)
 		return mod;
 
 	for (i = 0; i < db_mod->num_subs; i++) {
@@ -1605,6 +1690,59 @@  struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
 	return mod;
 }
 
+bool mesh_model_add_from_storage(struct mesh_node *node, uint8_t ele_idx,
+				struct l_queue *mods, struct l_queue *db_mods)
+{
+	struct mesh_net *net = node_get_net(node);
+	const struct l_queue_entry *entry;
+
+	/* Allow empty elements */
+	if (!db_mods)
+		return true;
+
+	entry = l_queue_get_entries(db_mods);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_model *mod;
+		struct mesh_config_model *db_mod;
+		uint32_t id;
+
+		db_mod = entry->data;
+
+		id = db_mod->vendor ? db_mod->id : db_mod->id | SIG_VENDOR;
+
+		if (l_queue_find(mods, match_model_id, L_UINT_TO_PTR(id)))
+			return false;
+
+		mod = model_setup(net, ele_idx, db_mod);
+		if (!mod)
+			return false;
+
+		l_queue_insert(mods, mod, compare_model_id, NULL);
+	}
+
+	return true;
+}
+
+void mesh_model_convert_to_storage(struct l_queue *db_mods,
+							struct l_queue *mods)
+{
+
+	const struct l_queue_entry *entry = l_queue_get_entries(mods);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_model *mod = entry->data;
+		struct mesh_config_model *db_mod;
+
+		db_mod = l_new(struct mesh_config_model, 1);
+		db_mod->id = mod->id;
+		db_mod->vendor = IS_VENDOR(mod->id);
+		db_mod->pub_enabled = mod->pub_enabled;
+		db_mod->sub_enabled = mod->sub_enabled;
+		l_queue_push_tail(db_mods, db_mod);
+	}
+}
+
 uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf)
 {
 	if (opcode <= 0x7e) {
@@ -1669,7 +1807,7 @@  bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size,
 	return true;
 }
 
-void model_build_config(void *model, void *msg_builder)
+void mesh_model_build_config(void *model, void *msg_builder)
 {
 	struct l_dbus_message_builder *builder = msg_builder;
 	struct mesh_model *mod = model;
@@ -1685,14 +1823,14 @@  void model_build_config(void *model, void *msg_builder)
 	l_dbus_message_builder_enter_struct(builder, "qa{sv}");
 
 	/* Model id */
-	id = mod->id & 0xffff;
+	id = MODEL_ID(mod->id);
 	l_dbus_message_builder_append_basic(builder, 'q', &id);
 
 	l_dbus_message_builder_enter_array(builder, "{sv}");
 
 	/* For vendor models, add vendor id */
-	if ((mod->id & VENDOR_ID_MASK) != VENDOR_ID_MASK) {
-		uint16_t vendor = mod->id >> 16;
+	if (IS_VENDOR(mod->id)) {
+		uint16_t vendor = VENDOR_ID(mod->id);
 		dbus_append_dict_entry_basic(builder, "Vendor", "q", &vendor);
 	}
 
@@ -1715,36 +1853,101 @@  void model_build_config(void *model, void *msg_builder)
 	l_dbus_message_builder_leave_struct(builder);
 }
 
-void mesh_model_enable_pub(struct mesh_model *mod, bool enable)
+void mesh_model_update_opts(struct mesh_node *node, uint8_t ele_idx,
+				struct l_queue *curr, struct l_queue *updated)
 {
-	mod->pub_enabled = enable;
+	uint16_t primary;
+	const struct l_queue_entry *entry;
 
-	if (!mod->pub_enabled && mod->pub) {
-		if (mod->pub->virt)
-			unref_virt(mod->pub->virt);
+	primary = node_get_primary(node);
+	entry = l_queue_get_entries(curr);
 
-		l_free(mod->pub);
-		mod->pub = NULL;
-	}
-}
+	for (; entry; entry = entry->next) {
+		struct mesh_model *mod, *updated_mod = entry->data;
+		uint32_t id = updated_mod->id;
+		bool updated_opt, vendor = IS_VENDOR(id);
 
-bool mesh_model_is_pub_enabled(struct mesh_model *mod)
-{
-	return mod->pub_enabled;
-}
+		mod = l_queue_find(curr, match_model_id, L_UINT_TO_PTR(id));
+		if (!mod)
+			continue;
 
-void mesh_model_enable_sub(struct mesh_node *node, struct mesh_model *mod,
-								bool enable)
-{
-	mod->sub_enabled = enable;
+		if (!vendor)
+			id = MODEL_ID(id);
 
-	if (!mod->sub_enabled)
-		remove_subs(node, mod);
+		updated_opt = updated_mod->pub_enabled;
+		if (mod->pub_enabled != updated_opt) {
+			model_enable_pub(mod, updated_opt);
+			mesh_config_model_pub_enable(node_config_get(node),
+							primary + ele_idx, id,
+							vendor, updated_opt);
+		}
+
+		updated_opt = updated_mod->sub_enabled;
+
+		if (mod->pub_enabled != updated_opt) {
+			model_enable_sub(node, mod, updated_opt);
+			mesh_config_model_sub_enable(node_config_get(node),
+							primary + ele_idx, id,
+							vendor, updated_opt);
+			}
+		}
 }
 
-bool mesh_model_is_sub_enabled(struct mesh_model *mod)
+/* Populate composition buffer with model IDs */
+uint16_t mesh_model_generate_composition(struct l_queue *mods, uint16_t buf_sz,
+								uint8_t *buf)
 {
-	return mod->sub_enabled;
+	const struct l_queue_entry *entry;
+	uint8_t num_s = 0, num_v = 0;
+	uint8_t *mod_buf;
+	uint16_t n;
+
+	/* Store models IDs, store num_s and num_v later */
+	mod_buf = buf;
+	n = 2;
+
+	entry = l_queue_get_entries(mods);
+
+	/* Get SIG models */
+	for (; entry; entry = entry->next) {
+		struct mesh_model *mod = entry->data;
+
+		if (n + 2 > buf_sz)
+			goto done;
+
+		if (IS_VENDOR(mod->id))
+			continue;
+
+		l_put_le16((uint16_t) (MODEL_ID(mod->id)), buf + n);
+		n += 2;
+		num_s++;
+	}
+
+	/* Get vendor models */
+	entry = l_queue_get_entries(mods);
+
+	for (; entry; entry = entry->next) {
+		struct mesh_model *mod = entry->data;
+		uint16_t vendor_id;
+
+		if (n + 4 > buf_sz)
+			goto done;
+
+		if (!IS_VENDOR(mod->id))
+			continue;
+
+		vendor_id = (uint16_t) (VENDOR_ID(mod->id));
+		l_put_le16(vendor_id, buf + n);
+		n += 2;
+		l_put_le16((uint16_t) (MODEL_ID(mod->id)), buf + n);
+		n += 2;
+		num_v++;
+	}
+
+done:
+	mod_buf[0] = num_s;
+	mod_buf[1] = num_v;
+	return n;
 }
 
 void mesh_model_init(void)
diff --git a/mesh/model.h b/mesh/model.h
index 0377d3fdd..fe5a0e736 100644
--- a/mesh/model.h
+++ b/mesh/model.h
@@ -2,7 +2,7 @@ 
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2018  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2018-2020  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -22,14 +22,17 @@  struct mesh_model;
 #define MAX_BINDINGS	10
 #define MAX_GRP_PER_MOD	10
 
-#define OP_MODEL_TEST			0x8000fffe
-#define OP_MODEL_INVALID		0x8000ffff
+#define ACTION_ADD	1
+#define ACTION_UPDATE	2
+#define ACTION_DELETE	3
 
-#define USE_PUB_VALUE			0x00
+/* For internal representation of SIG defined models */
+#define SIG_VENDOR	0xFFFF0000
 
-#define ACTION_ADD		1
-#define ACTION_UPDATE		2
-#define ACTION_DELETE		3
+#define IS_VENDOR(x)	((x) < SIG_VENDOR)
+#define SET_ID(v, m)	((((uint32_t) (v)) << 16) | (m))
+#define MODEL_ID(x)	((x) & ~VENDOR_ID_MASK)
+#define VENDOR_ID(x)	((x) >> 16)
 
 struct mesh_virtual;
 
@@ -60,61 +63,62 @@  struct mesh_model_ops {
 	mesh_model_sub_cb sub;
 };
 
-struct mesh_model *mesh_model_new(uint8_t ele_idx, uint32_t mod_id);
+bool mesh_model_add(struct mesh_node *node, struct l_queue *mods,
+			uint32_t id, struct l_dbus_message_iter *opts);
 void mesh_model_free(void *data);
-uint32_t mesh_model_get_model_id(const struct mesh_model *model);
 bool mesh_model_register(struct mesh_node *node, uint8_t ele_idx,
-			uint32_t mod_id, const struct mesh_model_ops *cbs,
+			uint32_t id, const struct mesh_model_ops *cbs,
 							void *user_data);
-struct mesh_model *mesh_model_setup(struct mesh_node *node, uint8_t ele_idx,
-								void *data);
+bool mesh_model_add_from_storage(struct mesh_node *node, uint8_t ele_idx,
+				struct l_queue *mods, struct l_queue *db_mods);
+void mesh_model_convert_to_storage(struct l_queue *db_mods,
+							struct l_queue *mods);
 struct mesh_model_pub *mesh_model_pub_get(struct mesh_node *node,
-				uint16_t addr, uint32_t mod_id, int *status);
-int mesh_model_pub_set(struct mesh_node *node, uint16_t addr, uint32_t id,
-			const uint8_t *pub_addr, uint16_t idx, bool cred_flag,
+						uint16_t ele_addr,
+						uint32_t id, int *status);
+int mesh_model_pub_set(struct mesh_node *node, uint16_t ele_addr, uint32_t id,
+			const uint8_t *addr, uint16_t idx, bool cred_flag,
 			uint8_t ttl, uint8_t period, uint8_t retransmit,
 			bool is_virt, uint16_t *dst);
 
-int mesh_model_binding_add(struct mesh_node *node, uint16_t addr, uint32_t id,
-								uint16_t idx);
-int mesh_model_binding_del(struct mesh_node *node, uint16_t addr, uint32_t id,
-								uint16_t idx);
-int mesh_model_get_bindings(struct mesh_node *node, uint16_t addr, uint32_t id,
-				uint8_t *buf, uint16_t buf_len, uint16_t *size);
-int mesh_model_sub_add(struct mesh_node *node, uint16_t addr, uint32_t id,
+int mesh_model_binding_add(struct mesh_node *node, uint16_t ele_addr,
+						uint32_t id, uint16_t idx);
+int mesh_model_binding_del(struct mesh_node *node, uint16_t ele_addr,
+						uint32_t id, uint16_t idx);
+int mesh_model_get_bindings(struct mesh_node *node, uint16_t ele_addr,
+				uint32_t id, uint8_t *buf, uint16_t buf_sz,
+								uint16_t *len);
+int mesh_model_sub_add(struct mesh_node *node, uint16_t ele_addr, uint32_t id,
 				const uint8_t *grp, bool b_virt, uint16_t *dst);
-int mesh_model_sub_del(struct mesh_node *node, uint16_t addr, uint32_t id,
+int mesh_model_sub_del(struct mesh_node *node, uint16_t ele_addr, uint32_t id,
 				const uint8_t *grp, bool b_virt, uint16_t *dst);
 int mesh_model_sub_del_all(struct mesh_node *node, uint16_t addr, uint32_t id);
 int mesh_model_sub_ovr(struct mesh_node *node, uint16_t addr, uint32_t id,
 				const uint8_t *grp, bool b_virt, uint16_t *dst);
-int mesh_model_sub_get(struct mesh_node *node, uint16_t addr, uint32_t id,
+int mesh_model_sub_get(struct mesh_node *node, uint16_t ele_addr, uint32_t id,
 			uint8_t *buf, uint16_t buf_size, uint16_t *size);
 uint16_t mesh_model_cfg_blk(uint8_t *pkt);
 bool mesh_model_send(struct mesh_node *node, uint16_t src, uint16_t dst,
 					uint16_t app_idx, uint16_t net_idx,
 					uint8_t ttl, bool segmented,
 					const void *msg, uint16_t msg_len);
-int mesh_model_publish(struct mesh_node *node, uint32_t mod_id, uint16_t src,
+int mesh_model_publish(struct mesh_node *node, uint32_t id, uint16_t src,
 				uint8_t ttl, const void *msg, uint16_t msg_len);
 bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
 			uint32_t seq, uint32_t iv_index, uint16_t net_idx,
 			uint16_t src, uint16_t dst, uint8_t key_aid,
 			const uint8_t *data, uint16_t size);
 void mesh_model_app_key_generate_new(struct mesh_node *node, uint16_t net_idx);
-void mesh_model_app_key_delete(struct mesh_node *node, struct l_queue *models,
-								uint16_t idx);
+void mesh_model_app_key_delete(struct mesh_node *node, uint16_t ele_idx,
+				struct l_queue *models, uint16_t app_idx);
 struct l_queue *mesh_model_get_appkeys(struct mesh_node *node);
 uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf);
 bool mesh_model_opcode_get(const uint8_t *buf, uint16_t size, uint32_t *opcode,
 								uint16_t *n);
-void model_build_config(void *model, void *msg_builder);
-
-void mesh_model_enable_pub(struct mesh_model *mod, bool enable);
-bool mesh_model_is_pub_enabled(struct mesh_model *mod);
-void mesh_model_enable_sub(struct mesh_node *node, struct mesh_model *mod,
-								bool enable);
-bool mesh_model_is_sub_enabled(struct mesh_model *mod);
-
+void mesh_model_build_config(void *model, void *msg_builder);
+void mesh_model_update_opts(struct mesh_node *node, uint8_t ele_idx,
+				struct l_queue *curr, struct l_queue *updated);
+uint16_t mesh_model_generate_composition(struct l_queue *mods, uint16_t buf_sz,
+								uint8_t *buf);
 void mesh_model_init(void);
 void mesh_model_cleanup(void);
diff --git a/mesh/node.c b/mesh/node.c
index 9b97aa927..395b7d3c4 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -2,7 +2,7 @@ 
  *
  *  BlueZ - Bluetooth protocol stack for Linux
  *
- *  Copyright (C) 2017-2019  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2017-2020  Intel Corporation. All rights reserved.
  *
  *
  *  This library is free software; you can redistribute it and/or
@@ -186,28 +186,6 @@  static bool match_element_path(const void *a, const void *b)
 	return (!strcmp(element->path, path));
 }
 
-static bool match_model_id(const void *a, const void *b)
-{
-	const struct mesh_model *mod = a;
-	uint32_t mod_id = L_PTR_TO_UINT(b);
-
-	return mesh_model_get_model_id(mod) == mod_id;
-}
-
-static int compare_model_id(const void *a, const void *b, void *user_data)
-{
-	uint32_t a_id = mesh_model_get_model_id(a);
-	uint32_t b_id = mesh_model_get_model_id(b);
-
-	if (a_id < b_id)
-		return -1;
-
-	if (a_id > b_id)
-		return 1;
-
-	return 0;
-}
-
 struct mesh_node *node_find_by_uuid(uint8_t uuid[16])
 {
 	return l_queue_find(nodes, match_device_uuid, uuid);
@@ -225,25 +203,6 @@  uint8_t *node_uuid_get(struct mesh_node *node)
 	return node->uuid;
 }
 
-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;
-
-	ele = l_queue_find(node->elements, match_element_idx,
-							L_UINT_TO_PTR(ele_idx));
-	if (!ele)
-		return;
-
-	if (l_queue_find(ele->models, match_model_id, L_UINT_TO_PTR(mod_id)))
-		return;
-
-	mod = mesh_model_new(ele_idx, mod_id);
-
-	l_queue_insert(ele->models, mod, compare_model_id, NULL);
-}
-
 static void set_defaults(struct mesh_node *node)
 {
 	node->lpn = MESH_MODE_UNSUPPORTED;
@@ -359,46 +318,6 @@  void node_remove(struct mesh_node *node)
 	free_node_resources(node);
 }
 
-static bool add_models_from_storage(struct mesh_node *node,
-					struct node_element *ele,
-					struct mesh_config_element *db_ele)
-{
-	const struct l_queue_entry *entry;
-
-	if (!ele->models)
-		ele->models = l_queue_new();
-
-	entry = l_queue_get_entries(db_ele->models);
-
-	for (; entry; entry = entry->next) {
-		struct mesh_model *mod;
-		struct mesh_config_model *db_mod;
-		uint32_t id;
-
-		db_mod = entry->data;
-
-		id = db_mod->vendor ? db_mod->id : db_mod->id | VENDOR_ID_MASK;
-
-		if (l_queue_find(ele->models, match_model_id,
-							L_UINT_TO_PTR(id)))
-			return false;
-
-		mod = mesh_model_setup(node, ele->idx, db_mod);
-		if (!mod)
-			return false;
-
-		if (!db_mod->pub_enabled)
-			mesh_model_enable_pub(mod, false);
-
-		if (!db_mod->sub_enabled)
-			mesh_model_enable_sub(node, mod, false);
-
-		l_queue_insert(ele->models, mod, compare_model_id, NULL);
-	}
-
-	return true;
-}
-
 static bool add_element_from_storage(struct mesh_node *node,
 					struct mesh_config_element *db_ele)
 {
@@ -411,7 +330,12 @@  static bool add_element_from_storage(struct mesh_node *node,
 	ele->idx = db_ele->index;
 	ele->location = db_ele->location;
 
-	if (!db_ele->models || !add_models_from_storage(node, ele, db_ele))
+
+	if (!ele->models)
+		ele->models = l_queue_new();
+
+	if (!mesh_model_add_from_storage(node, ele->idx, ele->models,
+							db_ele->models))
 		return false;
 
 	l_queue_push_tail(node->elements, ele);
@@ -424,12 +348,13 @@  static bool add_elements_from_storage(struct mesh_node *node,
 	const struct l_queue_entry *entry;
 
 	entry = l_queue_get_entries(db_node->elements);
+
 	for (; entry; entry = entry->next)
 		if (!add_element_from_storage(node, entry->data))
 			return false;
 
 	/* Add configuration server model on the primary element */
-	add_internal_model(node, CONFIG_SRV_MODEL, PRIMARY_ELE_IDX);
+	mesh_model_add(node, PRIMARY_ELE_IDX, CONFIG_SRV_MODEL, NULL);
 
 	return true;
 }
@@ -628,7 +553,7 @@  void node_app_key_delete(struct mesh_node *node, uint16_t net_idx,
 	for (; entry; entry = entry->next) {
 		struct node_element *ele = entry->data;
 
-		mesh_model_app_key_delete(node, ele->models, app_idx);
+		mesh_model_app_key_delete(node, ele->idx, ele->models, app_idx);
 	}
 }
 
@@ -666,27 +591,17 @@  uint8_t node_get_num_elements(struct mesh_node *node)
 	return node->num_ele;
 }
 
-struct l_queue *node_get_element_models(struct mesh_node *node,
-						uint8_t ele_idx, int *status)
+struct l_queue *node_get_element_models(struct mesh_node *node, uint8_t ele_idx)
 {
 	struct node_element *ele;
 
-	if (!node) {
-		if (status)
-			*status = MESH_STATUS_INVALID_ADDRESS;
+	if (!node)
 		return NULL;
-	}
 
 	ele = l_queue_find(node->elements, match_element_idx,
 							L_UINT_TO_PTR(ele_idx));
-	if (!ele) {
-		if (status)
-			*status = MESH_STATUS_INVALID_ADDRESS;
+	if (!ele)
 		return NULL;
-	}
-
-	if (status)
-		*status = MESH_STATUS_SUCCESS;
 
 	return ele->models;
 }
@@ -888,9 +803,8 @@  uint8_t node_friend_mode_get(struct mesh_node *node)
 static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
 								uint16_t sz)
 {
-	uint16_t n, features;
-	uint16_t num_ele = 0;
-	const struct l_queue_entry *ele_entry;
+	uint16_t n, features, num_ele = 0;
+	const struct l_queue_entry *entry;
 
 	if (!node || sz < MIN_COMP_SIZE)
 		return 0;
@@ -920,12 +834,10 @@  static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
 	l_put_le16(features, buf + n);
 	n += 2;
 
-	ele_entry = l_queue_get_entries(node->elements);
-	for (; ele_entry; ele_entry = ele_entry->next) {
-		struct node_element *ele = ele_entry->data;
-		const struct l_queue_entry *mod_entry;
-		uint8_t num_s = 0, num_v = 0;
-		uint8_t *mod_buf;
+	entry = l_queue_get_entries(node->elements);
+
+	for (; entry; entry = entry->next) {
+		struct node_element *ele = entry->data;
 
 		if (ele->idx != num_ele)
 			return 0;
@@ -939,59 +851,8 @@  static uint16_t node_generate_comp(struct mesh_node *node, uint8_t *buf,
 		l_put_le16(ele->location, buf + n);
 		n += 2;
 
-		/* Store models IDs, store num_s and num_v later */
-		mod_buf = buf + n;
-		n += 2;
-
-		/* Get SIG models */
-		mod_entry = l_queue_get_entries(ele->models);
-		for (; mod_entry; mod_entry = mod_entry->next) {
-			struct mesh_model *mod = mod_entry->data;
-			uint32_t mod_id;
-
-			mod_id = mesh_model_get_model_id(
-					(const struct mesh_model *) mod);
-
-			if ((mod_id & VENDOR_ID_MASK) == VENDOR_ID_MASK) {
-				if (n + 2 > sz)
-					goto element_done;
-
-				l_put_le16((uint16_t) (mod_id & 0xffff),
-								buf + n);
-				n += 2;
-				num_s++;
-			}
-		}
-
-		/* Get vendor models */
-		mod_entry = l_queue_get_entries(ele->models);
-		for (; mod_entry; mod_entry = mod_entry->next) {
-			struct mesh_model *mod = mod_entry->data;
-			uint32_t mod_id;
-			uint16_t vendor;
-
-			mod_id = mesh_model_get_model_id(
-					(const struct mesh_model *) mod);
-
-			vendor = (uint16_t) (mod_id >> 16);
-			if (vendor != 0xffff) {
-				if (n + 4 > sz)
-					goto element_done;
-
-				l_put_le16(vendor, buf + n);
-				n += 2;
-				l_put_le16((uint16_t) (mod_id & 0xffff),
+		n += mesh_model_generate_composition(ele->models, sz - n,
 								buf + n);
-				n += 2;
-				num_v++;
-			}
-
-		}
-
-element_done:
-		mod_buf[0] = num_s;
-		mod_buf[1] = num_v;
-
 	}
 
 	if (!num_ele)
@@ -1128,52 +989,6 @@  static void app_disc_cb(struct l_dbus *bus, void *user_data)
 	free_node_dbus_resources(node);
 }
 
-static bool get_model_options(struct mesh_node *node, struct mesh_model *mod,
-					struct l_dbus_message_iter *opts)
-{
-	const char *key;
-	struct l_dbus_message_iter var;
-	bool opt;
-
-	while (l_dbus_message_iter_next_entry(opts, &key, &var)) {
-
-		if (!strcmp(key, "Publish")) {
-			if (!l_dbus_message_iter_get_variant(&var, "b", &opt))
-				return false;
-			mesh_model_enable_pub(mod, opt);
-		} else if (!strcmp(key, "Subscribe")) {
-			if (!l_dbus_message_iter_get_variant(&var, "b", &opt))
-				return false;
-			mesh_model_enable_sub(node, mod, opt);
-		} else
-			return false;
-	}
-
-	return true;
-}
-
-static bool generate_model(struct mesh_node *node, struct node_element *ele,
-				uint32_t id, struct l_dbus_message_iter *opts)
-{
-	struct mesh_model *mod;
-
-	/* Disallow duplicates */
-	if (l_queue_find(ele->models, match_model_id,
-			 L_UINT_TO_PTR(id)))
-		return false;
-
-	mod = mesh_model_new(ele->idx, id);
-
-	if (!get_model_options(node, mod, opts)) {
-		l_free(mod);
-		return false;
-	}
-
-	l_queue_insert(ele->models, mod, compare_model_id, NULL);
-
-	return true;
-}
-
 static bool get_sig_models_from_properties(struct mesh_node *node,
 					struct node_element *ele,
 					struct l_dbus_message_iter *property)
@@ -1189,13 +1004,13 @@  static bool get_sig_models_from_properties(struct mesh_node *node,
 
 	/* Bluetooth SIG defined models */
 	while (l_dbus_message_iter_next_entry(&mods, &m_id, &var)) {
-		uint32_t id = m_id | VENDOR_ID_MASK;
+		uint32_t id = SIG_VENDOR | m_id;
 
 		/* Allow Config Server Model only on the primary element */
 		if (ele->idx != PRIMARY_ELE_IDX && id == CONFIG_SRV_MODEL)
 			return false;
 
-		if (!generate_model(node, ele, id, &var))
+		if (!mesh_model_add(node, ele->models, id, &var))
 			return false;
 	}
 
@@ -1217,9 +1032,9 @@  static bool get_vendor_models_from_properties(struct mesh_node *node,
 
 	/* Vendor defined models */
 	while (l_dbus_message_iter_next_entry(&mods, &v_id, &m_id, &var)) {
-		uint32_t id = m_id | (v_id << 16);
+		uint32_t id = SET_ID(v_id, m_id);
 
-		if (!generate_model(node, ele, id, &var))
+		if (!mesh_model_add(node, ele->models, id, &var))
 			return false;
 	}
 
@@ -1295,7 +1110,7 @@  static bool get_element_properties(struct mesh_node *node, const char *path,
 	 * the operation below will be a "no-op".
 	 */
 	if (ele->idx == PRIMARY_ELE_IDX)
-		add_internal_model(node, CONFIG_SRV_MODEL, PRIMARY_ELE_IDX);
+		mesh_model_add(node, ele->models, CONFIG_SRV_MODEL, NULL);
 
 	return true;
 fail:
@@ -1332,7 +1147,6 @@  static void convert_node_to_storage(struct mesh_node *node,
 	for (; entry; entry = entry->next) {
 		struct node_element *ele = entry->data;
 		struct mesh_config_element *db_ele;
-		const struct l_queue_entry *mod_entry;
 
 		db_ele = l_new(struct mesh_config_element, 1);
 
@@ -1340,21 +1154,8 @@  static void convert_node_to_storage(struct mesh_node *node,
 		db_ele->location = ele->location;
 		db_ele->models = l_queue_new();
 
-		mod_entry = l_queue_get_entries(ele->models);
+		mesh_model_convert_to_storage(db_ele->models, ele->models);
 
-		for (; mod_entry; mod_entry = mod_entry->next) {
-			struct mesh_model *mod = mod_entry->data;
-			struct mesh_config_model *db_mod;
-			uint32_t mod_id = mesh_model_get_model_id(mod);
-
-			db_mod = l_new(struct mesh_config_model, 1);
-			db_mod->id = mod_id;
-			db_mod->vendor = ((mod_id & VENDOR_ID_MASK)
-							!= VENDOR_ID_MASK);
-			db_mod->pub_enabled = mesh_model_is_pub_enabled(mod);
-			db_mod->sub_enabled = mesh_model_is_sub_enabled(mod);
-			l_queue_push_tail(db_ele->models, db_mod);
-		}
 		l_queue_push_tail(db_node->elements, db_ele);
 	}
 
@@ -1375,6 +1176,7 @@  static bool create_node_config(struct mesh_node *node, const uint8_t uuid[16])
 
 	/* Free temporarily allocated resources */
 	entry = l_queue_get_entries(db_node.elements);
+
 	for (; entry; entry = entry->next) {
 		struct mesh_config_element *db_ele = entry->data;
 
@@ -1517,7 +1319,6 @@  static void update_model_options(struct mesh_node *node,
 	len = l_queue_length(node->elements);
 
 	for (i = 0; i < len; i++) {
-		const struct l_queue_entry *entry;
 
 		ele = l_queue_find(node->elements, match_element_idx,
 							L_UINT_TO_PTR(i));
@@ -1526,42 +1327,8 @@  static void update_model_options(struct mesh_node *node,
 		if (!ele || !ele_attach)
 			continue;
 
-		entry = l_queue_get_entries(ele->models);
-
-		for (; entry; entry = entry->next) {
-			struct mesh_model *mod, *updated_mod = entry->data;
-			uint32_t id = mesh_model_get_model_id(updated_mod);
-			bool opt, updated_opt;
-			bool vendor = id < VENDOR_ID_MASK;
-
-			mod = l_queue_find(ele_attach->models, match_model_id,
-							L_UINT_TO_PTR(id));
-			if (!mod)
-				continue;
-
-			if (!vendor)
-				id &= ~VENDOR_ID_MASK;
-
-			opt = mesh_model_is_pub_enabled(mod);
-			updated_opt = mesh_model_is_pub_enabled(updated_mod);
-
-			if (updated_opt != opt) {
-				mesh_model_enable_pub(mod, updated_opt);
-				mesh_config_model_pub_enable(attach->cfg,
-							attach->primary + i, id,
-							vendor, updated_opt);
-			}
-
-			opt = mesh_model_is_sub_enabled(mod);
-			updated_opt = mesh_model_is_sub_enabled(updated_mod);
-
-			if (updated_opt != opt) {
-				mesh_model_enable_sub(node, mod, updated_opt);
-				mesh_config_model_sub_enable(attach->cfg,
-							attach->primary + i, id,
-							vendor, updated_opt);
-			}
-		}
+		mesh_model_update_opts(node, ele->idx, ele_attach->models,
+								ele->models);
 	}
 }
 
@@ -1964,7 +1731,7 @@  static void build_element_config(void *a, void *b)
 	l_dbus_message_builder_enter_array(builder, "(qa{sv})");
 
 	/* Iterate over models */
-	l_queue_foreach(ele->models, model_build_config, builder);
+	l_queue_foreach(ele->models, mesh_model_build_config, builder);
 
 	l_dbus_message_builder_leave_array(builder);
 
@@ -2216,8 +1983,8 @@  static struct l_dbus_message *publish_call(struct l_dbus *dbus,
 	struct l_dbus_message_iter iter_data;
 	uint16_t mod_id, src;
 	struct node_element *ele;
-	uint8_t *data;
-	uint32_t len;
+	uint8_t *data, ttl;
+	uint32_t len, id;
 	int result;
 
 	l_debug("Publish");
@@ -2243,8 +2010,9 @@  static struct l_dbus_message *publish_call(struct l_dbus *dbus,
 		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
 							"Incorrect data");
 
-	result = mesh_model_publish(node, VENDOR_ID_MASK | mod_id, src,
-				mesh_net_get_default_ttl(node->net), data, len);
+	ttl = mesh_net_get_default_ttl(node->net);
+	id = SIG_VENDOR | mod_id;
+	result = mesh_model_publish(node, id, src, ttl, data, len);
 
 	if (result != MESH_ERROR_NONE)
 		return dbus_error(msg, result, NULL);
@@ -2259,11 +2027,9 @@  static struct l_dbus_message *vendor_publish_call(struct l_dbus *dbus,
 	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;
+	uint16_t src, mod_id, vendor_id;
 	struct node_element *ele;
-	uint8_t *data = NULL;
+	uint8_t ttl, *data = NULL;
 	uint32_t len;
 	int result;
 
@@ -2274,8 +2040,8 @@  static struct l_dbus_message *vendor_publish_call(struct l_dbus *dbus,
 	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))
+	if (!l_dbus_message_get_arguments(msg, "oqqay", &ele_path, &vendor_id,
+							&mod_id, &iter_data))
 		return dbus_error(msg, MESH_ERROR_INVALID_ARGS, NULL);
 
 	ele = l_queue_find(node->elements, match_element_path, ele_path);
@@ -2290,9 +2056,9 @@  static struct l_dbus_message *vendor_publish_call(struct l_dbus *dbus,
 		return dbus_error(msg, MESH_ERROR_INVALID_ARGS,
 							"Incorrect data");
 
-	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);
+	ttl = mesh_net_get_default_ttl(node->net);
+	result = mesh_model_publish(node, SET_ID(vendor_id, mod_id), src, ttl,
+								data, len);
 
 	if (result != MESH_ERROR_NONE)
 		return dbus_error(msg, result, NULL);
diff --git a/mesh/node.h b/mesh/node.h
index df058458a..47c998530 100644
--- a/mesh/node.h
+++ b/mesh/node.h
@@ -58,8 +58,8 @@  bool node_default_ttl_set(struct mesh_node *node, uint8_t ttl);
 bool node_set_sequence_number(struct mesh_node *node, uint32_t seq);
 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 l_queue *node_get_element_models(struct mesh_node *node,
+							uint8_t ele_idx);
 uint16_t node_get_crpl(struct mesh_node *node);
 bool node_init_from_storage(struct mesh_node *node, const uint8_t uuid[16],
 					struct mesh_config_node *db_node);