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 ¬ify_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(¬ify_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 ¬ify_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