1 /*
2  * Copyright (c) 2017 Linaro Limited
3  * Copyright (c) 2018-2019 Foundries.io
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /*
9  * Uses some original concepts by:
10  *         Joakim Eriksson <joakime@sics.se>
11  *         Niclas Finne <nfi@sics.se>
12  *         Joel Hoglund <joel@sics.se>
13  */
14 
15 #define LOG_MODULE_NAME net_lwm2m_observation
16 #define LOG_LEVEL	CONFIG_LWM2M_LOG_LEVEL
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
20 
21 #include "lwm2m_engine.h"
22 #include "lwm2m_object.h"
23 #include "lwm2m_util.h"
24 #include "lwm2m_rd_client.h"
25 
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stddef.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include <zephyr/init.h>
34 #include <zephyr/net/http/parser_url.h>
35 #include <zephyr/net/lwm2m.h>
36 #include <zephyr/net/net_ip.h>
37 #include <zephyr/net/socket.h>
38 #include <zephyr/sys/printk.h>
39 #include <zephyr/types.h>
40 #include "lwm2m_obj_server.h"
41 
42 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
43 #include "lwm2m_rw_senml_json.h"
44 #endif
45 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
46 #include "lwm2m_rw_json.h"
47 #endif
48 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
49 #include "lwm2m_rw_cbor.h"
50 #endif
51 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
52 #include "lwm2m_rw_senml_cbor.h"
53 #endif
54 
55 #define OBSERVE_COUNTER_START 0U
56 
57 #if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
58 #define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
59 #else
60 #define COAP_OPTION_BUF_LEN 13
61 #endif
62 
63 /* Resources */
64 static sys_slist_t obs_obj_path_list;
65 
66 static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER];
67 
68 /* External resources */
69 struct lwm2m_ctx **lwm2m_sock_ctx(void);
70 
71 int lwm2m_sock_nfds(void);
72 /* Resource wrappers */
lwm2m_obs_obj_path_list(void)73 sys_slist_t *lwm2m_obs_obj_path_list(void) { return &obs_obj_path_list; }
74 
75 struct notification_attrs {
76 	/* use to determine which value is set */
77 	double gt;
78 	double lt;
79 	double st;
80 	int32_t pmin;
81 	int32_t pmax;
82 	uint8_t flags;
83 };
84 
85 /* write-attribute related definitions */
86 static const uint8_t LWM2M_ATTR_LEN[] = {4, 4, 2, 2, 2};
87 
88 static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR];
89 
90 /* Notification value register is used to store the resource values sent in
91  * the last notification, for resources that carry one of the numerical
92  * gt/lt/st attributes. This value is needed to determine whether notification
93  * send conditions are satisfied for given attributes.
94  */
95 static struct lwm2m_notify_value_register {
96 	/* A pointer to the corresponding resource instance. */
97 	const struct lwm2m_engine_res_inst *ref;
98 
99 	/* Last notified resource value. */
100 	double value;
101 
102 	/* Whether resource was notified already or not. */
103 	bool notified : 1;
104 } notify_value_pool[CONFIG_LWM2M_MAX_NOTIFIED_NUMERICAL_RES_TRACKED];
105 
106 /* Forward declarations */
107 
108 void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list);
109 
110 struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list);
111 
notify_value_reg_alloc(const struct lwm2m_engine_res_inst * ref)112 static struct lwm2m_notify_value_register *notify_value_reg_alloc(
113 	const struct lwm2m_engine_res_inst *ref)
114 {
115 	for (int i = 0; i < ARRAY_SIZE(notify_value_pool); i++) {
116 		if (notify_value_pool[i].ref == NULL) {
117 			notify_value_pool[i].ref = ref;
118 			notify_value_pool[i].notified = false;
119 
120 			return &notify_value_pool[i];
121 		}
122 	}
123 
124 	return NULL;
125 }
126 
notify_value_reg_free(const struct lwm2m_engine_res_inst * ref)127 static void notify_value_reg_free(const struct lwm2m_engine_res_inst *ref)
128 {
129 	for (int i = 0; i < ARRAY_SIZE(notify_value_pool); i++) {
130 		if (ref == notify_value_pool[i].ref) {
131 			(void)memset(&notify_value_pool[i], 0,
132 				     sizeof(notify_value_pool[i]));
133 			break;
134 		}
135 	}
136 }
137 
notify_value_reg_get(const struct lwm2m_engine_res_inst * ref)138 static struct lwm2m_notify_value_register *notify_value_reg_get(
139 	const struct lwm2m_engine_res_inst *ref)
140 {
141 	for (int i = 0; i < ARRAY_SIZE(notify_value_pool); i++) {
142 		if (ref == notify_value_pool[i].ref) {
143 			return &notify_value_pool[i];
144 		}
145 	}
146 
147 	return NULL;
148 }
149 
notify_value_reg_inst_update(const struct lwm2m_engine_res_inst * ref,bool enable)150 static int notify_value_reg_inst_update(const struct lwm2m_engine_res_inst *ref,
151 					bool enable)
152 {
153 	struct lwm2m_notify_value_register *notify_reg;
154 
155 	if (ref->res_inst_id == RES_INSTANCE_NOT_CREATED) {
156 		return 0;
157 	}
158 
159 	if (!enable) {
160 		notify_value_reg_free(ref);
161 		return 0;
162 	}
163 
164 	notify_reg = notify_value_reg_get(ref);
165 	if (notify_reg == NULL) {
166 		notify_reg = notify_value_reg_alloc(ref);
167 	}
168 
169 	if (notify_reg == NULL) {
170 		LOG_ERR("Failed to allocate entry in notify registry");
171 		return -ENOMEM;
172 	}
173 
174 	return 0;
175 }
176 
notify_value_reg_update(uint8_t level,void * ref,bool enable)177 static int notify_value_reg_update(uint8_t level, void *ref, bool enable)
178 {
179 	struct lwm2m_engine_res *res;
180 	int ret;
181 
182 	if (level < LWM2M_PATH_LEVEL_RESOURCE) {
183 		return -EINVAL;
184 	}
185 
186 	if (level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
187 		return notify_value_reg_inst_update(ref, enable);
188 	}
189 
190 	/* Resource level - need to allocate value registers for all instances */
191 	res = ref;
192 	for (int i = 0; i < res->res_inst_count; i++) {
193 		ret = notify_value_reg_inst_update(&res->res_instances[i],
194 						   enable);
195 		if (ret < 0) {
196 			goto cleanup;
197 		}
198 	}
199 
200 	return 0;
201 
202 cleanup:
203 	for (int i = 0; i < res->res_inst_count; i++) {
204 		bool skip = false;
205 
206 		for (int j = 0; j < ARRAY_SIZE(write_attr_pool); j++) {
207 			struct lwm2m_attr *attr = &write_attr_pool[j];
208 
209 			/* If register was allocated individually for the resource
210 			 * instance earlier - skip.
211 			 */
212 			if (attr->ref == &res->res_instances[i] &&
213 			    (attr->type == LWM2M_ATTR_LT ||
214 			     attr->type == LWM2M_ATTR_GT ||
215 			     attr->type == LWM2M_ATTR_STEP)) {
216 				skip = true;
217 				break;
218 			}
219 		}
220 
221 		(void)notify_value_reg_inst_update(&res->res_instances[i], false);
222 	}
223 
224 	return -ENOMEM;
225 }
226 
lwm2m_attr_to_str(uint8_t type)227 const char *const lwm2m_attr_to_str(uint8_t type)
228 {
229 	switch (type) {
230 	case LWM2M_ATTR_PMIN:
231 		return "pmin";
232 	case LWM2M_ATTR_PMAX:
233 		return "pmax";
234 	case LWM2M_ATTR_GT:
235 		return "gt";
236 	case LWM2M_ATTR_LT:
237 		return "lt";
238 	case LWM2M_ATTR_STEP:
239 		return "st";
240 	default:
241 		return "unknown";
242 	}
243 }
244 
update_attrs(void * ref,struct notification_attrs * out)245 static int update_attrs(void *ref, struct notification_attrs *out)
246 {
247 	int i;
248 
249 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
250 		if (ref != write_attr_pool[i].ref) {
251 			continue;
252 		}
253 
254 		switch (write_attr_pool[i].type) {
255 		case LWM2M_ATTR_PMIN:
256 			out->pmin = write_attr_pool[i].int_val;
257 			break;
258 		case LWM2M_ATTR_PMAX:
259 			out->pmax = write_attr_pool[i].int_val;
260 			break;
261 		case LWM2M_ATTR_LT:
262 			out->lt = write_attr_pool[i].float_val;
263 			break;
264 		case LWM2M_ATTR_GT:
265 			out->gt = write_attr_pool[i].float_val;
266 			break;
267 		case LWM2M_ATTR_STEP:
268 			out->st = write_attr_pool[i].float_val;
269 			break;
270 		default:
271 			LOG_ERR("Unrecognized attr: %d", write_attr_pool[i].type);
272 			return -EINVAL;
273 		}
274 
275 		/* mark as set */
276 		out->flags |= BIT(write_attr_pool[i].type);
277 	}
278 
279 	return 0;
280 }
281 
clear_attrs(uint8_t level,void * ref)282 void clear_attrs(uint8_t level, void *ref)
283 {
284 	int i;
285 
286 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
287 		if (ref == write_attr_pool[i].ref) {
288 			(void)memset(&write_attr_pool[i], 0, sizeof(write_attr_pool[i]));
289 		}
290 	}
291 
292 	(void)notify_value_reg_update(level, ref, false);
293 }
294 
lwm2m_observer_path_compare(const struct lwm2m_obj_path * o_p,const struct lwm2m_obj_path * p)295 static bool lwm2m_observer_path_compare(const struct lwm2m_obj_path *o_p,
296 					const struct lwm2m_obj_path *p)
297 {
298 	/* check obj id matched or not */
299 	if (p->obj_id != o_p->obj_id) {
300 		return false;
301 	}
302 
303 	if (o_p->level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
304 		if (p->level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
305 		    p->obj_inst_id != o_p->obj_inst_id) {
306 			return false;
307 		}
308 	}
309 
310 	if (o_p->level >= LWM2M_PATH_LEVEL_RESOURCE) {
311 		if (p->level >= LWM2M_PATH_LEVEL_RESOURCE && p->res_id != o_p->res_id) {
312 			return false;
313 		}
314 	}
315 
316 	if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && o_p->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
317 		if (p->level == LWM2M_PATH_LEVEL_RESOURCE_INST &&
318 		    p->res_inst_id != o_p->res_inst_id) {
319 			return false;
320 		}
321 	}
322 
323 	return true;
324 }
325 
lwm2m_notify_observer_list(sys_slist_t * path_list,const struct lwm2m_obj_path * path)326 static bool lwm2m_notify_observer_list(sys_slist_t *path_list, const struct lwm2m_obj_path *path)
327 {
328 	struct lwm2m_obj_path_list *o_p;
329 
330 	SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) {
331 		if (lwm2m_observer_path_compare(&o_p->path, path)) {
332 			return true;
333 		}
334 	}
335 
336 	return false;
337 }
338 
lwm2m_notify_observer(uint16_t obj_id,uint16_t obj_inst_id,uint16_t res_id)339 int lwm2m_notify_observer(uint16_t obj_id, uint16_t obj_inst_id, uint16_t res_id)
340 {
341 	struct lwm2m_obj_path path;
342 
343 	path.level = LWM2M_PATH_LEVEL_RESOURCE;
344 	path.obj_id = obj_id;
345 	path.obj_inst_id = obj_inst_id;
346 	path.res_id = res_id;
347 
348 	return lwm2m_notify_observer_path(&path);
349 }
350 
engine_observe_get_attributes(const struct lwm2m_obj_path * path,struct notification_attrs * attrs,uint16_t srv_obj_inst)351 static int engine_observe_get_attributes(const struct lwm2m_obj_path *path,
352 					 struct notification_attrs *attrs, uint16_t srv_obj_inst)
353 {
354 	struct lwm2m_engine_obj *obj;
355 	struct lwm2m_engine_obj_field *obj_field = NULL;
356 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
357 	struct lwm2m_engine_res_inst *res_inst = NULL;
358 	int ret, i;
359 
360 	/* defaults from server object */
361 	attrs->pmin = lwm2m_server_get_pmin(srv_obj_inst);
362 	attrs->pmax = lwm2m_server_get_pmax(srv_obj_inst);
363 	attrs->flags = BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX);
364 
365 	/* check if object exists */
366 	obj = get_engine_obj(path->obj_id);
367 	if (!obj) {
368 		LOG_ERR("unable to find obj: %u", path->obj_id);
369 		return -ENOENT;
370 	}
371 
372 	ret = update_attrs(obj, attrs);
373 	if (ret < 0) {
374 		return ret;
375 	}
376 
377 	/* check if object instance exists */
378 	if (path->level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
379 		obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
380 		if (!obj_inst) {
381 			attrs->pmax = 0;
382 			attrs->pmin = 0;
383 			return 0;
384 		}
385 
386 		ret = update_attrs(obj_inst, attrs);
387 		if (ret < 0) {
388 			return ret;
389 		}
390 	}
391 
392 	/* check if resource exists */
393 	if (path->level >= LWM2M_PATH_LEVEL_RESOURCE) {
394 		for (i = 0; i < obj_inst->resource_count; i++) {
395 			if (obj_inst->resources[i].res_id == path->res_id) {
396 				break;
397 			}
398 		}
399 
400 		if (i == obj_inst->resource_count) {
401 			LOG_ERR("unable to find res_id: %u/%u/%u", path->obj_id, path->obj_inst_id,
402 				path->res_id);
403 			return -ENOENT;
404 		}
405 
406 		/* load object field data */
407 		obj_field = lwm2m_get_engine_obj_field(obj, obj_inst->resources[i].res_id);
408 		if (!obj_field) {
409 			LOG_ERR("unable to find obj_field: %u/%u/%u", path->obj_id,
410 				path->obj_inst_id, path->res_id);
411 			return -ENOENT;
412 		}
413 
414 		/* check for READ permission on matching resource */
415 		if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
416 			return -EPERM;
417 		}
418 
419 		ret = update_attrs(&obj_inst->resources[i], attrs);
420 		if (ret < 0) {
421 			return ret;
422 		}
423 	}
424 
425 	/* check if resource instance exists */
426 	if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
427 		ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
428 		if (ret < 0) {
429 			return ret;
430 		}
431 
432 		if (res_inst == NULL) {
433 			return -ENOENT;
434 		}
435 
436 		ret = update_attrs(res_inst, attrs);
437 		if (ret < 0) {
438 			return ret;
439 		}
440 	}
441 
442 	attrs->pmax = (attrs->pmax >= attrs->pmin) ? (uint32_t)attrs->pmax : 0UL;
443 
444 	return 0;
445 }
446 
engine_observe_attribute_list_get(sys_slist_t * path_list,struct notification_attrs * nattrs,uint16_t server_obj_inst)447 int engine_observe_attribute_list_get(sys_slist_t *path_list, struct notification_attrs *nattrs,
448 				      uint16_t server_obj_inst)
449 {
450 	struct lwm2m_obj_path_list *o_p;
451 	/* Temporary compare values */
452 	int32_t pmin = 0;
453 	int32_t pmax = 0;
454 	int ret;
455 
456 	SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) {
457 		nattrs->pmin = 0;
458 		nattrs->pmax = 0;
459 
460 		ret = engine_observe_get_attributes(&o_p->path, nattrs, server_obj_inst);
461 		if (ret < 0) {
462 			return ret;
463 		}
464 
465 		if (nattrs->pmin) {
466 			if (pmin == 0) {
467 				pmin = nattrs->pmin;
468 			} else {
469 				pmin = MIN(pmin, nattrs->pmin);
470 			}
471 		}
472 
473 		if (nattrs->pmax) {
474 			if (pmax == 0) {
475 				pmax = nattrs->pmax;
476 			} else {
477 				pmax = MIN(pmax, nattrs->pmax);
478 			}
479 		}
480 	}
481 
482 	nattrs->pmin = pmin;
483 	nattrs->pmax = pmax;
484 	return 0;
485 }
486 
resource_value_as_double(const struct lwm2m_obj_path * path,double * value)487 static int resource_value_as_double(const struct lwm2m_obj_path *path,
488 				    double *value)
489 {
490 	struct lwm2m_engine_obj_field *obj_field;
491 	union {
492 		int64_t s64;
493 		int32_t s32;
494 		int16_t s16;
495 		int8_t s8;
496 		uint32_t u32;
497 		uint16_t u16;
498 		uint8_t u8;
499 	} temp_buf = { 0 };
500 	int ret;
501 
502 	lwm2m_registry_lock();
503 
504 	ret = path_to_objs(path, NULL, &obj_field, NULL, NULL);
505 	if (ret < 0 || obj_field == NULL) {
506 		lwm2m_registry_unlock();
507 		return -ENOENT;
508 	}
509 
510 	switch (obj_field->data_type) {
511 	case LWM2M_RES_TYPE_U32:
512 		ret = lwm2m_get_u32(path, &temp_buf.u32);
513 		*value = (double)temp_buf.u32;
514 		break;
515 	case LWM2M_RES_TYPE_U16:
516 		ret = lwm2m_get_u16(path, &temp_buf.u16);
517 		*value = (double)temp_buf.u16;
518 		break;
519 	case LWM2M_RES_TYPE_U8:
520 		ret = lwm2m_get_u8(path, &temp_buf.u8);
521 		*value = (double)temp_buf.u8;
522 		break;
523 	case LWM2M_RES_TYPE_S64:
524 		ret = lwm2m_get_s64(path, &temp_buf.s64);
525 		*value = (double)temp_buf.s64;
526 		break;
527 	case LWM2M_RES_TYPE_S32:
528 		ret = lwm2m_get_s32(path, &temp_buf.s32);
529 		*value = (double)temp_buf.s32;
530 		break;
531 	case LWM2M_RES_TYPE_S16:
532 		ret = lwm2m_get_s16(path, &temp_buf.s16);
533 		*value = (double)temp_buf.s16;
534 		break;
535 	case LWM2M_RES_TYPE_S8:
536 		ret = lwm2m_get_s8(path, &temp_buf.s8);
537 		*value = (double)temp_buf.s8;
538 		break;
539 	case LWM2M_RES_TYPE_FLOAT:
540 		ret = lwm2m_get_f64(path, value);
541 		break;
542 	default:
543 		ret = -ENOTSUP;
544 		break;
545 	}
546 
547 	lwm2m_registry_unlock();
548 
549 	return ret;
550 }
551 
value_conditions_satisfied(const struct lwm2m_obj_path * path,struct notification_attrs * attrs)552 static bool value_conditions_satisfied(const struct lwm2m_obj_path *path,
553 				       struct notification_attrs *attrs)
554 {
555 	struct lwm2m_notify_value_register *last_notified;
556 	double res_value, old_value;
557 	void *ref;
558 	int ret;
559 
560 	/* Value check only applies to resource or resource instance levels. */
561 	if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
562 		return true;
563 	}
564 
565 	/* Check if any of the value attributes is actually set. */
566 	if ((attrs->flags & (BIT(LWM2M_ATTR_GT) |
567 			     BIT(LWM2M_ATTR_LT) |
568 			     BIT(LWM2M_ATTR_STEP))) == 0) {
569 		return true;
570 	}
571 
572 	ret = lwm2m_get_path_reference_ptr(NULL, path, &ref);
573 	if (ret < 0 || ref == NULL) {
574 		return true;
575 	}
576 
577 	/* Notification value register uses resource instance pointer as ref */
578 	if (path->level == LWM2M_PATH_LEVEL_RESOURCE) {
579 		struct lwm2m_engine_res *res = ref;
580 
581 		ref = res->res_instances;
582 		if (ref == NULL) {
583 			return true;
584 		}
585 	}
586 
587 	last_notified = notify_value_reg_get(ref);
588 	if (last_notified == NULL || !last_notified->notified) {
589 		return true;
590 	}
591 
592 	old_value = last_notified->value;
593 
594 	/* Value check only applies to numerical resources. */
595 	ret = resource_value_as_double(path, &res_value);
596 	if (ret < 0) {
597 		return true;
598 	}
599 
600 	if ((attrs->flags & BIT(LWM2M_ATTR_STEP)) != 0) {
601 		double res_diff = old_value > res_value ?
602 				  old_value - res_value : res_value - old_value;
603 
604 		if (res_diff >= attrs->st) {
605 			return true;
606 		}
607 	}
608 
609 	if ((attrs->flags & BIT(LWM2M_ATTR_GT)) != 0) {
610 		if ((old_value <= attrs->gt && res_value > attrs->gt) ||
611 		    (old_value >= attrs->gt && res_value < attrs->gt)) {
612 			return true;
613 		}
614 	}
615 
616 	if ((attrs->flags & BIT(LWM2M_ATTR_LT)) != 0) {
617 		if ((old_value <= attrs->lt && res_value > attrs->lt) ||
618 		    (old_value >= attrs->lt && res_value < attrs->lt)) {
619 			return true;
620 		}
621 	}
622 
623 	return false;
624 }
625 
lwm2m_notify_observer_path(const struct lwm2m_obj_path * path)626 int lwm2m_notify_observer_path(const struct lwm2m_obj_path *path)
627 {
628 	struct observe_node *obs;
629 	struct notification_attrs obs_attrs = {0};
630 	struct notification_attrs res_attrs = {0};
631 	int64_t timestamp;
632 	int ret = 0;
633 	int i;
634 	struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx();
635 
636 	if (path->level < LWM2M_PATH_LEVEL_OBJECT) {
637 		return 0;
638 	}
639 
640 	/* look for observers which match our resource */
641 	for (i = 0; i < lwm2m_sock_nfds(); ++i) {
642 		SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
643 			if (lwm2m_notify_observer_list(&obs->path_list, path)) {
644 				/* update the event time for this observer */
645 				ret = engine_observe_attribute_list_get(&obs->path_list, &obs_attrs,
646 									sock_ctx[i]->srv_obj_inst);
647 				if (ret < 0) {
648 					return ret;
649 				}
650 
651 				/* Read attributes for the updated resource path */
652 				ret = engine_observe_get_attributes(path, &res_attrs,
653 								    sock_ctx[i]->srv_obj_inst);
654 				if (ret < 0) {
655 					return ret;
656 				}
657 
658 				if (!value_conditions_satisfied(path, &res_attrs)) {
659 					continue;
660 				}
661 
662 				/* In case the lowest pmin value for the observation is smaller
663 				 * than the pmin configured for the updated resource, use the
664 				 * resource value to prevent notification from being generated
665 				 * too early.
666 				 */
667 				if (obs_attrs.pmin < res_attrs.pmin) {
668 					obs_attrs.pmin = res_attrs.pmin;
669 				}
670 
671 				if (obs_attrs.pmin) {
672 					timestamp =
673 						obs->last_timestamp + MSEC_PER_SEC * obs_attrs.pmin;
674 				} else {
675 					/* Trig immediately */
676 					timestamp = k_uptime_get();
677 				}
678 
679 				if (!obs->event_timestamp || obs->event_timestamp > timestamp) {
680 					obs->resource_update = true;
681 					obs->event_timestamp = timestamp;
682 				}
683 
684 				LOG_DBG("NOTIFY EVENT %u/%u/%u", path->obj_id, path->obj_inst_id,
685 					path->res_id);
686 				ret++;
687 				lwm2m_engine_wake_up();
688 			}
689 		}
690 	}
691 
692 	return ret;
693 }
694 
engine_allocate_observer(sys_slist_t * path_list,bool composite)695 static struct observe_node *engine_allocate_observer(sys_slist_t *path_list, bool composite)
696 {
697 	int i;
698 	struct lwm2m_obj_path_list *entry, *tmp;
699 	struct observe_node *obs = NULL;
700 
701 	/* find an unused observer index node */
702 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
703 		if (!observe_node_data[i].tkl) {
704 
705 			obs = &observe_node_data[i];
706 			break;
707 		}
708 	}
709 
710 	if (!obs) {
711 		return NULL;
712 	}
713 
714 	sys_slist_init(&obs->path_list);
715 	obs->composite = composite;
716 
717 	/* Allocate and copy path */
718 	SYS_SLIST_FOR_EACH_CONTAINER(path_list, tmp, node) {
719 		/* Allocate path entry */
720 		entry = lwm2m_engine_get_from_list(&obs_obj_path_list);
721 		if (!entry) {
722 			/* Free list */
723 			lwm2m_engine_free_list(&obs->path_list, &obs_obj_path_list);
724 			return NULL;
725 		}
726 
727 		/* copy the values and add it to the list */
728 		memcpy(&entry->path, &tmp->path, sizeof(tmp->path));
729 		/* Add to last by keeping already sorted order */
730 		sys_slist_append(&obs->path_list, &entry->node);
731 	}
732 
733 	return obs;
734 }
735 
engine_observe_node_init(struct observe_node * obs,const uint8_t * token,struct lwm2m_ctx * ctx,uint8_t tkl,uint16_t format,int32_t att_pmax)736 static void engine_observe_node_init(struct observe_node *obs, const uint8_t *token,
737 				     struct lwm2m_ctx *ctx, uint8_t tkl, uint16_t format,
738 				     int32_t att_pmax)
739 {
740 	struct lwm2m_obj_path_list *tmp;
741 
742 	memcpy(obs->token, token, tkl);
743 	obs->tkl = tkl;
744 
745 	obs->last_timestamp = k_uptime_get();
746 	if (att_pmax) {
747 		obs->event_timestamp = obs->last_timestamp + MSEC_PER_SEC * att_pmax;
748 	} else {
749 		obs->event_timestamp = 0;
750 	}
751 	obs->resource_update = false;
752 	obs->active_notify = NULL;
753 	obs->format = format;
754 	obs->counter = OBSERVE_COUNTER_START;
755 	sys_slist_append(&ctx->observer, &obs->node);
756 
757 	SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, tmp, node) {
758 		LOG_DBG("OBSERVER ADDED %u/%u/%u/%u(%u)", tmp->path.obj_id, tmp->path.obj_inst_id,
759 			tmp->path.res_id, tmp->path.res_inst_id, tmp->path.level);
760 
761 		if (ctx->observe_cb) {
762 			ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_ADDED, &tmp->path, ctx);
763 		}
764 	}
765 
766 	LOG_DBG("token:'%s' addr:%s", sprint_token(token, tkl),
767 		lwm2m_sprint_ip_addr(&ctx->remote_addr));
768 }
769 
remove_observer_path_from_list(struct lwm2m_ctx * ctx,struct observe_node * obs,struct lwm2m_obj_path_list * o_p,sys_snode_t * prev_node)770 static void remove_observer_path_from_list(struct lwm2m_ctx *ctx, struct observe_node *obs,
771 					   struct lwm2m_obj_path_list *o_p, sys_snode_t *prev_node)
772 {
773 	char buf[LWM2M_MAX_PATH_STR_SIZE];
774 
775 	LOG_DBG("Removing observer %p for path %s", obs, lwm2m_path_log_buf(buf, &o_p->path));
776 	if (ctx->observe_cb) {
777 		ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED, &o_p->path, NULL);
778 	}
779 	/* Remove from the list and add to free list */
780 	sys_slist_remove(&obs->path_list, prev_node, &o_p->node);
781 	sys_slist_append(&obs_obj_path_list, &o_p->node);
782 }
783 
engine_observe_single_path_id_remove(struct lwm2m_ctx * ctx,struct observe_node * obs,uint16_t obj_id,int32_t obj_inst_id)784 static void engine_observe_single_path_id_remove(struct lwm2m_ctx *ctx, struct observe_node *obs,
785 						 uint16_t obj_id, int32_t obj_inst_id)
786 {
787 	struct lwm2m_obj_path_list *o_p, *tmp;
788 	sys_snode_t *prev_node = NULL;
789 
790 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) {
791 		if (o_p->path.obj_id != obj_id && o_p->path.obj_inst_id) {
792 			prev_node = &o_p->node;
793 			continue;
794 		}
795 
796 		if (obj_inst_id == -1 || o_p->path.obj_inst_id == obj_inst_id) {
797 			remove_observer_path_from_list(ctx, obs, o_p, prev_node);
798 		} else {
799 			prev_node = &o_p->node;
800 		}
801 	}
802 }
803 
engine_compare_obs_path_list(sys_slist_t * obs_path_list,sys_slist_t * path_list,int list_length)804 static bool engine_compare_obs_path_list(sys_slist_t *obs_path_list, sys_slist_t *path_list,
805 					 int list_length)
806 {
807 	sys_snode_t *obs_ptr, *comp_ptr;
808 	struct lwm2m_obj_path_list *obs_path, *comp_path;
809 
810 	obs_ptr = sys_slist_peek_head(obs_path_list);
811 	comp_ptr = sys_slist_peek_head(path_list);
812 	while (list_length--) {
813 		obs_path = (struct lwm2m_obj_path_list *)obs_ptr;
814 		comp_path = (struct lwm2m_obj_path_list *)comp_ptr;
815 		if (memcmp(&obs_path->path, &comp_path->path, sizeof(struct lwm2m_obj_path))) {
816 			return false;
817 		}
818 		/* Read Next Info from list entry*/
819 		obs_ptr = sys_slist_peek_next_no_check(obs_ptr);
820 		comp_ptr = sys_slist_peek_next_no_check(comp_ptr);
821 	}
822 
823 	return true;
824 }
825 
engine_path_list_size(sys_slist_t * lwm2m_path_list)826 static int engine_path_list_size(sys_slist_t *lwm2m_path_list)
827 {
828 	int list_size = 0;
829 	struct lwm2m_obj_path_list *entry;
830 
831 	SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
832 		list_size++;
833 	}
834 	return list_size;
835 }
836 
engine_observe_node_discover(sys_slist_t * observe_node_list,sys_snode_t ** prev_node,sys_slist_t * lwm2m_path_list,const uint8_t * token,uint8_t tkl)837 struct observe_node *engine_observe_node_discover(sys_slist_t *observe_node_list,
838 						  sys_snode_t **prev_node,
839 						  sys_slist_t *lwm2m_path_list,
840 						  const uint8_t *token, uint8_t tkl)
841 {
842 	struct observe_node *obs;
843 	int obs_list_size, path_list_size = 0;
844 
845 	if (lwm2m_path_list) {
846 		path_list_size = engine_path_list_size(lwm2m_path_list);
847 	}
848 
849 	*prev_node = NULL;
850 
851 	SYS_SLIST_FOR_EACH_CONTAINER(observe_node_list, obs, node) {
852 
853 		if (lwm2m_path_list) {
854 			/* Validate Path for discovery */
855 			obs_list_size = engine_path_list_size(&obs->path_list);
856 
857 			if (obs_list_size != path_list_size) {
858 				*prev_node = &obs->node;
859 				continue;
860 			}
861 
862 			if (!engine_compare_obs_path_list(&obs->path_list, lwm2m_path_list,
863 							  obs_list_size)) {
864 				*prev_node = &obs->node;
865 				continue;
866 			}
867 		}
868 
869 		if (token && memcmp(obs->token, token, tkl)) {
870 			/* Token not match */
871 			*prev_node = &obs->node;
872 			continue;
873 		}
874 		return obs;
875 	}
876 	return NULL;
877 }
878 
engine_add_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)879 static int engine_add_observer(struct lwm2m_message *msg, const uint8_t *token, uint8_t tkl,
880 			       uint16_t format)
881 {
882 	struct observe_node *obs;
883 	struct notification_attrs attrs;
884 	struct lwm2m_obj_path_list obs_path_list_buf;
885 	sys_slist_t lwm2m_path_list;
886 	sys_snode_t *prev_node = NULL;
887 	int ret;
888 
889 	if (!msg || !msg->ctx) {
890 		LOG_ERR("valid lwm2m message is required");
891 		return -EINVAL;
892 	}
893 
894 	if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
895 		LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
896 		return -EINVAL;
897 	}
898 
899 	/* Create 1 entry linked list for message path */
900 	memcpy(&obs_path_list_buf.path, &msg->path, sizeof(struct lwm2m_obj_path));
901 	sys_slist_init(&lwm2m_path_list);
902 	sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node);
903 
904 	obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL,
905 					   0);
906 	if (obs) {
907 		memcpy(obs->token, token, tkl);
908 		obs->tkl = tkl;
909 
910 		/* Cancel ongoing notification */
911 		if (obs->active_notify != NULL) {
912 			lwm2m_reset_message(obs->active_notify, true);
913 			obs->active_notify = NULL;
914 		}
915 
916 		LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]", msg->path.obj_id,
917 			msg->path.obj_inst_id, msg->path.res_id, msg->path.level,
918 			lwm2m_sprint_ip_addr(&msg->ctx->remote_addr));
919 
920 		lwm2m_engine_observer_refresh_notified_values(obs);
921 
922 		return 0;
923 	}
924 
925 	/* Read attributes and allocate new entry */
926 	ret = engine_observe_get_attributes(&msg->path, &attrs, msg->ctx->srv_obj_inst);
927 	if (ret < 0) {
928 		return ret;
929 	}
930 
931 	obs = engine_allocate_observer(&lwm2m_path_list, false);
932 	if (!obs) {
933 		return -ENOMEM;
934 	}
935 
936 	engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax);
937 	lwm2m_engine_observer_refresh_notified_values(obs);
938 
939 	return 0;
940 }
941 
do_composite_observe_read_path_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_path_free_list)942 int do_composite_observe_read_path_op(struct lwm2m_message *msg, uint16_t content_format,
943 				      sys_slist_t *lwm2m_path_list,
944 				      sys_slist_t *lwm2m_path_free_list)
945 {
946 	switch (content_format) {
947 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
948 	case LWM2M_FORMAT_APP_SEML_JSON:
949 		return do_composite_observe_parse_path_senml_json(msg, lwm2m_path_list,
950 								  lwm2m_path_free_list);
951 #endif
952 
953 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
954 	case LWM2M_FORMAT_APP_SENML_CBOR:
955 		return do_composite_observe_parse_path_senml_cbor(msg, lwm2m_path_list,
956 								  lwm2m_path_free_list);
957 #endif
958 
959 	default:
960 		LOG_ERR("Unsupported content-format: %u", content_format);
961 		return -ENOMSG;
962 	}
963 }
964 
engine_add_composite_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)965 static int engine_add_composite_observer(struct lwm2m_message *msg, const uint8_t *token,
966 					 uint8_t tkl, uint16_t format)
967 {
968 	struct observe_node *obs;
969 	struct notification_attrs attrs;
970 	struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
971 	sys_slist_t lwm2m_path_list;
972 	sys_slist_t lwm2m_path_free_list;
973 	sys_snode_t *prev_node = NULL;
974 	int ret;
975 
976 	if (!msg || !msg->ctx) {
977 		LOG_ERR("valid lwm2m message is required");
978 		return -EINVAL;
979 	}
980 
981 	if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
982 		LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
983 		return -EINVAL;
984 	}
985 
986 	/* Init list */
987 	lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
988 				    CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
989 
990 	/* Read attributes and allocate new entry */
991 	ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list,
992 						&lwm2m_path_free_list);
993 	if (ret < 0) {
994 		return ret;
995 	}
996 
997 	obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL,
998 					   0);
999 	if (obs) {
1000 		memcpy(obs->token, token, tkl);
1001 		obs->tkl = tkl;
1002 
1003 		/* Cancel ongoing notification */
1004 		if (obs->active_notify != NULL) {
1005 			lwm2m_reset_message(obs->active_notify, true);
1006 			obs->active_notify = NULL;
1007 		}
1008 
1009 		LOG_DBG("OBSERVER Composite DUPLICATE [%s]",
1010 			lwm2m_sprint_ip_addr(&msg->ctx->remote_addr));
1011 
1012 		lwm2m_engine_observer_refresh_notified_values(obs);
1013 
1014 		return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list);
1015 	}
1016 
1017 	ret = engine_observe_attribute_list_get(&lwm2m_path_list, &attrs, msg->ctx->srv_obj_inst);
1018 	if (ret < 0) {
1019 		return ret;
1020 	}
1021 
1022 	obs = engine_allocate_observer(&lwm2m_path_list, true);
1023 	if (!obs) {
1024 		return -ENOMEM;
1025 	}
1026 	engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax);
1027 	lwm2m_engine_observer_refresh_notified_values(obs);
1028 
1029 	return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list);
1030 }
1031 
remove_observer_from_list(struct lwm2m_ctx * ctx,sys_snode_t * prev_node,struct observe_node * obs)1032 void remove_observer_from_list(struct lwm2m_ctx *ctx, sys_snode_t *prev_node,
1033 			       struct observe_node *obs)
1034 {
1035 	struct lwm2m_obj_path_list *o_p, *tmp;
1036 
1037 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) {
1038 		remove_observer_path_from_list(ctx, obs, o_p, NULL);
1039 	}
1040 	sys_slist_remove(&ctx->observer, prev_node, &obs->node);
1041 	(void)memset(obs, 0, sizeof(*obs));
1042 }
1043 
engine_remove_observer_by_token(struct lwm2m_ctx * ctx,const uint8_t * token,uint8_t tkl)1044 int engine_remove_observer_by_token(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl)
1045 {
1046 	struct observe_node *obs;
1047 	sys_snode_t *prev_node = NULL;
1048 
1049 	if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
1050 		LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
1051 		return -EINVAL;
1052 	}
1053 
1054 	obs = engine_observe_node_discover(&ctx->observer, &prev_node, NULL, token, tkl);
1055 	if (!obs) {
1056 		return -ENOENT;
1057 	}
1058 
1059 	remove_observer_from_list(ctx, prev_node, obs);
1060 
1061 	LOG_DBG("observer '%s' removed", sprint_token(token, tkl));
1062 
1063 	return 0;
1064 }
1065 
engine_remove_composite_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)1066 static int engine_remove_composite_observer(struct lwm2m_message *msg, const uint8_t *token,
1067 					    uint8_t tkl, uint16_t format)
1068 {
1069 	struct observe_node *obs;
1070 	sys_snode_t *prev_node = NULL;
1071 	struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
1072 	sys_slist_t lwm2m_path_list;
1073 	sys_slist_t lwm2m_path_free_list;
1074 	int ret;
1075 
1076 	if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
1077 		LOG_ERR("token(%p) and token length(%u) must be valid.", token, tkl);
1078 		return -EINVAL;
1079 	}
1080 
1081 	/* Init list */
1082 	lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
1083 				    CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
1084 
1085 	ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list,
1086 						&lwm2m_path_free_list);
1087 	if (ret < 0) {
1088 		return ret;
1089 	}
1090 
1091 	obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, token,
1092 					   tkl);
1093 	if (!obs) {
1094 		return -ENOENT;
1095 	}
1096 
1097 	remove_observer_from_list(msg->ctx, prev_node, obs);
1098 
1099 	LOG_DBG("observer '%s' removed", sprint_token(token, tkl));
1100 
1101 	return do_composite_read_op_for_parsed_list(msg, format, &lwm2m_path_list);
1102 }
1103 
1104 #if defined(CONFIG_LOG)
lwm2m_path_log_buf(char * buf,struct lwm2m_obj_path * path)1105 char *lwm2m_path_log_buf(char *buf, struct lwm2m_obj_path *path)
1106 {
1107 	size_t cur;
1108 
1109 	if (!path) {
1110 		sprintf(buf, "/");
1111 		return buf;
1112 	}
1113 
1114 	cur = sprintf(buf, "%u", path->obj_id);
1115 
1116 	if (path->level > 1) {
1117 		cur += sprintf(buf + cur, "/%u", path->obj_inst_id);
1118 	}
1119 	if (path->level > 2) {
1120 		cur += sprintf(buf + cur, "/%u", path->res_id);
1121 	}
1122 	if (path->level > 3) {
1123 		cur += sprintf(buf + cur, "/%u", path->res_inst_id);
1124 	}
1125 
1126 	return buf;
1127 }
1128 #endif /* CONFIG_LOG */
1129 
1130 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
engine_remove_observer_by_path(struct lwm2m_ctx * ctx,struct lwm2m_obj_path * path)1131 static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx, struct lwm2m_obj_path *path)
1132 {
1133 	char buf[LWM2M_MAX_PATH_STR_SIZE];
1134 	struct observe_node *obs;
1135 	struct lwm2m_obj_path_list obs_path_list_buf;
1136 	sys_slist_t lwm2m_path_list;
1137 	sys_snode_t *prev_node = NULL;
1138 
1139 	/* Create 1 entry linked list for message path */
1140 	memcpy(&obs_path_list_buf.path, path, sizeof(struct lwm2m_obj_path));
1141 	sys_slist_init(&lwm2m_path_list);
1142 	sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node);
1143 
1144 	obs = engine_observe_node_discover(&ctx->observer, &prev_node, &lwm2m_path_list, NULL, 0);
1145 	if (!obs) {
1146 		return -ENOENT;
1147 	}
1148 
1149 	LOG_INF("Removing observer for path %s", lwm2m_path_log_buf(buf, path));
1150 
1151 	remove_observer_from_list(ctx, prev_node, obs);
1152 
1153 	return 0;
1154 }
1155 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
1156 
engine_remove_observer_by_id(uint16_t obj_id,int32_t obj_inst_id)1157 void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id)
1158 {
1159 	struct observe_node *obs, *tmp;
1160 	sys_snode_t *prev_node = NULL;
1161 	int i;
1162 	struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx();
1163 
1164 	/* remove observer instances accordingly */
1165 	for (i = 0; i < lwm2m_sock_nfds(); ++i) {
1166 		SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sock_ctx[i]->observer, obs, tmp, node) {
1167 			engine_observe_single_path_id_remove(sock_ctx[i], obs, obj_id, obj_inst_id);
1168 
1169 			if (sys_slist_is_empty(&obs->path_list)) {
1170 				remove_observer_from_list(sock_ctx[i], prev_node, obs);
1171 			} else {
1172 				prev_node = &obs->node;
1173 			}
1174 		}
1175 	}
1176 }
1177 
lwm2m_update_or_allocate_attribute(void * ref,uint8_t type,void * data)1178 static int lwm2m_update_or_allocate_attribute(void *ref, uint8_t type, void *data)
1179 {
1180 	struct lwm2m_attr *attr;
1181 	int i;
1182 
1183 	/* find matching attributes */
1184 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
1185 		if (ref != write_attr_pool[i].ref || write_attr_pool[i].type != type) {
1186 			continue;
1187 		}
1188 
1189 		attr = write_attr_pool + i;
1190 		type = attr->type;
1191 
1192 		if (type <= LWM2M_ATTR_PMAX) {
1193 			attr->int_val = *(int32_t *)data;
1194 			LOG_DBG("Update %s to %d", lwm2m_attr_to_str(type), attr->int_val);
1195 		} else {
1196 			attr->float_val = *(double *)data;
1197 			LOG_DBG("Update %s to %f", lwm2m_attr_to_str(type), attr->float_val);
1198 		}
1199 		return 0;
1200 	}
1201 
1202 	/* add attribute to obj/obj_inst/res/res_inst */
1203 	/* grab an entry for newly added attribute */
1204 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
1205 		if (!write_attr_pool[i].ref) {
1206 			break;
1207 		}
1208 	}
1209 
1210 	if (i == CONFIG_LWM2M_NUM_ATTR) {
1211 		return -ENOMEM;
1212 	}
1213 
1214 	attr = write_attr_pool + i;
1215 	attr->type = type;
1216 	attr->ref = ref;
1217 
1218 	if (type <= LWM2M_ATTR_PMAX) {
1219 		attr->int_val = *(int32_t *)data;
1220 		LOG_DBG("Add %s to %d", lwm2m_attr_to_str(type), attr->int_val);
1221 	} else {
1222 		attr->float_val = *(double *)data;
1223 		LOG_DBG("Add %s to %f", lwm2m_attr_to_str(type), attr->float_val);
1224 	}
1225 	return 0;
1226 }
1227 
lwm2m_engine_get_attr_name(const struct lwm2m_attr * attr)1228 const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr)
1229 {
1230 	if (attr->type >= NR_LWM2M_ATTR) {
1231 		return NULL;
1232 	}
1233 
1234 	return lwm2m_attr_to_str(attr->type);
1235 }
1236 
lwm2m_engine_observer_timestamp_update(sys_slist_t * observer,const struct lwm2m_obj_path * path,uint16_t srv_obj_inst)1237 static int lwm2m_engine_observer_timestamp_update(sys_slist_t *observer,
1238 						  const struct lwm2m_obj_path *path,
1239 						  uint16_t srv_obj_inst)
1240 {
1241 	struct observe_node *obs;
1242 	struct notification_attrs nattrs = {0};
1243 	int ret;
1244 	int64_t timestamp;
1245 
1246 	/* update observe_node accordingly */
1247 	SYS_SLIST_FOR_EACH_CONTAINER(observer, obs, node) {
1248 		if (obs->resource_update) {
1249 			/* Resource Update on going skip this*/
1250 			continue;
1251 		}
1252 		/* Compare Observation node path to updated one */
1253 		if (!lwm2m_notify_observer_list(&obs->path_list, path)) {
1254 			continue;
1255 		}
1256 
1257 		/* Read Attributes after validation Path */
1258 		ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs, srv_obj_inst);
1259 		if (ret < 0) {
1260 			return ret;
1261 		}
1262 
1263 		/* Update based on by PMax */
1264 		if (nattrs.pmax) {
1265 			/* Update Current */
1266 			timestamp = obs->last_timestamp + MSEC_PER_SEC * nattrs.pmax;
1267 		} else {
1268 			/* Disable Automatic Notify */
1269 			timestamp = 0;
1270 		}
1271 		obs->event_timestamp = timestamp;
1272 
1273 		(void)memset(&nattrs, 0, sizeof(nattrs));
1274 	}
1275 	return 0;
1276 }
1277 
1278 /* input / output selection */
1279 
lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj * obj,const struct lwm2m_obj_path * path,void ** ref)1280 int lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj *obj, const struct lwm2m_obj_path *path,
1281 				 void **ref)
1282 {
1283 	struct lwm2m_engine_obj_inst *obj_inst;
1284 	struct lwm2m_engine_res *res;
1285 	struct lwm2m_engine_res_inst *res_inst;
1286 	int ret;
1287 
1288 	if (!obj) {
1289 		/* Discover Object */
1290 		obj = get_engine_obj(path->obj_id);
1291 		if (!obj) {
1292 			/* No matching object found - ignore request */
1293 			return -ENOENT;
1294 		}
1295 	}
1296 
1297 	if (path->level == LWM2M_PATH_LEVEL_OBJECT) {
1298 		*ref = obj;
1299 	} else if (path->level == LWM2M_PATH_LEVEL_OBJECT_INST) {
1300 		obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
1301 		if (!obj_inst) {
1302 			return -ENOENT;
1303 		}
1304 
1305 		*ref = obj_inst;
1306 	} else if (path->level == LWM2M_PATH_LEVEL_RESOURCE) {
1307 		ret = path_to_objs(path, NULL, NULL, &res, NULL);
1308 		if (ret < 0) {
1309 			return ret;
1310 		}
1311 
1312 		*ref = res;
1313 	} else if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
1314 		   path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
1315 
1316 		ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
1317 		if (ret < 0) {
1318 			return ret;
1319 		}
1320 
1321 		*ref = res_inst;
1322 	} else {
1323 		/* bad request */
1324 		return -EEXIST;
1325 	}
1326 
1327 	return 0;
1328 }
1329 
lwm2m_update_observer_min_period(struct lwm2m_ctx * client_ctx,const struct lwm2m_obj_path * path,uint32_t period_s)1330 int lwm2m_update_observer_min_period(struct lwm2m_ctx *client_ctx,
1331 				     const struct lwm2m_obj_path *path, uint32_t period_s)
1332 {
1333 	int ret;
1334 	struct notification_attrs nattrs = {0};
1335 	struct notification_attrs attrs = {0};
1336 	void *ref;
1337 
1338 	/* Read Reference pointer to attribute */
1339 	ret = lwm2m_get_path_reference_ptr(NULL, path, &ref);
1340 	if (ret < 0) {
1341 		return ret;
1342 	}
1343 	/* retrieve existing attributes */
1344 	ret = update_attrs(ref, &nattrs);
1345 	if (ret < 0) {
1346 		return ret;
1347 	}
1348 
1349 	if (nattrs.flags & BIT(LWM2M_ATTR_PMIN) && nattrs.pmin == period_s) {
1350 		/* No need to change */
1351 		return 0;
1352 	}
1353 
1354 	/* Read Pmin & Pmax values for path */
1355 	ret = engine_observe_get_attributes(path, &attrs, client_ctx->srv_obj_inst);
1356 	if (ret < 0) {
1357 		return ret;
1358 	}
1359 
1360 	if (period_s && attrs.pmax && attrs.pmax < period_s) {
1361 		LOG_DBG("New pmin (%d) > pmax (%d)", period_s, attrs.pmax);
1362 		return -EEXIST;
1363 	}
1364 
1365 	return lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMIN, &period_s);
1366 }
1367 
lwm2m_update_observer_max_period(struct lwm2m_ctx * client_ctx,const struct lwm2m_obj_path * path,uint32_t period_s)1368 int lwm2m_update_observer_max_period(struct lwm2m_ctx *client_ctx,
1369 				     const struct lwm2m_obj_path *path, uint32_t period_s)
1370 {
1371 	int ret;
1372 	void *ref;
1373 	struct notification_attrs nattrs = {0};
1374 	struct notification_attrs attrs = {0};
1375 
1376 	/* Read Reference pointer to attribute */
1377 	ret = lwm2m_get_path_reference_ptr(NULL, path, &ref);
1378 	if (ret < 0) {
1379 		return ret;
1380 	}
1381 	/* retrieve existing attributes */
1382 	ret = update_attrs(ref, &nattrs);
1383 	if (ret < 0) {
1384 		return ret;
1385 	}
1386 
1387 	if (nattrs.flags & BIT(LWM2M_ATTR_PMAX) && nattrs.pmax == period_s) {
1388 		/* No need to change */
1389 		return 0;
1390 	}
1391 
1392 	/* Read Pmin & Pmax values for path */
1393 	ret = engine_observe_get_attributes(path, &attrs, client_ctx->srv_obj_inst);
1394 	if (ret < 0) {
1395 		return ret;
1396 	}
1397 
1398 	if (period_s && attrs.pmin > period_s) {
1399 		LOG_DBG("pmin (%d) > new pmax (%d)", attrs.pmin, period_s);
1400 		return -EEXIST;
1401 	}
1402 
1403 	/* Update or allocate new */
1404 	ret = lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMAX, &period_s);
1405 	if (ret < 0) {
1406 		return ret;
1407 	}
1408 
1409 	/* Update Observer timestamp */
1410 	return lwm2m_engine_observer_timestamp_update(&client_ctx->observer, path,
1411 						      client_ctx->srv_obj_inst);
1412 }
1413 
lwm2m_engine_get_next_attr(const void * ref,struct lwm2m_attr * prev)1414 struct lwm2m_attr *lwm2m_engine_get_next_attr(const void *ref, struct lwm2m_attr *prev)
1415 {
1416 	struct lwm2m_attr *iter = (prev == NULL) ? write_attr_pool : prev + 1;
1417 	struct lwm2m_attr *result = NULL;
1418 
1419 	if (!PART_OF_ARRAY(write_attr_pool, iter)) {
1420 		return NULL;
1421 	}
1422 
1423 	while (iter < &write_attr_pool[ARRAY_SIZE(write_attr_pool)]) {
1424 		if (ref == iter->ref) {
1425 			result = iter;
1426 			break;
1427 		}
1428 
1429 		++iter;
1430 	}
1431 
1432 	return result;
1433 }
1434 
is_resource_numerical(const struct lwm2m_obj_path * path)1435 static bool is_resource_numerical(const struct lwm2m_obj_path *path)
1436 {
1437 	struct lwm2m_engine_obj_field *obj_field;
1438 	int ret;
1439 
1440 	ret = path_to_objs(path, NULL, &obj_field, NULL, NULL);
1441 	if (ret < 0 || obj_field == NULL) {
1442 		return false;
1443 	}
1444 
1445 	switch (obj_field->data_type) {
1446 	case LWM2M_RES_TYPE_U32:
1447 	case LWM2M_RES_TYPE_U16:
1448 	case LWM2M_RES_TYPE_U8:
1449 	case LWM2M_RES_TYPE_S64:
1450 	case LWM2M_RES_TYPE_S32:
1451 	case LWM2M_RES_TYPE_S16:
1452 	case LWM2M_RES_TYPE_S8:
1453 	case LWM2M_RES_TYPE_FLOAT:
1454 		return true;
1455 	default:
1456 		break;
1457 	}
1458 
1459 	return false;
1460 }
1461 
lwm2m_write_attr_handler(struct lwm2m_engine_obj * obj,struct lwm2m_message * msg)1462 int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj, struct lwm2m_message *msg)
1463 {
1464 	bool update_observe_node = false;
1465 	char opt_buf[COAP_OPTION_BUF_LEN];
1466 	int nr_opt, i, ret = 0;
1467 	struct coap_option options[NR_LWM2M_ATTR];
1468 	struct lwm2m_attr *attr;
1469 	struct notification_attrs nattrs = {0};
1470 	uint8_t type = 0U;
1471 	void *nattr_ptrs[NR_LWM2M_ATTR] = {&nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt,
1472 					   &nattrs.st};
1473 	void *ref;
1474 
1475 	if (!obj || !msg) {
1476 		return -EINVAL;
1477 	}
1478 
1479 	/* do not expose security obj */
1480 	if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
1481 		return -ENOENT;
1482 	}
1483 
1484 	nr_opt = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_QUERY, options, NR_LWM2M_ATTR);
1485 	if (nr_opt <= 0) {
1486 		LOG_ERR("No attribute found!");
1487 		/* translate as bad request */
1488 		return -EEXIST;
1489 	}
1490 
1491 	/* get lwm2m_attr slist */
1492 	ret = lwm2m_get_path_reference_ptr(obj, &msg->path, &ref);
1493 	if (ret < 0) {
1494 		return ret;
1495 	}
1496 
1497 	/* retrieve existing attributes */
1498 	ret = update_attrs(ref, &nattrs);
1499 	if (ret < 0) {
1500 		return ret;
1501 	}
1502 
1503 	/* loop through options to parse attribute */
1504 	for (i = 0; i < nr_opt; i++) {
1505 		int limit = MIN(options[i].len, 5), plen = 0, vlen;
1506 		struct lwm2m_attr val = {0};
1507 
1508 		type = 0U;
1509 
1510 		/* search for '=' */
1511 		while (plen < limit && options[i].value[plen] != '=') {
1512 			plen += 1;
1513 		}
1514 
1515 		/* either length = 2(gt/lt/st) or = 4(pmin/pmax) */
1516 		if (plen != 2 && plen != 4) {
1517 			continue;
1518 		}
1519 
1520 		/* matching attribute name */
1521 		for (type = 0U; type < NR_LWM2M_ATTR; type++) {
1522 			if (LWM2M_ATTR_LEN[type] == plen &&
1523 			    !memcmp(options[i].value, lwm2m_attr_to_str(type),
1524 				    LWM2M_ATTR_LEN[type])) {
1525 				break;
1526 			}
1527 		}
1528 
1529 		/* unrecognized attribute */
1530 		if (type == NR_LWM2M_ATTR) {
1531 			continue;
1532 		}
1533 
1534 		/* unset attribute when no value's given */
1535 		if (options[i].len == plen) {
1536 			nattrs.flags &= ~BIT(type);
1537 
1538 			(void)memset(nattr_ptrs[type], 0,
1539 				     type <= LWM2M_ATTR_PMAX ? sizeof(int32_t) : sizeof(double));
1540 			continue;
1541 		}
1542 
1543 		/* gt/lt/st cannot be assigned to obj/obj_inst unless unset.
1544 		 * They also can only be applied to numerical resources.
1545 		 */
1546 		if (plen == 2 &&
1547 		    (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT_INST ||
1548 		     !is_resource_numerical(&msg->path))) {
1549 			return -EEXIST;
1550 		}
1551 
1552 		vlen = options[i].len - plen - 1;
1553 		memcpy(opt_buf, options[i].value + plen + 1, vlen);
1554 		opt_buf[vlen] = '\0';
1555 
1556 		/* convert value to integer or float */
1557 		if (plen == 4) {
1558 			char *end;
1559 			long v;
1560 
1561 			/* pmin/pmax: integer (sec 5.1.2)
1562 			 * however, negative is non-sense
1563 			 */
1564 			errno = 0;
1565 			v = strtol(opt_buf, &end, 10);
1566 			if (errno || *end || v < 0) {
1567 				ret = -EINVAL;
1568 			}
1569 
1570 			val.int_val = v;
1571 		} else {
1572 			/* gt/lt/st: type float */
1573 			ret = lwm2m_atof(opt_buf, &val.float_val);
1574 		}
1575 
1576 		if (ret < 0) {
1577 			LOG_ERR("invalid attr[%s] value", lwm2m_attr_to_str(type));
1578 			/* bad request */
1579 			return -EEXIST;
1580 		}
1581 
1582 		if (type <= LWM2M_ATTR_PMAX) {
1583 			*(int32_t *)nattr_ptrs[type] = val.int_val;
1584 		} else {
1585 			memcpy(nattr_ptrs[type], &val.float_val, sizeof(val.float_val));
1586 		}
1587 
1588 		nattrs.flags |= BIT(type);
1589 	}
1590 
1591 	if (((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) ==
1592 	     (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) &&
1593 	    nattrs.pmin > nattrs.pmax) {
1594 		LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax);
1595 		return -EEXIST;
1596 	}
1597 
1598 	if ((nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) ==
1599 	    (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) {
1600 		if (nattrs.lt > nattrs.gt) {
1601 			LOG_DBG("lt > gt");
1602 			return -EEXIST;
1603 		}
1604 
1605 		if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) {
1606 			if (nattrs.lt + 2 * nattrs.st > nattrs.gt) {
1607 				LOG_DBG("lt + 2*st > gt");
1608 				return -EEXIST;
1609 			}
1610 		}
1611 	}
1612 
1613 	if (msg->path.level >= LWM2M_PATH_LEVEL_RESOURCE) {
1614 		bool enable = false;
1615 
1616 		if ((nattrs.flags & (BIT(LWM2M_ATTR_LT) |
1617 				     BIT(LWM2M_ATTR_GT) |
1618 				     BIT(LWM2M_ATTR_STEP))) != 0) {
1619 			enable = true;
1620 		}
1621 
1622 		ret = notify_value_reg_update(msg->path.level, ref, enable);
1623 		if (ret < 0) {
1624 			return ret;
1625 		}
1626 	}
1627 
1628 	/* find matching attributes */
1629 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
1630 		if (ref != write_attr_pool[i].ref) {
1631 			continue;
1632 		}
1633 
1634 		attr = write_attr_pool + i;
1635 		type = attr->type;
1636 
1637 		if (!(BIT(type) & nattrs.flags)) {
1638 			LOG_DBG("Unset attr %s", lwm2m_attr_to_str(type));
1639 			(void)memset(attr, 0, sizeof(*attr));
1640 
1641 			if (type <= LWM2M_ATTR_PMAX) {
1642 				update_observe_node = true;
1643 			}
1644 
1645 			continue;
1646 		}
1647 
1648 		nattrs.flags &= ~BIT(type);
1649 
1650 		if (type <= LWM2M_ATTR_PMAX) {
1651 			if (attr->int_val == *(int32_t *)nattr_ptrs[type]) {
1652 				continue;
1653 			}
1654 
1655 			attr->int_val = *(int32_t *)nattr_ptrs[type];
1656 			update_observe_node = true;
1657 
1658 			LOG_DBG("Update %s to %d", lwm2m_attr_to_str(type), attr->int_val);
1659 		} else {
1660 			if (attr->float_val == *(double *)nattr_ptrs[type]) {
1661 				continue;
1662 			}
1663 
1664 			attr->float_val = *(double *)nattr_ptrs[type];
1665 
1666 			LOG_DBG("Update %s to %f", lwm2m_attr_to_str(type), attr->float_val);
1667 		}
1668 	}
1669 
1670 	/* add attribute to obj/obj_inst/res/res_inst */
1671 	for (type = 0U; nattrs.flags && type < NR_LWM2M_ATTR; type++) {
1672 		if (!(BIT(type) & nattrs.flags)) {
1673 			continue;
1674 		}
1675 
1676 		/* grab an entry for newly added attribute */
1677 		for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
1678 			if (!write_attr_pool[i].ref) {
1679 				break;
1680 			}
1681 		}
1682 
1683 		if (i == CONFIG_LWM2M_NUM_ATTR) {
1684 			return -ENOMEM;
1685 		}
1686 
1687 		attr = write_attr_pool + i;
1688 		attr->type = type;
1689 		attr->ref = ref;
1690 
1691 		if (type <= LWM2M_ATTR_PMAX) {
1692 			attr->int_val = *(int32_t *)nattr_ptrs[type];
1693 			update_observe_node = true;
1694 
1695 			LOG_DBG("Add %s to %d", lwm2m_attr_to_str(type), attr->int_val);
1696 		} else {
1697 			attr->float_val = *(double *)nattr_ptrs[type];
1698 
1699 			LOG_DBG("Add %s to %f", lwm2m_attr_to_str(type), attr->float_val);
1700 		}
1701 
1702 		nattrs.flags &= ~BIT(type);
1703 	}
1704 
1705 	/* check only pmin/pmax */
1706 	if (!update_observe_node) {
1707 		return 0;
1708 	}
1709 
1710 	lwm2m_engine_observer_timestamp_update(&msg->ctx->observer, &msg->path,
1711 					       msg->ctx->srv_obj_inst);
1712 
1713 	return 0;
1714 }
1715 
lwm2m_path_is_observed(const struct lwm2m_obj_path * path)1716 bool lwm2m_path_is_observed(const struct lwm2m_obj_path *path)
1717 {
1718 	int i;
1719 	struct observe_node *obs;
1720 	struct lwm2m_ctx **sock_ctx = lwm2m_sock_ctx();
1721 
1722 	for (i = 0; i < lwm2m_sock_nfds(); ++i) {
1723 		SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
1724 
1725 			if (lwm2m_notify_observer_list(&obs->path_list, path)) {
1726 				return true;
1727 			}
1728 		}
1729 	}
1730 	return false;
1731 }
1732 
lwm2m_engine_observation_handler(struct lwm2m_message * msg,int observe,uint16_t accept,bool composite)1733 int lwm2m_engine_observation_handler(struct lwm2m_message *msg, int observe, uint16_t accept,
1734 				     bool composite)
1735 {
1736 	int r;
1737 
1738 	if (observe == 0) {
1739 		/* add new observer */
1740 		r = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_OBSERVE,
1741 					   OBSERVE_COUNTER_START);
1742 		if (r < 0) {
1743 			LOG_ERR("OBSERVE option error: %d", r);
1744 			return r;
1745 		}
1746 
1747 		if (composite) {
1748 			r = engine_add_composite_observer(msg, msg->token, msg->tkl, accept);
1749 		} else {
1750 			r = engine_add_observer(msg, msg->token, msg->tkl, accept);
1751 		}
1752 
1753 		if (r < 0) {
1754 			LOG_ERR("add OBSERVE error: %d", r);
1755 		}
1756 	} else if (observe == 1) {
1757 		/* remove observer */
1758 		if (composite) {
1759 			r = engine_remove_composite_observer(msg, msg->token, msg->tkl, accept);
1760 		} else {
1761 			r = engine_remove_observer_by_token(msg->ctx, msg->token, msg->tkl);
1762 			if (r < 0) {
1763 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
1764 				r = engine_remove_observer_by_path(msg->ctx, &msg->path);
1765 				if (r < 0)
1766 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
1767 				{
1768 					LOG_ERR("remove observe error: %d", r);
1769 					r = 0;
1770 				}
1771 			}
1772 		}
1773 
1774 	} else {
1775 		r = -EINVAL;
1776 	}
1777 	return r;
1778 }
1779 
engine_observe_shedule_next_event(struct observe_node * obs,uint16_t srv_obj_inst,const int64_t timestamp)1780 int64_t engine_observe_shedule_next_event(struct observe_node *obs, uint16_t srv_obj_inst,
1781 					  const int64_t timestamp)
1782 {
1783 	struct notification_attrs attrs;
1784 	int64_t t_s = 0;
1785 	int ret;
1786 
1787 	ret = engine_observe_attribute_list_get(&obs->path_list, &attrs, srv_obj_inst);
1788 	if (ret < 0) {
1789 		return 0;
1790 	}
1791 
1792 	if (attrs.pmax) {
1793 		t_s = timestamp + MSEC_PER_SEC * attrs.pmax;
1794 	}
1795 
1796 	return t_s;
1797 }
1798 
lwm2m_engine_get_from_list(sys_slist_t * path_list)1799 struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list)
1800 {
1801 	sys_snode_t *path_node = sys_slist_get(path_list);
1802 	struct lwm2m_obj_path_list *entry;
1803 
1804 	if (!path_node) {
1805 		return NULL;
1806 	}
1807 
1808 	entry = SYS_SLIST_CONTAINER(path_node, entry, node);
1809 	if (entry) {
1810 		memset(entry, 0, sizeof(struct lwm2m_obj_path_list));
1811 	}
1812 	return entry;
1813 }
1814 
lwm2m_engine_free_list(sys_slist_t * path_list,sys_slist_t * free_list)1815 void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list)
1816 {
1817 	sys_snode_t *node;
1818 
1819 	while (NULL != (node = sys_slist_get(path_list))) {
1820 		/* Add to free list */
1821 		sys_slist_append(free_list, node);
1822 	}
1823 }
1824 
lwm2m_engine_path_list_init(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list,struct lwm2m_obj_path_list path_object_buf[],uint8_t path_object_size)1825 void lwm2m_engine_path_list_init(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list,
1826 				 struct lwm2m_obj_path_list path_object_buf[],
1827 				 uint8_t path_object_size)
1828 {
1829 	/* Init list */
1830 	sys_slist_init(lwm2m_path_list);
1831 	sys_slist_init(lwm2m_free_list);
1832 
1833 	/* Put buffer elements to free list */
1834 	for (int i = 0; i < path_object_size; i++) {
1835 		sys_slist_append(lwm2m_free_list, &path_object_buf[i].node);
1836 	}
1837 }
1838 
lwm2m_engine_add_path_to_list(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list,const struct lwm2m_obj_path * path)1839 int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list,
1840 				  const struct lwm2m_obj_path *path)
1841 {
1842 	struct lwm2m_obj_path_list *prev = NULL;
1843 	struct lwm2m_obj_path_list *entry;
1844 	struct lwm2m_obj_path_list *new_entry;
1845 	bool add_before_current = false;
1846 
1847 	if (path->level == LWM2M_PATH_LEVEL_NONE) {
1848 		/* Clear the list if we are adding the root path which includes all */
1849 		lwm2m_engine_free_list(lwm2m_path_list, lwm2m_free_list);
1850 	}
1851 
1852 	/* Check is it at list already here */
1853 	new_entry = lwm2m_engine_get_from_list(lwm2m_free_list);
1854 	if (!new_entry) {
1855 		return -1;
1856 	}
1857 
1858 	new_entry->path = *path;
1859 	if (!sys_slist_is_empty(lwm2m_path_list)) {
1860 
1861 		/* Keep list Ordered by Object ID / Object instance / resource ID /
1862 		 * Resource Instance ID
1863 		 */
1864 		SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
1865 			if (entry->path.level == LWM2M_PATH_LEVEL_NONE ||
1866 			    lwm2m_obj_path_equal(&entry->path, &new_entry->path)) {
1867 				/* Already Root request at list or current path is at list */
1868 				sys_slist_append(lwm2m_free_list, &new_entry->node);
1869 				return 0;
1870 			}
1871 
1872 			/*
1873 			 * algorithm assumes that list is already properly sorted and that
1874 			 * there are no duplicates. general idea:
1875 			 * - if at any level up to new entry's path level, IDs below the level
1876 			 *   match and the ID of the new entry at that level is smaller,
1877 			 *   insert before.
1878 			 * - if all IDs of the new entry match the existing entry, insert before.
1879 			 *   Because of sorting and no duplicates, the existing entry must have
1880 			 *   higher path level and come after the new entry.
1881 			 */
1882 			switch (new_entry->path.level) {
1883 			case LWM2M_PATH_LEVEL_OBJECT:
1884 				add_before_current = new_entry->path.obj_id <= entry->path.obj_id;
1885 				break;
1886 
1887 			case LWM2M_PATH_LEVEL_OBJECT_INST:
1888 				add_before_current =
1889 					(new_entry->path.obj_id < entry->path.obj_id) ||
1890 
1891 					(entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
1892 					 entry->path.obj_id == new_entry->path.obj_id &&
1893 					 new_entry->path.obj_inst_id <= entry->path.obj_inst_id);
1894 				break;
1895 
1896 			case LWM2M_PATH_LEVEL_RESOURCE:
1897 				add_before_current =
1898 					(new_entry->path.obj_id < entry->path.obj_id) ||
1899 
1900 					(entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
1901 					 entry->path.obj_id == new_entry->path.obj_id &&
1902 					 new_entry->path.obj_inst_id < entry->path.obj_inst_id) ||
1903 
1904 					(entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE &&
1905 					 entry->path.obj_id == new_entry->path.obj_id &&
1906 					 entry->path.obj_inst_id == new_entry->path.obj_inst_id &&
1907 					 new_entry->path.res_id <= entry->path.res_id);
1908 				break;
1909 
1910 			case LWM2M_PATH_LEVEL_RESOURCE_INST:
1911 				add_before_current =
1912 					(new_entry->path.obj_id < entry->path.obj_id) ||
1913 
1914 					(entry->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
1915 					 entry->path.obj_id == new_entry->path.obj_id &&
1916 					 new_entry->path.obj_inst_id < entry->path.obj_inst_id) ||
1917 
1918 					(entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE &&
1919 					 entry->path.obj_id == new_entry->path.obj_id &&
1920 					 entry->path.obj_inst_id == new_entry->path.obj_inst_id &&
1921 					 new_entry->path.res_id < entry->path.res_id) ||
1922 
1923 					(entry->path.level >= LWM2M_PATH_LEVEL_RESOURCE_INST &&
1924 					 entry->path.obj_id == new_entry->path.obj_id &&
1925 					 entry->path.obj_inst_id == new_entry->path.obj_inst_id &&
1926 					 entry->path.res_id == new_entry->path.res_id &&
1927 					 new_entry->path.res_inst_id <= entry->path.res_inst_id);
1928 				break;
1929 			}
1930 
1931 			if (add_before_current) {
1932 				if (prev) {
1933 					sys_slist_insert(lwm2m_path_list, &prev->node,
1934 							 &new_entry->node);
1935 				} else {
1936 					sys_slist_prepend(lwm2m_path_list, &new_entry->node);
1937 				}
1938 				return 0;
1939 			}
1940 			prev = entry;
1941 		}
1942 	}
1943 
1944 	/* Add First or new tail entry */
1945 	sys_slist_append(lwm2m_path_list, &new_entry->node);
1946 	return 0;
1947 }
1948 
lwm2m_engine_clear_duplicate_path(sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_free_list)1949 void lwm2m_engine_clear_duplicate_path(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list)
1950 {
1951 	struct lwm2m_obj_path_list *prev = NULL;
1952 	struct lwm2m_obj_path_list *entry, *tmp;
1953 	bool remove_entry;
1954 
1955 	if (sys_slist_is_empty(lwm2m_path_list)) {
1956 		return;
1957 	}
1958 
1959 	/* Keep list Ordered but remove if shorter path is similar */
1960 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(lwm2m_path_list, entry, tmp, node) {
1961 
1962 		if (prev && prev->path.level < entry->path.level) {
1963 			if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT &&
1964 			    entry->path.obj_id == prev->path.obj_id) {
1965 				remove_entry = true;
1966 			} else if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT_INST &&
1967 				   entry->path.obj_id == prev->path.obj_id &&
1968 				   entry->path.obj_inst_id == prev->path.obj_inst_id) {
1969 				/* Remove current from the list */
1970 				remove_entry = true;
1971 			} else if (prev->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
1972 				   entry->path.obj_id == prev->path.obj_id &&
1973 				   entry->path.obj_inst_id == prev->path.obj_inst_id &&
1974 				   entry->path.res_id == prev->path.res_id) {
1975 				/* Remove current from the list */
1976 				remove_entry = true;
1977 			} else {
1978 				remove_entry = false;
1979 			}
1980 
1981 			if (remove_entry) {
1982 				/* Remove Current entry */
1983 				sys_slist_remove(lwm2m_path_list, &prev->node, &entry->node);
1984 				sys_slist_append(lwm2m_free_list, &entry->node);
1985 			} else {
1986 				prev = entry;
1987 			}
1988 		} else {
1989 			prev = entry;
1990 		}
1991 	}
1992 }
1993 
update_resource_instance_notified_value(const struct lwm2m_obj_path * path,const struct lwm2m_engine_res_inst * ref)1994 static void update_resource_instance_notified_value(
1995 		const struct lwm2m_obj_path *path,
1996 		const struct lwm2m_engine_res_inst *ref)
1997 {
1998 	struct lwm2m_notify_value_register *last_notified;
1999 	double new_value;
2000 	int ret;
2001 
2002 	last_notified = notify_value_reg_get(ref);
2003 	if (last_notified == NULL) {
2004 		return;
2005 	}
2006 
2007 	ret = resource_value_as_double(path, &new_value);
2008 	if (ret < 0) {
2009 		return;
2010 	}
2011 
2012 	last_notified->value = new_value;
2013 	last_notified->notified = true;
2014 }
2015 
update_resource_notified_value(const struct lwm2m_obj_path * path,const struct lwm2m_engine_res * res)2016 static void update_resource_notified_value(const struct lwm2m_obj_path *path,
2017 					   const struct lwm2m_engine_res *res)
2018 {
2019 	struct lwm2m_obj_path res_inst_path = {
2020 		.level = LWM2M_PATH_LEVEL_RESOURCE_INST,
2021 		.obj_id = path->obj_id,
2022 		.obj_inst_id = path->obj_inst_id,
2023 		.res_id = path->res_id,
2024 	};
2025 	struct lwm2m_engine_res_inst *res_inst;
2026 
2027 	if (!is_resource_numerical(path)) {
2028 		return;
2029 	}
2030 
2031 	for (int i = 0; i < res->res_inst_count; i++) {
2032 		res_inst = &res->res_instances[i];
2033 		res_inst_path.res_inst_id = res_inst->res_inst_id;
2034 
2035 		if (res_inst->res_inst_id == RES_INSTANCE_NOT_CREATED) {
2036 			continue;
2037 		}
2038 
2039 		update_resource_instance_notified_value(&res_inst_path, res_inst);
2040 	}
2041 }
2042 
update_object_instance_notified_values(const struct lwm2m_obj_path * path,const struct lwm2m_engine_obj_inst * obj_inst)2043 static void update_object_instance_notified_values(
2044 		const struct lwm2m_obj_path *path,
2045 		const struct lwm2m_engine_obj_inst *obj_inst)
2046 {
2047 	struct lwm2m_obj_path res_path = {
2048 		.level = LWM2M_PATH_LEVEL_RESOURCE,
2049 		.obj_id = path->obj_id,
2050 		.obj_inst_id = path->obj_inst_id,
2051 	};
2052 	struct lwm2m_engine_res *res;
2053 
2054 	for (int i = 0; i < obj_inst->resource_count; i++) {
2055 		res = &obj_inst->resources[i];
2056 		res_path.res_id = res->res_id;
2057 
2058 		update_resource_notified_value(&res_path, res);
2059 	}
2060 }
2061 
update_object_notified_values(const struct lwm2m_obj_path * path,const struct lwm2m_engine_obj * obj)2062 static void update_object_notified_values(const struct lwm2m_obj_path *path,
2063 					  const struct lwm2m_engine_obj *obj)
2064 {
2065 	struct lwm2m_engine_obj_inst *obj_inst = next_engine_obj_inst(obj->obj_id, -1);
2066 	struct lwm2m_obj_path obj_inst_path = {
2067 		.level = LWM2M_PATH_LEVEL_OBJECT_INST,
2068 		.obj_id = path->obj_id,
2069 	};
2070 
2071 	if (obj_inst == NULL) {
2072 		return;
2073 	}
2074 
2075 	for (int i = 0; i < obj->instance_count; i++) {
2076 		obj_inst_path.obj_inst_id = obj_inst->obj_inst_id;
2077 
2078 		update_object_instance_notified_values(&obj_inst_path, obj_inst);
2079 
2080 		obj_inst = next_engine_obj_inst(obj->obj_id, obj_inst->obj_inst_id);
2081 		if (obj_inst == NULL) {
2082 			break;
2083 		}
2084 	}
2085 }
2086 
lwm2m_engine_observer_refresh_notified_values(struct observe_node * obs)2087 void lwm2m_engine_observer_refresh_notified_values(struct observe_node *obs)
2088 {
2089 	struct lwm2m_obj_path_list *tmp;
2090 	struct lwm2m_obj_path *obs_path;
2091 	void *ref;
2092 	int ret;
2093 
2094 	lwm2m_registry_lock();
2095 
2096 	SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, tmp, node) {
2097 		obs_path =  &tmp->path;
2098 
2099 		ret = lwm2m_get_path_reference_ptr(NULL, &tmp->path, &ref);
2100 		if (ret < 0) {
2101 			continue;
2102 		}
2103 
2104 		switch (obs_path->level) {
2105 		case LWM2M_PATH_LEVEL_RESOURCE_INST:
2106 			update_resource_instance_notified_value(obs_path, ref);
2107 			break;
2108 		case LWM2M_PATH_LEVEL_RESOURCE:
2109 			update_resource_notified_value(obs_path, ref);
2110 			break;
2111 		case LWM2M_PATH_LEVEL_OBJECT_INST:
2112 			update_object_instance_notified_values(obs_path, ref);
2113 			break;
2114 		case LWM2M_PATH_LEVEL_OBJECT:
2115 			update_object_notified_values(obs_path, ref);
2116 			break;
2117 		default:
2118 			break;
2119 		}
2120 	}
2121 
2122 	lwm2m_registry_unlock();
2123 }
2124