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_registry
16 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
17
18 #include <zephyr/logging/log.h>
19 #include <zephyr/sys/ring_buffer.h>
20 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
21
22 #include "lwm2m_engine.h"
23 #include "lwm2m_object.h"
24 #include "lwm2m_obj_access_control.h"
25 #include "lwm2m_util.h"
26 #include "lwm2m_rd_client.h"
27
28 #include <ctype.h>
29 #include <errno.h>
30 #include <stddef.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
35
36 #include <zephyr/types.h>
37
38 #define BINDING_OPT_MAX_LEN 3 /* "UQ" */
39 #define QUEUE_OPT_MAX_LEN 2 /* "Q" */
40
41 /* Thread safety */
42 static K_MUTEX_DEFINE(registry_lock);
43
lwm2m_registry_lock(void)44 void lwm2m_registry_lock(void)
45 {
46 (void)k_mutex_lock(®istry_lock, K_FOREVER);
47 }
48
lwm2m_registry_unlock(void)49 void lwm2m_registry_unlock(void)
50 {
51 (void)k_mutex_unlock(®istry_lock);
52 }
53
54 /* Default core object version */
55 struct default_obj_version {
56 uint16_t obj_id;
57 uint8_t version_major;
58 uint8_t version_minor;
59 };
60
61 /* Based on Appendix E of the respective LwM2M specification. */
62 static const struct default_obj_version default_obj_versions[] = {
63 #if defined(CONFIG_LWM2M_VERSION_1_0)
64 { LWM2M_OBJECT_SECURITY_ID, 1, 0 },
65 { LWM2M_OBJECT_SERVER_ID, 1, 0 },
66 { LWM2M_OBJECT_ACCESS_CONTROL_ID, 1, 0 },
67 { LWM2M_OBJECT_DEVICE_ID, 1, 0 },
68 { LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 1, 0 },
69 { LWM2M_OBJECT_FIRMWARE_ID, 1, 0 },
70 { LWM2M_OBJECT_LOCATION_ID, 1, 0 },
71 { LWM2M_OBJECT_CONNECTIVITY_STATISTICS_ID, 1, 0 },
72 #elif defined(CONFIG_LWM2M_VERSION_1_1)
73 { LWM2M_OBJECT_SECURITY_ID, 1, 1 },
74 { LWM2M_OBJECT_SERVER_ID, 1, 1 },
75 { LWM2M_OBJECT_ACCESS_CONTROL_ID, 1, 0 },
76 { LWM2M_OBJECT_DEVICE_ID, 1, 1 },
77 { LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID, 1, 2 },
78 { LWM2M_OBJECT_FIRMWARE_ID, 1, 0 },
79 { LWM2M_OBJECT_LOCATION_ID, 1, 0 },
80 { LWM2M_OBJECT_CONNECTIVITY_STATISTICS_ID, 1, 0 },
81 /* OSCORE object not implemented yet, but include it for completeness */
82 { LWM2M_OBJECT_OSCORE_ID, 1, 0 },
83 #else
84 #error "Default core object versions not defined for LwM2M version"
85 #endif
86 };
87
88 /* Resources */
89 static sys_slist_t engine_obj_list;
90 static sys_slist_t engine_obj_inst_list;
91
92 /* Resource wrappers */
lwm2m_engine_obj_list(void)93 sys_slist_t *lwm2m_engine_obj_list(void) { return &engine_obj_list; }
94
lwm2m_engine_obj_inst_list(void)95 sys_slist_t *lwm2m_engine_obj_inst_list(void) { return &engine_obj_inst_list; }
96
97 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
98 static void lwm2m_engine_cache_write(const struct lwm2m_engine_obj_field *obj_field,
99 const struct lwm2m_obj_path *path, const void *value,
100 uint16_t len);
101 #endif
102 /* Engine object */
103
lwm2m_register_obj(struct lwm2m_engine_obj * obj)104 void lwm2m_register_obj(struct lwm2m_engine_obj *obj)
105 {
106 k_mutex_lock(®istry_lock, K_FOREVER);
107 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
108 /* If bootstrap, then bootstrap server should create the ac obj instances */
109 #if !defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
110 int server_obj_inst_id = lwm2m_server_short_id_to_inst(CONFIG_LWM2M_SERVER_DEFAULT_SSID);
111
112 access_control_add_obj(obj->obj_id, server_obj_inst_id);
113 #endif /* CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP */
114 #endif /* CONFIG_LWM2M_ACCESS_CONTROL_ENABLE */
115 sys_slist_append(&engine_obj_list, &obj->node);
116 k_mutex_unlock(®istry_lock);
117 }
118
lwm2m_unregister_obj(struct lwm2m_engine_obj * obj)119 void lwm2m_unregister_obj(struct lwm2m_engine_obj *obj)
120 {
121 k_mutex_lock(®istry_lock, K_FOREVER);
122 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
123 access_control_remove_obj(obj->obj_id);
124 #endif
125 engine_remove_observer_by_id(obj->obj_id, -1);
126 sys_slist_find_and_remove(&engine_obj_list, &obj->node);
127 k_mutex_unlock(®istry_lock);
128 }
129
get_engine_obj(int obj_id)130 struct lwm2m_engine_obj *get_engine_obj(int obj_id)
131 {
132 struct lwm2m_engine_obj *obj;
133
134 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
135 if (obj->obj_id == obj_id) {
136 return obj;
137 }
138 }
139
140 return NULL;
141 }
142
lwm2m_get_engine_obj_field(struct lwm2m_engine_obj * obj,int res_id)143 struct lwm2m_engine_obj_field *lwm2m_get_engine_obj_field(struct lwm2m_engine_obj *obj, int res_id)
144 {
145 int i;
146
147 if (obj && obj->fields && obj->field_count > 0) {
148 for (i = 0; i < obj->field_count; i++) {
149 if (obj->fields[i].res_id == res_id) {
150 return &obj->fields[i];
151 }
152 }
153 }
154
155 return NULL;
156 }
157
lwm2m_engine_get_obj(const struct lwm2m_obj_path * path)158 struct lwm2m_engine_obj *lwm2m_engine_get_obj(const struct lwm2m_obj_path *path)
159 {
160 if (path->level < LWM2M_PATH_LEVEL_OBJECT) {
161 return NULL;
162 }
163
164 return get_engine_obj(path->obj_id);
165 }
166 /* Engine object instance */
167
engine_register_obj_inst(struct lwm2m_engine_obj_inst * obj_inst)168 static void engine_register_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
169 {
170 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
171 /* If bootstrap, then bootstrap server should create the ac obj instances */
172 #if !defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
173 int server_obj_inst_id = lwm2m_server_short_id_to_inst(CONFIG_LWM2M_SERVER_DEFAULT_SSID);
174
175 access_control_add(obj_inst->obj->obj_id, obj_inst->obj_inst_id, server_obj_inst_id);
176 #endif /* CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP */
177 #endif /* CONFIG_LWM2M_ACCESS_CONTROL_ENABLE */
178 sys_slist_append(&engine_obj_inst_list, &obj_inst->node);
179 }
180
engine_unregister_obj_inst(struct lwm2m_engine_obj_inst * obj_inst)181 static void engine_unregister_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
182 {
183 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
184 access_control_remove(obj_inst->obj->obj_id, obj_inst->obj_inst_id);
185 #endif
186 engine_remove_observer_by_id(obj_inst->obj->obj_id, obj_inst->obj_inst_id);
187 sys_slist_find_and_remove(&engine_obj_inst_list, &obj_inst->node);
188 }
189
get_engine_obj_inst(int obj_id,int obj_inst_id)190 struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id, int obj_inst_id)
191 {
192 struct lwm2m_engine_obj_inst *obj_inst;
193
194 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst, node) {
195 if (obj_inst->obj->obj_id == obj_id && obj_inst->obj_inst_id == obj_inst_id) {
196 return obj_inst;
197 }
198 }
199
200 return NULL;
201 }
202
next_engine_obj_inst(int obj_id,int obj_inst_id)203 struct lwm2m_engine_obj_inst *next_engine_obj_inst(int obj_id, int obj_inst_id)
204 {
205 struct lwm2m_engine_obj_inst *obj_inst, *next = NULL;
206
207 SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst, node) {
208 if (obj_inst->obj->obj_id == obj_id && obj_inst->obj_inst_id > obj_inst_id &&
209 (!next || next->obj_inst_id > obj_inst->obj_inst_id)) {
210 next = obj_inst;
211 }
212 }
213
214 return next;
215 }
216
lwm2m_create_obj_inst(uint16_t obj_id,uint16_t obj_inst_id,struct lwm2m_engine_obj_inst ** obj_inst)217 int lwm2m_create_obj_inst(uint16_t obj_id, uint16_t obj_inst_id,
218 struct lwm2m_engine_obj_inst **obj_inst)
219 {
220 k_mutex_lock(®istry_lock, K_FOREVER);
221 struct lwm2m_engine_obj *obj;
222 int ret;
223
224 *obj_inst = NULL;
225 obj = get_engine_obj(obj_id);
226 if (!obj) {
227 LOG_ERR("unable to find obj: %u", obj_id);
228 k_mutex_unlock(®istry_lock);
229 return -ENOENT;
230 }
231
232 if (!obj->create_cb) {
233 LOG_ERR("obj %u has no create_cb", obj_id);
234 k_mutex_unlock(®istry_lock);
235 return -EINVAL;
236 }
237
238 if (obj->instance_count + 1 > obj->max_instance_count) {
239 LOG_ERR("no more instances available for obj %u", obj_id);
240 k_mutex_unlock(®istry_lock);
241 return -ENOMEM;
242 }
243
244 *obj_inst = obj->create_cb(obj_inst_id);
245 if (!*obj_inst) {
246 LOG_ERR("unable to create obj %u instance %u", obj_id, obj_inst_id);
247 /*
248 * Already checked for instance count total.
249 * This can only be an error if the object instance exists.
250 */
251 k_mutex_unlock(®istry_lock);
252 return -EEXIST;
253 }
254
255 obj->instance_count++;
256 (*obj_inst)->obj = obj;
257 (*obj_inst)->obj_inst_id = obj_inst_id;
258 engine_register_obj_inst(*obj_inst);
259
260 if (obj->user_create_cb) {
261 ret = obj->user_create_cb(obj_inst_id);
262 if (ret < 0) {
263 LOG_ERR("Error in user obj create %u/%u: %d", obj_id, obj_inst_id, ret);
264 k_mutex_unlock(®istry_lock);
265 lwm2m_delete_obj_inst(obj_id, obj_inst_id);
266 return ret;
267 }
268 }
269 k_mutex_unlock(®istry_lock);
270 return 0;
271 }
272
lwm2m_delete_obj_inst(uint16_t obj_id,uint16_t obj_inst_id)273 int lwm2m_delete_obj_inst(uint16_t obj_id, uint16_t obj_inst_id)
274 {
275 k_mutex_lock(®istry_lock, K_FOREVER);
276 int i, ret = 0;
277 struct lwm2m_engine_obj *obj;
278 struct lwm2m_engine_obj_inst *obj_inst;
279
280 obj = get_engine_obj(obj_id);
281 if (!obj) {
282 k_mutex_unlock(®istry_lock);
283 return -ENOENT;
284 }
285
286 obj_inst = get_engine_obj_inst(obj_id, obj_inst_id);
287 if (!obj_inst) {
288 k_mutex_unlock(®istry_lock);
289 return -ENOENT;
290 }
291
292 if (obj->user_delete_cb) {
293 ret = obj->user_delete_cb(obj_inst_id);
294 if (ret < 0) {
295 LOG_ERR("Error in user obj delete %u/%u: %d", obj_id, obj_inst_id, ret);
296 /* don't return error */
297 }
298 }
299
300 engine_unregister_obj_inst(obj_inst);
301 obj->instance_count--;
302
303 if (obj->delete_cb) {
304 ret = obj->delete_cb(obj_inst_id);
305 }
306
307 /* reset obj_inst and res_inst data structure */
308 for (i = 0; i < obj_inst->resource_count; i++) {
309 clear_attrs(&obj_inst->resources[i]);
310 (void)memset(obj_inst->resources + i, 0, sizeof(struct lwm2m_engine_res));
311 }
312
313 clear_attrs(obj_inst);
314 (void)memset(obj_inst, 0, sizeof(struct lwm2m_engine_obj_inst));
315 k_mutex_unlock(®istry_lock);
316 return ret;
317 }
318
lwm2m_create_object_inst(const struct lwm2m_obj_path * path)319 int lwm2m_create_object_inst(const struct lwm2m_obj_path *path)
320 {
321 struct lwm2m_engine_obj_inst *obj_inst;
322 int ret = 0;
323
324 if (path->level != LWM2M_PATH_LEVEL_OBJECT_INST) {
325 LOG_ERR("path must have 2 parts");
326 return -EINVAL;
327 }
328
329 ret = lwm2m_create_obj_inst(path->obj_id, path->obj_inst_id, &obj_inst);
330 if (ret < 0) {
331 return ret;
332 }
333
334 engine_trigger_update(true);
335
336 return 0;
337 }
338
lwm2m_delete_object_inst(const struct lwm2m_obj_path * path)339 int lwm2m_delete_object_inst(const struct lwm2m_obj_path *path)
340 {
341 int ret = 0;
342
343 if (path->level != LWM2M_PATH_LEVEL_OBJECT_INST) {
344 LOG_ERR("path must have 2 parts");
345 return -EINVAL;
346 }
347
348 ret = lwm2m_delete_obj_inst(path->obj_id, path->obj_inst_id);
349 if (ret < 0) {
350 return ret;
351 }
352
353 engine_trigger_update(true);
354
355 return 0;
356 }
357
lwm2m_engine_get_obj_inst(const struct lwm2m_obj_path * path)358 struct lwm2m_engine_obj_inst *lwm2m_engine_get_obj_inst(const struct lwm2m_obj_path *path)
359 {
360 if (path->level < LWM2M_PATH_LEVEL_OBJECT_INST) {
361 return NULL;
362 }
363
364 return get_engine_obj_inst(path->obj_id, path->obj_inst_id);
365 }
366
path_to_objs(const struct lwm2m_obj_path * path,struct lwm2m_engine_obj_inst ** obj_inst,struct lwm2m_engine_obj_field ** obj_field,struct lwm2m_engine_res ** res,struct lwm2m_engine_res_inst ** res_inst)367 int path_to_objs(const struct lwm2m_obj_path *path, struct lwm2m_engine_obj_inst **obj_inst,
368 struct lwm2m_engine_obj_field **obj_field, struct lwm2m_engine_res **res,
369 struct lwm2m_engine_res_inst **res_inst)
370 {
371 struct lwm2m_engine_obj_inst *oi;
372 struct lwm2m_engine_obj_field *of;
373 struct lwm2m_engine_res *r = NULL;
374 struct lwm2m_engine_res_inst *ri = NULL;
375 int i;
376
377 if (!path) {
378 return -EINVAL;
379 }
380
381 oi = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
382 if (!oi) {
383 LOG_ERR("obj instance %d/%d not found", path->obj_id, path->obj_inst_id);
384 return -ENOENT;
385 }
386
387 if (!oi->resources || oi->resource_count == 0U) {
388 LOG_ERR("obj instance has no resources");
389 return -EINVAL;
390 }
391
392 of = lwm2m_get_engine_obj_field(oi->obj, path->res_id);
393 if (!of) {
394 LOG_ERR("obj field %d not found", path->res_id);
395 return -ENOENT;
396 }
397
398 for (i = 0; i < oi->resource_count; i++) {
399 if (oi->resources[i].res_id == path->res_id) {
400 r = &oi->resources[i];
401 break;
402 }
403 }
404
405 if (!r) {
406 if (LWM2M_HAS_PERM(of, BIT(LWM2M_FLAG_OPTIONAL))) {
407 LOG_DBG("resource %d not found", path->res_id);
408 } else {
409 LOG_ERR("resource %d not found", path->res_id);
410 }
411
412 return -ENOENT;
413 }
414
415 for (i = 0; i < r->res_inst_count; i++) {
416 if (r->res_instances[i].res_inst_id == path->res_inst_id) {
417 ri = &r->res_instances[i];
418 break;
419 }
420 }
421
422 /* specifically don't complain about missing resource instance */
423
424 if (obj_inst) {
425 *obj_inst = oi;
426 }
427
428 if (obj_field) {
429 *obj_field = of;
430 }
431
432 if (res) {
433 *res = r;
434 }
435
436 if (ri && res_inst) {
437 *res_inst = ri;
438 }
439
440 return 0;
441 }
442
is_string(const struct lwm2m_obj_path * path)443 static bool is_string(const struct lwm2m_obj_path *path)
444 {
445 struct lwm2m_engine_obj_field *obj_field;
446 int ret;
447
448 ret = path_to_objs(path, NULL, &obj_field, NULL, NULL);
449 if (ret < 0 || !obj_field) {
450 return false;
451 }
452 if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
453 return true;
454 }
455 return false;
456 }
457
458 /* User data setter functions */
459
lwm2m_set_res_buf(const struct lwm2m_obj_path * path,void * buffer_ptr,uint16_t buffer_len,uint16_t data_len,uint8_t data_flags)460 int lwm2m_set_res_buf(const struct lwm2m_obj_path *path, void *buffer_ptr, uint16_t buffer_len,
461 uint16_t data_len, uint8_t data_flags)
462 {
463 int ret;
464 struct lwm2m_engine_res_inst *res_inst = NULL;
465
466 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
467 LOG_ERR("path must have at least 3 parts");
468 return -EINVAL;
469 }
470
471 k_mutex_lock(®istry_lock, K_FOREVER);
472 /* look up resource obj */
473 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
474 if (ret < 0) {
475 k_mutex_unlock(®istry_lock);
476 return ret;
477 }
478
479 if (!res_inst) {
480 LOG_ERR("res instance %d not found", path->res_inst_id);
481 k_mutex_unlock(®istry_lock);
482 return -ENOENT;
483 }
484
485 /* assign data elements */
486 res_inst->data_ptr = buffer_ptr;
487 res_inst->data_len = data_len;
488 res_inst->max_data_len = buffer_len;
489 res_inst->data_flags = data_flags;
490
491 k_mutex_unlock(®istry_lock);
492 return ret;
493 }
494
lwm2m_validate_time_resource_lenghts(uint16_t resource_length,uint16_t buf_length)495 static bool lwm2m_validate_time_resource_lenghts(uint16_t resource_length, uint16_t buf_length)
496 {
497 if (resource_length != sizeof(time_t) && resource_length != sizeof(uint32_t)) {
498 return false;
499 }
500
501 if (buf_length != sizeof(time_t) && buf_length != sizeof(uint32_t)) {
502 return false;
503 }
504
505 return true;
506 }
507
lwm2m_check_buf_sizes(uint8_t data_type,uint16_t resource_length,uint16_t buf_length)508 static int lwm2m_check_buf_sizes(uint8_t data_type, uint16_t resource_length, uint16_t buf_length)
509 {
510 switch (data_type) {
511 case LWM2M_RES_TYPE_OPAQUE:
512 case LWM2M_RES_TYPE_STRING:
513 if (resource_length > buf_length) {
514 return -ENOMEM;
515 }
516 break;
517 case LWM2M_RES_TYPE_U32:
518 case LWM2M_RES_TYPE_U16:
519 case LWM2M_RES_TYPE_U8:
520 case LWM2M_RES_TYPE_S64:
521 case LWM2M_RES_TYPE_S32:
522 case LWM2M_RES_TYPE_S16:
523 case LWM2M_RES_TYPE_S8:
524 case LWM2M_RES_TYPE_BOOL:
525 case LWM2M_RES_TYPE_FLOAT:
526 case LWM2M_RES_TYPE_OBJLNK:
527 if (resource_length != buf_length) {
528 return -EINVAL;
529 }
530 break;
531 default:
532 return 0;
533 }
534 return 0;
535 }
536
lwm2m_engine_set(const struct lwm2m_obj_path * path,const void * value,uint16_t len)537 static int lwm2m_engine_set(const struct lwm2m_obj_path *path, const void *value, uint16_t len)
538 {
539 struct lwm2m_engine_obj_inst *obj_inst;
540 struct lwm2m_engine_obj_field *obj_field;
541 struct lwm2m_engine_res *res = NULL;
542 struct lwm2m_engine_res_inst *res_inst = NULL;
543 void *data_ptr = NULL;
544 size_t max_data_len = 0;
545 int ret = 0;
546 bool changed = false;
547
548 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
549 LOG_ERR("path must have at least 3 parts");
550 return -EINVAL;
551 }
552
553 LOG_DBG("path:%u/%u/%u, buf:%p, len:%d", path->obj_id, path->obj_inst_id,
554 path->res_id, value, len);
555
556 k_mutex_lock(®istry_lock, K_FOREVER);
557 /* look up resource obj */
558 ret = path_to_objs(path, &obj_inst, &obj_field, &res, &res_inst);
559 if (ret < 0) {
560 k_mutex_unlock(®istry_lock);
561 return ret;
562 }
563
564 if (!res_inst) {
565 LOG_ERR("res instance %d not found", path->res_inst_id);
566 k_mutex_unlock(®istry_lock);
567 return -ENOENT;
568 }
569
570 if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
571 LOG_ERR("res instance data pointer is read-only "
572 "[%u/%u/%u/%u:lvl%u]", path->obj_id, path->obj_inst_id, path->res_id,
573 path->res_inst_id, path->level);
574 k_mutex_unlock(®istry_lock);
575 return -EACCES;
576 }
577
578 /* setup initial data elements */
579 data_ptr = res_inst->data_ptr;
580 max_data_len = res_inst->max_data_len;
581
582 /* allow user to override data elements via callback */
583 if (res->pre_write_cb) {
584 data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, res->res_id,
585 res_inst->res_inst_id, &max_data_len);
586 }
587
588 if (!data_ptr) {
589 LOG_ERR("res instance data pointer is NULL [%u/%u/%u/%u:%u]", path->obj_id,
590 path->obj_inst_id, path->res_id, path->res_inst_id, path->level);
591 k_mutex_unlock(®istry_lock);
592 return -EINVAL;
593 }
594
595 ret = lwm2m_check_buf_sizes(obj_field->data_type, len, max_data_len);
596 if (ret) {
597 LOG_ERR("Incorrect buffer length %u for res data length %zu", len,
598 max_data_len);
599 k_mutex_unlock(®istry_lock);
600 return ret;
601 }
602
603 if (memcmp(data_ptr, value, len) != 0 || res_inst->data_len != len) {
604 changed = true;
605 }
606
607 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
608 if (res->validate_cb) {
609 ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id, res_inst->res_inst_id,
610 (uint8_t *)value, len, false, 0, 0);
611 if (ret < 0) {
612 k_mutex_unlock(®istry_lock);
613 return -EINVAL;
614 }
615 }
616 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
617
618 switch (obj_field->data_type) {
619
620 case LWM2M_RES_TYPE_OPAQUE:
621 if (len) {
622 memcpy((uint8_t *)data_ptr, value, len);
623 }
624 break;
625
626 case LWM2M_RES_TYPE_STRING:
627 if (len) {
628 strncpy(data_ptr, value, len - 1);
629 ((char *)data_ptr)[len - 1] = '\0';
630 } else {
631 ((char *)data_ptr)[0] = '\0';
632 }
633 break;
634
635 case LWM2M_RES_TYPE_U32:
636 *((uint32_t *)data_ptr) = *(uint32_t *)value;
637 break;
638
639 case LWM2M_RES_TYPE_U16:
640 *((uint16_t *)data_ptr) = *(uint16_t *)value;
641 break;
642
643 case LWM2M_RES_TYPE_U8:
644 *((uint8_t *)data_ptr) = *(uint8_t *)value;
645 break;
646
647 case LWM2M_RES_TYPE_TIME:
648 if (!lwm2m_validate_time_resource_lenghts(max_data_len, len)) {
649 LOG_ERR("Time Set: buffer length %u max data len %zu not supported", len,
650 max_data_len);
651 k_mutex_unlock(®istry_lock);
652 return -EINVAL;
653 }
654
655 if (max_data_len == sizeof(time_t)) {
656 if (len == sizeof(time_t)) {
657 *((time_t *)data_ptr) = *(time_t *)value;
658 } else {
659 *((time_t *)data_ptr) = (time_t) *((uint32_t *)value);
660 }
661 } else {
662 LOG_WRN("Converting time to 32bit may cause integer overflow on resource "
663 "[%u/%u/%u/%u:%u]", path->obj_id, path->obj_inst_id, path->res_id,
664 path->res_inst_id, path->level);
665 if (len == sizeof(uint32_t)) {
666 *((uint32_t *)data_ptr) = *(uint32_t *)value;
667 } else {
668 *((uint32_t *)data_ptr) = (uint32_t) *((time_t *)value);
669 }
670 }
671
672 break;
673
674 case LWM2M_RES_TYPE_S64:
675 *((int64_t *)data_ptr) = *(int64_t *)value;
676 break;
677
678 case LWM2M_RES_TYPE_S32:
679 *((int32_t *)data_ptr) = *(int32_t *)value;
680 break;
681
682 case LWM2M_RES_TYPE_S16:
683 *((int16_t *)data_ptr) = *(int16_t *)value;
684 break;
685
686 case LWM2M_RES_TYPE_S8:
687 *((int8_t *)data_ptr) = *(int8_t *)value;
688 break;
689
690 case LWM2M_RES_TYPE_BOOL:
691 *((bool *)data_ptr) = *(bool *)value;
692 break;
693
694 case LWM2M_RES_TYPE_FLOAT:
695 *(double *)data_ptr = *(double *)value;
696 break;
697
698 case LWM2M_RES_TYPE_OBJLNK:
699 *((struct lwm2m_objlnk *)data_ptr) = *(struct lwm2m_objlnk *)value;
700 break;
701
702 default:
703 LOG_ERR("unknown obj data_type %d", obj_field->data_type);
704 k_mutex_unlock(®istry_lock);
705 return -EINVAL;
706 }
707
708 res_inst->data_len = len;
709
710 /* Cache Data Write */
711 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
712 lwm2m_engine_cache_write(obj_field, path, value, len);
713 #endif
714
715 if (res->post_write_cb) {
716 ret = res->post_write_cb(obj_inst->obj_inst_id, res->res_id, res_inst->res_inst_id,
717 data_ptr, len, false, 0, 0);
718 }
719
720 if (changed && LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
721 lwm2m_notify_observer_path(path);
722 }
723 k_mutex_unlock(®istry_lock);
724 return ret;
725 }
726
lwm2m_set_opaque(const struct lwm2m_obj_path * path,const char * data_ptr,uint16_t data_len)727 int lwm2m_set_opaque(const struct lwm2m_obj_path *path, const char *data_ptr, uint16_t data_len)
728 {
729 return lwm2m_engine_set(path, data_ptr, data_len);
730 }
731
lwm2m_set_string(const struct lwm2m_obj_path * path,const char * data_ptr)732 int lwm2m_set_string(const struct lwm2m_obj_path *path, const char *data_ptr)
733 {
734 uint16_t len = strlen(data_ptr);
735
736 /* String resources contain terminator as well, opaque resources don't */
737 if (is_string(path)) {
738 len += 1;
739 }
740
741 return lwm2m_engine_set(path, data_ptr, len);
742 }
743
lwm2m_set_u8(const struct lwm2m_obj_path * path,uint8_t value)744 int lwm2m_set_u8(const struct lwm2m_obj_path *path, uint8_t value)
745 {
746 return lwm2m_engine_set(path, &value, 1);
747 }
748
lwm2m_set_u16(const struct lwm2m_obj_path * path,uint16_t value)749 int lwm2m_set_u16(const struct lwm2m_obj_path *path, uint16_t value)
750 {
751 return lwm2m_engine_set(path, &value, 2);
752 }
753
lwm2m_set_u32(const struct lwm2m_obj_path * path,uint32_t value)754 int lwm2m_set_u32(const struct lwm2m_obj_path *path, uint32_t value)
755 {
756 return lwm2m_engine_set(path, &value, 4);
757 }
758
lwm2m_set_s8(const struct lwm2m_obj_path * path,int8_t value)759 int lwm2m_set_s8(const struct lwm2m_obj_path *path, int8_t value)
760 {
761 return lwm2m_engine_set(path, &value, 1);
762 }
763
lwm2m_set_s16(const struct lwm2m_obj_path * path,int16_t value)764 int lwm2m_set_s16(const struct lwm2m_obj_path *path, int16_t value)
765 {
766 return lwm2m_engine_set(path, &value, 2);
767
768 }
769
lwm2m_set_s32(const struct lwm2m_obj_path * path,int32_t value)770 int lwm2m_set_s32(const struct lwm2m_obj_path *path, int32_t value)
771 {
772 return lwm2m_engine_set(path, &value, 4);
773 }
774
lwm2m_set_s64(const struct lwm2m_obj_path * path,int64_t value)775 int lwm2m_set_s64(const struct lwm2m_obj_path *path, int64_t value)
776 {
777 return lwm2m_engine_set(path, &value, 8);
778 }
779
lwm2m_set_bool(const struct lwm2m_obj_path * path,bool value)780 int lwm2m_set_bool(const struct lwm2m_obj_path *path, bool value)
781 {
782 uint8_t temp = (value != 0 ? 1 : 0);
783
784 return lwm2m_engine_set(path, &temp, 1);
785 }
786
lwm2m_set_f64(const struct lwm2m_obj_path * path,const double value)787 int lwm2m_set_f64(const struct lwm2m_obj_path *path, const double value)
788 {
789 return lwm2m_engine_set(path, &value, sizeof(double));
790 }
791
lwm2m_set_objlnk(const struct lwm2m_obj_path * path,const struct lwm2m_objlnk * value)792 int lwm2m_set_objlnk(const struct lwm2m_obj_path *path, const struct lwm2m_objlnk *value)
793 {
794 return lwm2m_engine_set(path, value, sizeof(struct lwm2m_objlnk));
795 }
796
lwm2m_set_time(const struct lwm2m_obj_path * path,time_t value)797 int lwm2m_set_time(const struct lwm2m_obj_path *path, time_t value)
798 {
799 return lwm2m_engine_set(path, &value, sizeof(time_t));
800 }
801
lwm2m_set_res_data_len(const struct lwm2m_obj_path * path,uint16_t data_len)802 int lwm2m_set_res_data_len(const struct lwm2m_obj_path *path, uint16_t data_len)
803 {
804 int ret;
805 void *buffer_ptr;
806 uint16_t buffer_len;
807 uint16_t old_len;
808 uint8_t data_flags;
809
810 ret = lwm2m_get_res_buf(path, &buffer_ptr, &buffer_len, &old_len, &data_flags);
811 if (ret) {
812 return ret;
813 }
814 return lwm2m_set_res_buf(path, buffer_ptr, buffer_len, data_len, data_flags);
815 }
816 /* User data getter functions */
817
lwm2m_get_res_buf(const struct lwm2m_obj_path * path,void ** buffer_ptr,uint16_t * buffer_len,uint16_t * data_len,uint8_t * data_flags)818 int lwm2m_get_res_buf(const struct lwm2m_obj_path *path, void **buffer_ptr, uint16_t *buffer_len,
819 uint16_t *data_len, uint8_t *data_flags)
820 {
821 int ret;
822 struct lwm2m_engine_res_inst *res_inst = NULL;
823
824 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
825 LOG_ERR("path must have at least 3 parts");
826 return -EINVAL;
827 }
828
829 k_mutex_lock(®istry_lock, K_FOREVER);
830 /* look up resource obj */
831 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
832 if (ret < 0) {
833 k_mutex_unlock(®istry_lock);
834 return ret;
835 }
836
837 if (!res_inst) {
838 LOG_ERR("res instance %d not found", path->res_inst_id);
839 k_mutex_unlock(®istry_lock);
840 return -ENOENT;
841 }
842
843 if (buffer_ptr) {
844 *buffer_ptr = res_inst->data_ptr;
845 }
846 if (buffer_len) {
847 *buffer_len = res_inst->max_data_len;
848 }
849 if (data_len) {
850 *data_len = res_inst->data_len;
851 }
852 if (data_flags) {
853 *data_flags = res_inst->data_flags;
854 }
855
856 k_mutex_unlock(®istry_lock);
857 return 0;
858 }
859
lwm2m_engine_get(const struct lwm2m_obj_path * path,void * buf,uint16_t buflen)860 static int lwm2m_engine_get(const struct lwm2m_obj_path *path, void *buf, uint16_t buflen)
861 {
862 int ret = 0;
863 struct lwm2m_engine_obj_inst *obj_inst;
864 struct lwm2m_engine_obj_field *obj_field;
865 struct lwm2m_engine_res *res = NULL;
866 struct lwm2m_engine_res_inst *res_inst = NULL;
867 void *data_ptr = NULL;
868 size_t data_len = 0;
869
870 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
871 LOG_ERR("path must have at least 3 parts");
872 return -EINVAL;
873 }
874 LOG_DBG("path:%u/%u/%u/%u, level %u, buf:%p, buflen:%d", path->obj_id, path->obj_inst_id,
875 path->res_id, path->res_inst_id, path->level, buf, buflen);
876
877 k_mutex_lock(®istry_lock, K_FOREVER);
878 /* look up resource obj */
879 ret = path_to_objs(path, &obj_inst, &obj_field, &res, &res_inst);
880 if (ret < 0) {
881 k_mutex_unlock(®istry_lock);
882 return ret;
883 }
884
885 if (!res_inst) {
886 LOG_ERR("res instance %d not found", path->res_inst_id);
887 k_mutex_unlock(®istry_lock);
888 return -ENOENT;
889 }
890
891 /* setup initial data elements */
892 data_ptr = res_inst->data_ptr;
893 data_len = res_inst->data_len;
894
895 /* allow user to override data elements via callback */
896 if (res->read_cb) {
897 data_ptr = res->read_cb(obj_inst->obj_inst_id, res->res_id, res_inst->res_inst_id,
898 &data_len);
899 }
900
901 if (data_ptr && data_len > 0) {
902 ret = lwm2m_check_buf_sizes(obj_field->data_type, data_len, buflen);
903 if (ret) {
904 LOG_ERR("Incorrect resource data length %zu. Buffer length %u", data_len,
905 buflen);
906 k_mutex_unlock(®istry_lock);
907 return ret;
908 }
909
910 switch (obj_field->data_type) {
911
912 case LWM2M_RES_TYPE_OPAQUE:
913 memcpy(buf, data_ptr, data_len);
914 break;
915
916 case LWM2M_RES_TYPE_STRING:
917 strncpy(buf, data_ptr, data_len - 1);
918 ((char *)buf)[data_len - 1] = '\0';
919 break;
920
921 case LWM2M_RES_TYPE_U32:
922 *(uint32_t *)buf = *(uint32_t *)data_ptr;
923 break;
924 case LWM2M_RES_TYPE_TIME:
925 if (!lwm2m_validate_time_resource_lenghts(data_len, buflen)) {
926 LOG_ERR("Time get buffer length %u data len %zu not supported",
927 buflen, data_len);
928 k_mutex_unlock(®istry_lock);
929 return -EINVAL;
930 }
931
932 if (data_len == sizeof(time_t)) {
933 if (buflen == sizeof(time_t)) {
934 *((time_t *)buf) = *(time_t *)data_ptr;
935 } else {
936 /* In this case get operation may not got correct value */
937 LOG_WRN("Converting time to 32bit may cause integer "
938 "overflow");
939 *((uint32_t *)buf) = (uint32_t) *((time_t *)data_ptr);
940 }
941 } else {
942 LOG_WRN("Converting time to 32bit may cause integer overflow");
943 if (buflen == sizeof(uint32_t)) {
944 *((uint32_t *)buf) = *(uint32_t *)data_ptr;
945 } else {
946 *((time_t *)buf) = (time_t) *((uint32_t *)data_ptr);
947 }
948 }
949 break;
950
951 case LWM2M_RES_TYPE_U16:
952 *(uint16_t *)buf = *(uint16_t *)data_ptr;
953 break;
954
955 case LWM2M_RES_TYPE_U8:
956 *(uint8_t *)buf = *(uint8_t *)data_ptr;
957 break;
958
959 case LWM2M_RES_TYPE_S64:
960 *(int64_t *)buf = *(int64_t *)data_ptr;
961 break;
962
963 case LWM2M_RES_TYPE_S32:
964 *(int32_t *)buf = *(int32_t *)data_ptr;
965 break;
966
967 case LWM2M_RES_TYPE_S16:
968 *(int16_t *)buf = *(int16_t *)data_ptr;
969 break;
970
971 case LWM2M_RES_TYPE_S8:
972 *(int8_t *)buf = *(int8_t *)data_ptr;
973 break;
974
975 case LWM2M_RES_TYPE_BOOL:
976 *(bool *)buf = *(bool *)data_ptr;
977 break;
978
979 case LWM2M_RES_TYPE_FLOAT:
980 *(double *)buf = *(double *)data_ptr;
981 break;
982
983 case LWM2M_RES_TYPE_OBJLNK:
984 *(struct lwm2m_objlnk *)buf = *(struct lwm2m_objlnk *)data_ptr;
985 break;
986
987 default:
988 LOG_ERR("unknown obj data_type %d", obj_field->data_type);
989 k_mutex_unlock(®istry_lock);
990 return -EINVAL;
991 }
992 } else if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
993 /* Ensure empty string when there is no data */
994 ((char *)buf)[0] = '\0';
995 }
996 k_mutex_unlock(®istry_lock);
997 return 0;
998 }
999
lwm2m_get_opaque(const struct lwm2m_obj_path * path,void * buf,uint16_t buflen)1000 int lwm2m_get_opaque(const struct lwm2m_obj_path *path, void *buf, uint16_t buflen)
1001 {
1002 return lwm2m_engine_get(path, buf, buflen);
1003 }
1004
lwm2m_get_string(const struct lwm2m_obj_path * path,void * str,uint16_t buflen)1005 int lwm2m_get_string(const struct lwm2m_obj_path *path, void *str, uint16_t buflen)
1006 {
1007 /* Ensure termination, in case resource is not a string type */
1008 if (!is_string(path)) {
1009 memset(str, 0, buflen);
1010 buflen -= 1; /* Last terminator cannot be overwritten */
1011 }
1012
1013 return lwm2m_engine_get(path, str, buflen);
1014 }
1015
lwm2m_get_u8(const struct lwm2m_obj_path * path,uint8_t * value)1016 int lwm2m_get_u8(const struct lwm2m_obj_path *path, uint8_t *value)
1017 {
1018 return lwm2m_engine_get(path, value, 1);
1019 }
1020
lwm2m_get_u16(const struct lwm2m_obj_path * path,uint16_t * value)1021 int lwm2m_get_u16(const struct lwm2m_obj_path *path, uint16_t *value)
1022 {
1023 return lwm2m_engine_get(path, value, 2);
1024 }
1025
lwm2m_get_u32(const struct lwm2m_obj_path * path,uint32_t * value)1026 int lwm2m_get_u32(const struct lwm2m_obj_path *path, uint32_t *value)
1027 {
1028 return lwm2m_engine_get(path, value, 4);
1029 }
1030
lwm2m_get_s8(const struct lwm2m_obj_path * path,int8_t * value)1031 int lwm2m_get_s8(const struct lwm2m_obj_path *path, int8_t *value)
1032 {
1033 return lwm2m_engine_get(path, value, 1);
1034 }
1035
lwm2m_get_s16(const struct lwm2m_obj_path * path,int16_t * value)1036 int lwm2m_get_s16(const struct lwm2m_obj_path *path, int16_t *value)
1037 {
1038 return lwm2m_engine_get(path, value, 2);
1039 }
1040
lwm2m_get_s32(const struct lwm2m_obj_path * path,int32_t * value)1041 int lwm2m_get_s32(const struct lwm2m_obj_path *path, int32_t *value)
1042 {
1043 return lwm2m_engine_get(path, value, 4);
1044 }
1045
lwm2m_get_s64(const struct lwm2m_obj_path * path,int64_t * value)1046 int lwm2m_get_s64(const struct lwm2m_obj_path *path, int64_t *value)
1047 {
1048 return lwm2m_engine_get(path, value, 8);
1049 }
1050
lwm2m_get_bool(const struct lwm2m_obj_path * path,bool * value)1051 int lwm2m_get_bool(const struct lwm2m_obj_path *path, bool *value)
1052 {
1053 int ret = 0;
1054 int8_t temp = 0;
1055
1056 ret = lwm2m_get_s8(path, &temp);
1057 if (!ret) {
1058 *value = temp != 0;
1059 }
1060
1061 return ret;
1062 }
1063
lwm2m_get_f64(const struct lwm2m_obj_path * path,double * value)1064 int lwm2m_get_f64(const struct lwm2m_obj_path *path, double *value)
1065 {
1066 return lwm2m_engine_get(path, value, sizeof(double));
1067 }
1068
lwm2m_get_objlnk(const struct lwm2m_obj_path * path,struct lwm2m_objlnk * buf)1069 int lwm2m_get_objlnk(const struct lwm2m_obj_path *path, struct lwm2m_objlnk *buf)
1070 {
1071 return lwm2m_engine_get(path, buf, sizeof(struct lwm2m_objlnk));
1072 }
1073
lwm2m_get_time(const struct lwm2m_obj_path * path,time_t * buf)1074 int lwm2m_get_time(const struct lwm2m_obj_path *path, time_t *buf)
1075 {
1076 return lwm2m_engine_get(path, buf, sizeof(time_t));
1077 }
1078
lwm2m_get_resource(const struct lwm2m_obj_path * path,struct lwm2m_engine_res ** res)1079 int lwm2m_get_resource(const struct lwm2m_obj_path *path, struct lwm2m_engine_res **res)
1080 {
1081 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
1082 LOG_ERR("path must have 3 parts");
1083 return -EINVAL;
1084 }
1085
1086 return path_to_objs(path, NULL, NULL, res, NULL);
1087 }
1088
lwm2m_engine_get_opaque_more(struct lwm2m_input_context * in,uint8_t * buf,size_t buflen,struct lwm2m_opaque_context * opaque,bool * last_block)1089 size_t lwm2m_engine_get_opaque_more(struct lwm2m_input_context *in, uint8_t *buf, size_t buflen,
1090 struct lwm2m_opaque_context *opaque, bool *last_block)
1091 {
1092 uint32_t in_len = opaque->remaining;
1093 uint16_t remaining = in->in_cpkt->max_len - in->offset;
1094
1095 if (in_len > buflen) {
1096 in_len = buflen;
1097 }
1098
1099 if (in_len > remaining) {
1100 in_len = remaining;
1101 }
1102
1103 opaque->remaining -= in_len;
1104 remaining -= in_len;
1105 if (opaque->remaining == 0U || remaining == 0) {
1106 *last_block = true;
1107 }
1108
1109 if (buf_read(buf, in_len, CPKT_BUF_READ(in->in_cpkt), &in->offset) < 0) {
1110 *last_block = true;
1111 return 0;
1112 }
1113
1114 return (size_t)in_len;
1115 }
1116
lwm2m_engine_get_queue_mode(char * queue)1117 void lwm2m_engine_get_queue_mode(char *queue)
1118 {
1119 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
1120 strncpy(queue, "Q", QUEUE_OPT_MAX_LEN);
1121 } else {
1122 strncpy(queue, "", QUEUE_OPT_MAX_LEN);
1123 }
1124 }
1125
lwm2m_engine_get_binding(char * binding)1126 void lwm2m_engine_get_binding(char *binding)
1127 {
1128 /* Defaults to UDP. */
1129 strncpy(binding, "U", BINDING_OPT_MAX_LEN);
1130 #if CONFIG_LWM2M_VERSION_1_0
1131 /* In LwM2M 1.0 binding and queue mode are in same parameter */
1132 char queue[QUEUE_OPT_MAX_LEN];
1133
1134 lwm2m_engine_get_queue_mode(queue);
1135 strncat(binding, queue, QUEUE_OPT_MAX_LEN);
1136 #endif
1137 }
1138 /* Engine resource instance */
1139
lwm2m_engine_allocate_resource_instance(struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst ** res_inst,uint8_t resource_instance_id)1140 static int lwm2m_engine_allocate_resource_instance(struct lwm2m_engine_res *res,
1141 struct lwm2m_engine_res_inst **res_inst,
1142 uint8_t resource_instance_id)
1143 {
1144 int i;
1145
1146 if (!res->res_instances || res->res_inst_count == 0) {
1147 return -ENOMEM;
1148 }
1149
1150 for (i = 0; i < res->res_inst_count; i++) {
1151 if (res->res_instances[i].res_inst_id == RES_INSTANCE_NOT_CREATED) {
1152 break;
1153 }
1154 }
1155
1156 if (i >= res->res_inst_count) {
1157 return -ENOMEM;
1158 }
1159
1160 res->res_instances[i].res_inst_id = resource_instance_id;
1161 *res_inst = &res->res_instances[i];
1162 return 0;
1163 }
1164
lwm2m_engine_get_create_res_inst(const struct lwm2m_obj_path * path,struct lwm2m_engine_res ** res,struct lwm2m_engine_res_inst ** res_inst)1165 int lwm2m_engine_get_create_res_inst(const struct lwm2m_obj_path *path,
1166 struct lwm2m_engine_res **res,
1167 struct lwm2m_engine_res_inst **res_inst)
1168 {
1169 int ret;
1170 struct lwm2m_engine_res *r = NULL;
1171 struct lwm2m_engine_res_inst *r_i = NULL;
1172
1173 ret = path_to_objs(path, NULL, NULL, &r, &r_i);
1174 if (ret < 0) {
1175 return ret;
1176 }
1177
1178 if (!r) {
1179 return -ENOENT;
1180 }
1181 /* Store resource pointer */
1182 *res = r;
1183
1184 if (!r_i) {
1185 if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
1186 return -EINVAL;
1187 }
1188
1189 ret = lwm2m_engine_allocate_resource_instance(r, &r_i, path->res_inst_id);
1190 if (ret < 0) {
1191 return ret;
1192 }
1193 }
1194
1195 /* Store resource instance pointer */
1196 *res_inst = r_i;
1197 return 0;
1198 }
1199
lwm2m_create_res_inst(const struct lwm2m_obj_path * path)1200 int lwm2m_create_res_inst(const struct lwm2m_obj_path *path)
1201 {
1202 int ret;
1203 struct lwm2m_engine_res *res = NULL;
1204 struct lwm2m_engine_res_inst *res_inst = NULL;
1205
1206 if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
1207 LOG_ERR("path must have 4 parts");
1208 return -EINVAL;
1209 }
1210 k_mutex_lock(®istry_lock, K_FOREVER);
1211 ret = path_to_objs(path, NULL, NULL, &res, &res_inst);
1212 if (ret < 0) {
1213 k_mutex_unlock(®istry_lock);
1214 return ret;
1215 }
1216
1217 if (!res) {
1218 LOG_ERR("resource %u not found", path->res_id);
1219 k_mutex_unlock(®istry_lock);
1220 return -ENOENT;
1221 }
1222
1223 if (res_inst && res_inst->res_inst_id != RES_INSTANCE_NOT_CREATED) {
1224 LOG_ERR("res instance %u already exists", path->res_inst_id);
1225 k_mutex_unlock(®istry_lock);
1226 return -EINVAL;
1227 }
1228 k_mutex_unlock(®istry_lock);
1229 return lwm2m_engine_allocate_resource_instance(res, &res_inst, path->res_inst_id);
1230 }
1231
lwm2m_delete_res_inst(const struct lwm2m_obj_path * path)1232 int lwm2m_delete_res_inst(const struct lwm2m_obj_path *path)
1233 {
1234 int ret;
1235 struct lwm2m_engine_res_inst *res_inst = NULL;
1236
1237 if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
1238 LOG_ERR("path must have 4 parts");
1239 return -EINVAL;
1240 }
1241 k_mutex_lock(®istry_lock, K_FOREVER);
1242 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
1243 if (ret < 0) {
1244 k_mutex_unlock(®istry_lock);
1245 return ret;
1246 }
1247
1248 if (!res_inst) {
1249 LOG_ERR("res instance %u not found", path->res_inst_id);
1250 k_mutex_unlock(®istry_lock);
1251 return -ENOENT;
1252 }
1253
1254 res_inst->data_ptr = NULL;
1255 res_inst->max_data_len = 0U;
1256 res_inst->data_len = 0U;
1257 res_inst->res_inst_id = RES_INSTANCE_NOT_CREATED;
1258 k_mutex_unlock(®istry_lock);
1259 return 0;
1260 }
1261 /* Register callbacks */
1262
lwm2m_register_read_callback(const struct lwm2m_obj_path * path,lwm2m_engine_get_data_cb_t cb)1263 int lwm2m_register_read_callback(const struct lwm2m_obj_path *path, lwm2m_engine_get_data_cb_t cb)
1264 {
1265 int ret;
1266 struct lwm2m_engine_res *res = NULL;
1267
1268 ret = lwm2m_get_resource(path, &res);
1269 if (ret < 0) {
1270 return ret;
1271 }
1272
1273 res->read_cb = cb;
1274 return 0;
1275 }
1276
lwm2m_register_pre_write_callback(const struct lwm2m_obj_path * path,lwm2m_engine_get_data_cb_t cb)1277 int lwm2m_register_pre_write_callback(const struct lwm2m_obj_path *path,
1278 lwm2m_engine_get_data_cb_t cb)
1279 {
1280 int ret;
1281 struct lwm2m_engine_res *res = NULL;
1282
1283 ret = lwm2m_get_resource(path, &res);
1284 if (ret < 0) {
1285 return ret;
1286 }
1287
1288 res->pre_write_cb = cb;
1289 return 0;
1290 }
1291
lwm2m_register_validate_callback(const struct lwm2m_obj_path * path,lwm2m_engine_set_data_cb_t cb)1292 int lwm2m_register_validate_callback(const struct lwm2m_obj_path *path,
1293 lwm2m_engine_set_data_cb_t cb)
1294 {
1295 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1296 int ret;
1297 struct lwm2m_engine_res *res = NULL;
1298
1299 ret = lwm2m_get_resource(path, &res);
1300 if (ret < 0) {
1301 return ret;
1302 }
1303
1304 res->validate_cb = cb;
1305 return 0;
1306 #else
1307 ARG_UNUSED(path);
1308 ARG_UNUSED(cb);
1309
1310 LOG_ERR("Validation disabled. Set "
1311 "CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 to "
1312 "enable validation support.");
1313 return -ENOTSUP;
1314 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1315 }
1316
lwm2m_register_post_write_callback(const struct lwm2m_obj_path * path,lwm2m_engine_set_data_cb_t cb)1317 int lwm2m_register_post_write_callback(const struct lwm2m_obj_path *path,
1318 lwm2m_engine_set_data_cb_t cb)
1319 {
1320 int ret;
1321 struct lwm2m_engine_res *res = NULL;
1322
1323 ret = lwm2m_get_resource(path, &res);
1324 if (ret < 0) {
1325 return ret;
1326 }
1327
1328 res->post_write_cb = cb;
1329 return 0;
1330 }
1331
lwm2m_register_exec_callback(const struct lwm2m_obj_path * path,lwm2m_engine_execute_cb_t cb)1332 int lwm2m_register_exec_callback(const struct lwm2m_obj_path *path, lwm2m_engine_execute_cb_t cb)
1333 {
1334 int ret;
1335 struct lwm2m_engine_res *res = NULL;
1336
1337 ret = lwm2m_get_resource(path, &res);
1338 if (ret < 0) {
1339 return ret;
1340 }
1341
1342 res->execute_cb = cb;
1343 return 0;
1344 }
1345
lwm2m_register_create_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)1346 int lwm2m_register_create_callback(uint16_t obj_id, lwm2m_engine_user_cb_t cb)
1347 {
1348 struct lwm2m_engine_obj *obj = NULL;
1349
1350 obj = get_engine_obj(obj_id);
1351 if (!obj) {
1352 LOG_ERR("unable to find obj: %u", obj_id);
1353 return -ENOENT;
1354 }
1355
1356 obj->user_create_cb = cb;
1357 return 0;
1358 }
1359
lwm2m_register_delete_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)1360 int lwm2m_register_delete_callback(uint16_t obj_id, lwm2m_engine_user_cb_t cb)
1361 {
1362 struct lwm2m_engine_obj *obj = NULL;
1363
1364 obj = get_engine_obj(obj_id);
1365 if (!obj) {
1366 LOG_ERR("unable to find obj: %u", obj_id);
1367 return -ENOENT;
1368 }
1369
1370 obj->user_delete_cb = cb;
1371 return 0;
1372 }
1373 /* Generic data handlers */
1374
lwm2m_get_or_create_engine_obj(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst ** obj_inst,uint8_t * created)1375 int lwm2m_get_or_create_engine_obj(struct lwm2m_message *msg,
1376 struct lwm2m_engine_obj_inst **obj_inst, uint8_t *created)
1377 {
1378 int ret = 0;
1379
1380 if (created) {
1381 *created = 0U;
1382 }
1383
1384 *obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
1385 if (!*obj_inst) {
1386 ret = lwm2m_create_obj_inst(msg->path.obj_id, msg->path.obj_inst_id, obj_inst);
1387 if (ret < 0) {
1388 return ret;
1389 }
1390
1391 /* set created flag to one */
1392 if (created) {
1393 *created = 1U;
1394 }
1395
1396 if (!msg->ctx->bootstrap_mode) {
1397 engine_trigger_update(true);
1398 }
1399 }
1400
1401 return ret;
1402 }
1403
lwm2m_engine_get_res(const struct lwm2m_obj_path * path)1404 struct lwm2m_engine_res *lwm2m_engine_get_res(const struct lwm2m_obj_path *path)
1405 {
1406 struct lwm2m_engine_res *res = NULL;
1407 int ret;
1408
1409 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
1410 return NULL;
1411 }
1412
1413 ret = path_to_objs(path, NULL, NULL, &res, NULL);
1414 if (ret < 0) {
1415 return NULL;
1416 }
1417
1418 return res;
1419 }
1420
lwm2m_engine_get_res_inst(const struct lwm2m_obj_path * path)1421 struct lwm2m_engine_res_inst *lwm2m_engine_get_res_inst(const struct lwm2m_obj_path *path)
1422 {
1423 struct lwm2m_engine_res_inst *res_inst = NULL;
1424 int ret;
1425
1426 if (path->level != LWM2M_PATH_LEVEL_RESOURCE_INST) {
1427 return NULL;
1428 }
1429
1430 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
1431 if (ret < 0) {
1432 return NULL;
1433 }
1434
1435 return res_inst;
1436 }
1437
lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj * obj)1438 bool lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj *obj)
1439 {
1440 if (IS_ENABLED(CONFIG_LWM2M_ENGINE_ALWAYS_REPORT_OBJ_VERSION)) {
1441 return true;
1442 }
1443
1444 /* For non-core objects, report version other than 1.0 */
1445 if (!obj->is_core) {
1446 return obj->version_major != 1 || obj->version_minor != 0;
1447 }
1448
1449 /* For core objects, report version based on default version array. */
1450 for (size_t i = 0; i < ARRAY_SIZE(default_obj_versions); i++) {
1451 if (obj->obj_id != default_obj_versions[i].obj_id) {
1452 continue;
1453 }
1454
1455 return obj->version_major != default_obj_versions[i].version_major ||
1456 obj->version_minor != default_obj_versions[i].version_minor;
1457 }
1458
1459 return true;
1460 }
1461
1462 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1463 static sys_slist_t lwm2m_timed_cache_list;
1464 static struct lwm2m_time_series_resource lwm2m_cache_entries[CONFIG_LWM2M_MAX_CACHED_RESOURCES];
1465
1466 static struct lwm2m_time_series_resource *
lwm2m_cache_entry_allocate(const struct lwm2m_obj_path * path)1467 lwm2m_cache_entry_allocate(const struct lwm2m_obj_path *path)
1468 {
1469 int i;
1470 struct lwm2m_time_series_resource *entry;
1471
1472 entry = lwm2m_cache_entry_get_by_object(path);
1473 if (entry) {
1474 return entry;
1475 }
1476
1477 for (i = 0; i < ARRAY_SIZE(lwm2m_cache_entries); i++) {
1478 if (lwm2m_cache_entries[i].path.level == 0) {
1479 lwm2m_cache_entries[i].path = *path;
1480 sys_slist_append(&lwm2m_timed_cache_list, &lwm2m_cache_entries[i].node);
1481 return &lwm2m_cache_entries[i];
1482 }
1483 }
1484
1485 return NULL;
1486 }
1487
lwm2m_engine_cache_write(const struct lwm2m_engine_obj_field * obj_field,const struct lwm2m_obj_path * path,const void * value,uint16_t len)1488 static void lwm2m_engine_cache_write(const struct lwm2m_engine_obj_field *obj_field,
1489 const struct lwm2m_obj_path *path, const void *value,
1490 uint16_t len)
1491 {
1492 struct lwm2m_time_series_resource *cache_entry;
1493 struct lwm2m_time_series_elem elements;
1494
1495 cache_entry = lwm2m_cache_entry_get_by_object(path);
1496 if (!cache_entry) {
1497 return;
1498 }
1499
1500 elements.t = time(NULL);
1501
1502 if (elements.t <= 0) {
1503 LOG_WRN("Time() not available");
1504 return;
1505 }
1506
1507 switch (obj_field->data_type) {
1508 case LWM2M_RES_TYPE_U32:
1509 elements.u32 = *(uint32_t *)value;
1510 break;
1511
1512 case LWM2M_RES_TYPE_U16:
1513 elements.u16 = *(uint16_t *)value;
1514 break;
1515
1516 case LWM2M_RES_TYPE_U8:
1517 elements.u8 = *(uint8_t *)value;
1518 break;
1519
1520 case LWM2M_RES_TYPE_S64:
1521 elements.i64 = *(int64_t *)value;
1522 break;
1523
1524 case LWM2M_RES_TYPE_TIME:
1525 if (len == sizeof(time_t)) {
1526 elements.time = *(time_t *)value;
1527 } else if (len == sizeof(uint32_t)) {
1528 elements.time = (time_t) *((uint32_t *)value);
1529 } else {
1530 LOG_ERR("Not supporting size %d bytes for time", len);
1531 return;
1532 }
1533 break;
1534
1535 case LWM2M_RES_TYPE_S32:
1536 elements.i32 = *(int32_t *)value;
1537 break;
1538
1539 case LWM2M_RES_TYPE_S16:
1540 elements.i16 = *(int16_t *)value;
1541 break;
1542
1543 case LWM2M_RES_TYPE_S8:
1544 elements.i8 = *(int8_t *)value;
1545 break;
1546
1547 case LWM2M_RES_TYPE_BOOL:
1548 elements.b = *(bool *)value;
1549 break;
1550
1551 default:
1552 elements.f = *(double *)value;
1553 break;
1554 }
1555
1556 if (!lwm2m_cache_write(cache_entry, &elements)) {
1557 LOG_WRN("Data cache full");
1558 }
1559 }
1560 #endif /* CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT */
1561
1562 struct lwm2m_time_series_resource *
lwm2m_cache_entry_get_by_object(const struct lwm2m_obj_path * obj_path)1563 lwm2m_cache_entry_get_by_object(const struct lwm2m_obj_path *obj_path)
1564 {
1565 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1566 struct lwm2m_time_series_resource *entry;
1567
1568 if (obj_path->level < LWM2M_PATH_LEVEL_RESOURCE) {
1569 LOG_ERR("Path level wrong for cache %u", obj_path->level);
1570 return NULL;
1571 }
1572
1573 if (sys_slist_is_empty(&lwm2m_timed_cache_list)) {
1574 return NULL;
1575 }
1576
1577 SYS_SLIST_FOR_EACH_CONTAINER(&lwm2m_timed_cache_list, entry, node) {
1578 if (lwm2m_obj_path_equal(&entry->path, obj_path)) {
1579 return entry;
1580 }
1581 }
1582 #endif /* CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT */
1583 return NULL;
1584
1585 }
1586
lwm2m_enable_cache(const struct lwm2m_obj_path * path,struct lwm2m_time_series_elem * data_cache,size_t cache_len)1587 int lwm2m_enable_cache(const struct lwm2m_obj_path *path, struct lwm2m_time_series_elem *data_cache,
1588 size_t cache_len)
1589 {
1590 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1591 struct lwm2m_engine_obj_inst *obj_inst;
1592 struct lwm2m_engine_obj_field *obj_field;
1593 struct lwm2m_engine_res_inst *res_inst = NULL;
1594 struct lwm2m_time_series_resource *cache_entry;
1595 int ret = 0;
1596 size_t cache_entry_size = sizeof(struct lwm2m_time_series_elem);
1597
1598 /* look up resource obj */
1599 ret = path_to_objs(path, &obj_inst, &obj_field, NULL, &res_inst);
1600 if (ret < 0) {
1601 return ret;
1602 }
1603
1604 if (!res_inst) {
1605 LOG_ERR("res instance %d not found", path->res_inst_id);
1606 return -ENOENT;
1607 }
1608
1609 switch (obj_field->data_type) {
1610 case LWM2M_RES_TYPE_U32:
1611 case LWM2M_RES_TYPE_TIME:
1612 case LWM2M_RES_TYPE_U16:
1613 case LWM2M_RES_TYPE_U8:
1614 case LWM2M_RES_TYPE_S64:
1615 case LWM2M_RES_TYPE_S32:
1616 case LWM2M_RES_TYPE_S16:
1617 case LWM2M_RES_TYPE_S8:
1618 case LWM2M_RES_TYPE_BOOL:
1619 case LWM2M_RES_TYPE_FLOAT:
1620 /* Support only fixed width resource types */
1621 cache_entry = lwm2m_cache_entry_allocate(path);
1622 break;
1623 default:
1624 cache_entry = NULL;
1625 break;
1626 }
1627
1628 if (!cache_entry) {
1629 return -ENODATA;
1630 }
1631
1632 ring_buf_init(&cache_entry->rb, cache_entry_size * cache_len, (uint8_t *)data_cache);
1633
1634 return 0;
1635 #else
1636 LOG_ERR("LwM2M resource cache is only supported for "
1637 "CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT");
1638 return -ENOTSUP;
1639 #endif /* CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT */
1640 }
1641
1642 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
lwm2m_engine_data_cache_init(void)1643 static int lwm2m_engine_data_cache_init(void)
1644 {
1645 int i;
1646
1647 sys_slist_init(&lwm2m_timed_cache_list);
1648
1649 for (i = 0; i < ARRAY_SIZE(lwm2m_cache_entries); i++) {
1650 lwm2m_cache_entries[i].path.level = LWM2M_PATH_LEVEL_NONE;
1651 }
1652 return 0;
1653 }
1654 LWM2M_ENGINE_INIT(lwm2m_engine_data_cache_init);
1655 #endif
1656
lwm2m_cache_write(struct lwm2m_time_series_resource * cache_entry,struct lwm2m_time_series_elem * buf)1657 bool lwm2m_cache_write(struct lwm2m_time_series_resource *cache_entry,
1658 struct lwm2m_time_series_elem *buf)
1659 {
1660 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1661 uint32_t length;
1662 uint8_t *buf_ptr;
1663 uint32_t element_size = sizeof(struct lwm2m_time_series_elem);
1664
1665 if (ring_buf_space_get(&cache_entry->rb) < element_size) {
1666 /* No space */
1667 if (IS_ENABLED(CONFIG_LWM2M_CACHE_DROP_LATEST)) {
1668 return false;
1669 }
1670 /* Free entry */
1671 length = ring_buf_get_claim(&cache_entry->rb, &buf_ptr, element_size);
1672 ring_buf_get_finish(&cache_entry->rb, length);
1673 }
1674
1675 length = ring_buf_put_claim(&cache_entry->rb, &buf_ptr, element_size);
1676
1677 if (length != element_size) {
1678 ring_buf_put_finish(&cache_entry->rb, 0);
1679 LOG_ERR("Allocation failed %u", length);
1680 return false;
1681 }
1682
1683 ring_buf_put_finish(&cache_entry->rb, length);
1684 /* Store data */
1685 memcpy(buf_ptr, buf, element_size);
1686 return true;
1687 #else
1688 return NULL;
1689 #endif
1690 }
1691
lwm2m_cache_read(struct lwm2m_time_series_resource * cache_entry,struct lwm2m_time_series_elem * buf)1692 bool lwm2m_cache_read(struct lwm2m_time_series_resource *cache_entry,
1693 struct lwm2m_time_series_elem *buf)
1694 {
1695 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1696 uint32_t length;
1697 uint8_t *buf_ptr;
1698 uint32_t element_size = sizeof(struct lwm2m_time_series_elem);
1699
1700 if (ring_buf_is_empty(&cache_entry->rb)) {
1701 return false;
1702 }
1703
1704 length = ring_buf_get_claim(&cache_entry->rb, &buf_ptr, element_size);
1705
1706 if (length != element_size) {
1707 LOG_ERR("Cache read fail %u", length);
1708 ring_buf_get_finish(&cache_entry->rb, 0);
1709 return false;
1710 }
1711
1712 /* Read Data */
1713 memcpy(buf, buf_ptr, element_size);
1714 ring_buf_get_finish(&cache_entry->rb, length);
1715 return true;
1716
1717 #else
1718 return NULL;
1719 #endif
1720 }
1721
lwm2m_cache_size(const struct lwm2m_time_series_resource * cache_entry)1722 size_t lwm2m_cache_size(const struct lwm2m_time_series_resource *cache_entry)
1723 {
1724 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1725 uint32_t bytes_available;
1726
1727 /* ring_buf_is_empty() takes non-const pointer but still does not modify */
1728 if (ring_buf_is_empty((struct ring_buf *) &cache_entry->rb)) {
1729 return 0;
1730 }
1731
1732 /* ring_buf_size_get() takes non-const pointer but still does not modify */
1733 bytes_available = ring_buf_size_get((struct ring_buf *) &cache_entry->rb);
1734
1735 return (bytes_available / sizeof(struct lwm2m_time_series_elem));
1736 #else
1737 return 0;
1738 #endif
1739 }
1740
lwm2m_set_bulk(const struct lwm2m_res_item res_list[],size_t res_list_size)1741 int lwm2m_set_bulk(const struct lwm2m_res_item res_list[], size_t res_list_size)
1742 {
1743 int ret;
1744
1745 k_mutex_lock(®istry_lock, K_FOREVER);
1746 for (int i = 0; i < res_list_size; i++) {
1747
1748 ret = lwm2m_engine_set(res_list[i].path, res_list[i].value, res_list[i].size);
1749
1750 if (ret) {
1751 k_mutex_unlock(®istry_lock);
1752 return ret;
1753 }
1754 }
1755 k_mutex_unlock(®istry_lock);
1756
1757 return 0;
1758 }
1759