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->len - opaque->offset;
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 remaining -= in_len;
1104 if (opaque->offset + in_len >= opaque->len) {
1105 *last_block = true;
1106 }
1107
1108 if (buf_read(buf, in_len, CPKT_BUF_READ(in->in_cpkt), &in->offset) < 0) {
1109 *last_block = true;
1110 return 0;
1111 }
1112
1113 return (size_t)in_len;
1114 }
1115
lwm2m_engine_get_queue_mode(char * queue)1116 void lwm2m_engine_get_queue_mode(char *queue)
1117 {
1118 if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
1119 strncpy(queue, "Q", QUEUE_OPT_MAX_LEN);
1120 } else {
1121 strncpy(queue, "", QUEUE_OPT_MAX_LEN);
1122 }
1123 }
1124
lwm2m_engine_get_binding(char * binding)1125 void lwm2m_engine_get_binding(char *binding)
1126 {
1127 /* Defaults to UDP. */
1128 strncpy(binding, "U", BINDING_OPT_MAX_LEN);
1129 #if CONFIG_LWM2M_VERSION_1_0
1130 /* In LwM2M 1.0 binding and queue mode are in same parameter */
1131 char queue[QUEUE_OPT_MAX_LEN];
1132
1133 lwm2m_engine_get_queue_mode(queue);
1134 strncat(binding, queue, QUEUE_OPT_MAX_LEN);
1135 #endif
1136 }
1137 /* Engine resource instance */
1138
lwm2m_engine_allocate_resource_instance(struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst ** res_inst,uint8_t resource_instance_id)1139 static int lwm2m_engine_allocate_resource_instance(struct lwm2m_engine_res *res,
1140 struct lwm2m_engine_res_inst **res_inst,
1141 uint8_t resource_instance_id)
1142 {
1143 int i;
1144
1145 if (!res->res_instances || res->res_inst_count == 0) {
1146 return -ENOMEM;
1147 }
1148
1149 for (i = 0; i < res->res_inst_count; i++) {
1150 if (res->res_instances[i].res_inst_id == RES_INSTANCE_NOT_CREATED) {
1151 break;
1152 }
1153 }
1154
1155 if (i >= res->res_inst_count) {
1156 return -ENOMEM;
1157 }
1158
1159 res->res_instances[i].res_inst_id = resource_instance_id;
1160 *res_inst = &res->res_instances[i];
1161 return 0;
1162 }
1163
lwm2m_engine_get_create_res_inst(const struct lwm2m_obj_path * path,struct lwm2m_engine_res ** res,struct lwm2m_engine_res_inst ** res_inst)1164 int lwm2m_engine_get_create_res_inst(const struct lwm2m_obj_path *path,
1165 struct lwm2m_engine_res **res,
1166 struct lwm2m_engine_res_inst **res_inst)
1167 {
1168 int ret;
1169 struct lwm2m_engine_res *r = NULL;
1170 struct lwm2m_engine_res_inst *r_i = NULL;
1171
1172 ret = path_to_objs(path, NULL, NULL, &r, &r_i);
1173 if (ret < 0) {
1174 return ret;
1175 }
1176
1177 if (!r) {
1178 return -ENOENT;
1179 }
1180 /* Store resource pointer */
1181 *res = r;
1182
1183 if (!r_i) {
1184 if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
1185 return -EINVAL;
1186 }
1187
1188 ret = lwm2m_engine_allocate_resource_instance(r, &r_i, path->res_inst_id);
1189 if (ret < 0) {
1190 return ret;
1191 }
1192 }
1193
1194 /* Store resource instance pointer */
1195 *res_inst = r_i;
1196 return 0;
1197 }
1198
lwm2m_create_res_inst(const struct lwm2m_obj_path * path)1199 int lwm2m_create_res_inst(const struct lwm2m_obj_path *path)
1200 {
1201 int ret;
1202 struct lwm2m_engine_res *res = NULL;
1203 struct lwm2m_engine_res_inst *res_inst = NULL;
1204
1205 if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
1206 LOG_ERR("path must have 4 parts");
1207 return -EINVAL;
1208 }
1209 k_mutex_lock(®istry_lock, K_FOREVER);
1210 ret = path_to_objs(path, NULL, NULL, &res, &res_inst);
1211 if (ret < 0) {
1212 k_mutex_unlock(®istry_lock);
1213 return ret;
1214 }
1215
1216 if (!res) {
1217 LOG_ERR("resource %u not found", path->res_id);
1218 k_mutex_unlock(®istry_lock);
1219 return -ENOENT;
1220 }
1221
1222 if (res_inst && res_inst->res_inst_id != RES_INSTANCE_NOT_CREATED) {
1223 LOG_ERR("res instance %u already exists", path->res_inst_id);
1224 k_mutex_unlock(®istry_lock);
1225 return -EINVAL;
1226 }
1227 k_mutex_unlock(®istry_lock);
1228 return lwm2m_engine_allocate_resource_instance(res, &res_inst, path->res_inst_id);
1229 }
1230
lwm2m_delete_res_inst(const struct lwm2m_obj_path * path)1231 int lwm2m_delete_res_inst(const struct lwm2m_obj_path *path)
1232 {
1233 int ret;
1234 struct lwm2m_engine_res_inst *res_inst = NULL;
1235
1236 if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
1237 LOG_ERR("path must have 4 parts");
1238 return -EINVAL;
1239 }
1240 k_mutex_lock(®istry_lock, K_FOREVER);
1241 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
1242 if (ret < 0) {
1243 k_mutex_unlock(®istry_lock);
1244 return ret;
1245 }
1246
1247 if (!res_inst) {
1248 LOG_ERR("res instance %u not found", path->res_inst_id);
1249 k_mutex_unlock(®istry_lock);
1250 return -ENOENT;
1251 }
1252
1253 res_inst->data_ptr = NULL;
1254 res_inst->max_data_len = 0U;
1255 res_inst->data_len = 0U;
1256 res_inst->res_inst_id = RES_INSTANCE_NOT_CREATED;
1257 k_mutex_unlock(®istry_lock);
1258 return 0;
1259 }
1260 /* Register callbacks */
1261
lwm2m_register_read_callback(const struct lwm2m_obj_path * path,lwm2m_engine_get_data_cb_t cb)1262 int lwm2m_register_read_callback(const struct lwm2m_obj_path *path, lwm2m_engine_get_data_cb_t cb)
1263 {
1264 int ret;
1265 struct lwm2m_engine_res *res = NULL;
1266
1267 ret = lwm2m_get_resource(path, &res);
1268 if (ret < 0) {
1269 return ret;
1270 }
1271
1272 res->read_cb = cb;
1273 return 0;
1274 }
1275
lwm2m_register_pre_write_callback(const struct lwm2m_obj_path * path,lwm2m_engine_get_data_cb_t cb)1276 int lwm2m_register_pre_write_callback(const struct lwm2m_obj_path *path,
1277 lwm2m_engine_get_data_cb_t cb)
1278 {
1279 int ret;
1280 struct lwm2m_engine_res *res = NULL;
1281
1282 ret = lwm2m_get_resource(path, &res);
1283 if (ret < 0) {
1284 return ret;
1285 }
1286
1287 res->pre_write_cb = cb;
1288 return 0;
1289 }
1290
lwm2m_register_validate_callback(const struct lwm2m_obj_path * path,lwm2m_engine_set_data_cb_t cb)1291 int lwm2m_register_validate_callback(const struct lwm2m_obj_path *path,
1292 lwm2m_engine_set_data_cb_t cb)
1293 {
1294 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1295 int ret;
1296 struct lwm2m_engine_res *res = NULL;
1297
1298 ret = lwm2m_get_resource(path, &res);
1299 if (ret < 0) {
1300 return ret;
1301 }
1302
1303 res->validate_cb = cb;
1304 return 0;
1305 #else
1306 ARG_UNUSED(path);
1307 ARG_UNUSED(cb);
1308
1309 LOG_ERR("Validation disabled. Set "
1310 "CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 to "
1311 "enable validation support.");
1312 return -ENOTSUP;
1313 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1314 }
1315
lwm2m_register_post_write_callback(const struct lwm2m_obj_path * path,lwm2m_engine_set_data_cb_t cb)1316 int lwm2m_register_post_write_callback(const struct lwm2m_obj_path *path,
1317 lwm2m_engine_set_data_cb_t cb)
1318 {
1319 int ret;
1320 struct lwm2m_engine_res *res = NULL;
1321
1322 ret = lwm2m_get_resource(path, &res);
1323 if (ret < 0) {
1324 return ret;
1325 }
1326
1327 res->post_write_cb = cb;
1328 return 0;
1329 }
1330
lwm2m_register_exec_callback(const struct lwm2m_obj_path * path,lwm2m_engine_execute_cb_t cb)1331 int lwm2m_register_exec_callback(const struct lwm2m_obj_path *path, lwm2m_engine_execute_cb_t cb)
1332 {
1333 int ret;
1334 struct lwm2m_engine_res *res = NULL;
1335
1336 ret = lwm2m_get_resource(path, &res);
1337 if (ret < 0) {
1338 return ret;
1339 }
1340
1341 res->execute_cb = cb;
1342 return 0;
1343 }
1344
lwm2m_register_create_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)1345 int lwm2m_register_create_callback(uint16_t obj_id, lwm2m_engine_user_cb_t cb)
1346 {
1347 struct lwm2m_engine_obj *obj = NULL;
1348
1349 obj = get_engine_obj(obj_id);
1350 if (!obj) {
1351 LOG_ERR("unable to find obj: %u", obj_id);
1352 return -ENOENT;
1353 }
1354
1355 obj->user_create_cb = cb;
1356 return 0;
1357 }
1358
lwm2m_register_delete_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)1359 int lwm2m_register_delete_callback(uint16_t obj_id, lwm2m_engine_user_cb_t cb)
1360 {
1361 struct lwm2m_engine_obj *obj = NULL;
1362
1363 obj = get_engine_obj(obj_id);
1364 if (!obj) {
1365 LOG_ERR("unable to find obj: %u", obj_id);
1366 return -ENOENT;
1367 }
1368
1369 obj->user_delete_cb = cb;
1370 return 0;
1371 }
1372 /* Generic data handlers */
1373
lwm2m_get_or_create_engine_obj(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst ** obj_inst,uint8_t * created)1374 int lwm2m_get_or_create_engine_obj(struct lwm2m_message *msg,
1375 struct lwm2m_engine_obj_inst **obj_inst, uint8_t *created)
1376 {
1377 int ret = 0;
1378
1379 if (created) {
1380 *created = 0U;
1381 }
1382
1383 *obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
1384 if (!*obj_inst) {
1385 ret = lwm2m_create_obj_inst(msg->path.obj_id, msg->path.obj_inst_id, obj_inst);
1386 if (ret < 0) {
1387 return ret;
1388 }
1389
1390 /* set created flag to one */
1391 if (created) {
1392 *created = 1U;
1393 }
1394
1395 if (!msg->ctx->bootstrap_mode) {
1396 engine_trigger_update(true);
1397 }
1398 }
1399
1400 return ret;
1401 }
1402
lwm2m_engine_get_res(const struct lwm2m_obj_path * path)1403 struct lwm2m_engine_res *lwm2m_engine_get_res(const struct lwm2m_obj_path *path)
1404 {
1405 struct lwm2m_engine_res *res = NULL;
1406 int ret;
1407
1408 if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
1409 return NULL;
1410 }
1411
1412 ret = path_to_objs(path, NULL, NULL, &res, NULL);
1413 if (ret < 0) {
1414 return NULL;
1415 }
1416
1417 return res;
1418 }
1419
lwm2m_engine_get_res_inst(const struct lwm2m_obj_path * path)1420 struct lwm2m_engine_res_inst *lwm2m_engine_get_res_inst(const struct lwm2m_obj_path *path)
1421 {
1422 struct lwm2m_engine_res_inst *res_inst = NULL;
1423 int ret;
1424
1425 if (path->level != LWM2M_PATH_LEVEL_RESOURCE_INST) {
1426 return NULL;
1427 }
1428
1429 ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
1430 if (ret < 0) {
1431 return NULL;
1432 }
1433
1434 return res_inst;
1435 }
1436
lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj * obj)1437 bool lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj *obj)
1438 {
1439 if (IS_ENABLED(CONFIG_LWM2M_ENGINE_ALWAYS_REPORT_OBJ_VERSION)) {
1440 return true;
1441 }
1442
1443 /* For non-core objects, report version other than 1.0 */
1444 if (!obj->is_core) {
1445 return obj->version_major != 1 || obj->version_minor != 0;
1446 }
1447
1448 /* For core objects, report version based on default version array. */
1449 for (size_t i = 0; i < ARRAY_SIZE(default_obj_versions); i++) {
1450 if (obj->obj_id != default_obj_versions[i].obj_id) {
1451 continue;
1452 }
1453
1454 return obj->version_major != default_obj_versions[i].version_major ||
1455 obj->version_minor != default_obj_versions[i].version_minor;
1456 }
1457
1458 return true;
1459 }
1460
1461 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1462 static sys_slist_t lwm2m_timed_cache_list;
1463 static struct lwm2m_time_series_resource lwm2m_cache_entries[CONFIG_LWM2M_MAX_CACHED_RESOURCES];
1464
1465 static struct lwm2m_time_series_resource *
lwm2m_cache_entry_allocate(const struct lwm2m_obj_path * path)1466 lwm2m_cache_entry_allocate(const struct lwm2m_obj_path *path)
1467 {
1468 int i;
1469 struct lwm2m_time_series_resource *entry;
1470
1471 entry = lwm2m_cache_entry_get_by_object(path);
1472 if (entry) {
1473 return entry;
1474 }
1475
1476 for (i = 0; i < ARRAY_SIZE(lwm2m_cache_entries); i++) {
1477 if (lwm2m_cache_entries[i].path.level == 0) {
1478 lwm2m_cache_entries[i].path = *path;
1479 sys_slist_append(&lwm2m_timed_cache_list, &lwm2m_cache_entries[i].node);
1480 return &lwm2m_cache_entries[i];
1481 }
1482 }
1483
1484 return NULL;
1485 }
1486
lwm2m_engine_cache_write(const struct lwm2m_engine_obj_field * obj_field,const struct lwm2m_obj_path * path,const void * value,uint16_t len)1487 static void lwm2m_engine_cache_write(const struct lwm2m_engine_obj_field *obj_field,
1488 const struct lwm2m_obj_path *path, const void *value,
1489 uint16_t len)
1490 {
1491 struct lwm2m_time_series_resource *cache_entry;
1492 struct lwm2m_time_series_elem elements;
1493
1494 cache_entry = lwm2m_cache_entry_get_by_object(path);
1495 if (!cache_entry) {
1496 return;
1497 }
1498
1499 elements.t = time(NULL);
1500
1501 if (elements.t <= 0) {
1502 LOG_WRN("Time() not available");
1503 return;
1504 }
1505
1506 switch (obj_field->data_type) {
1507 case LWM2M_RES_TYPE_U32:
1508 elements.u32 = *(uint32_t *)value;
1509 break;
1510
1511 case LWM2M_RES_TYPE_U16:
1512 elements.u16 = *(uint16_t *)value;
1513 break;
1514
1515 case LWM2M_RES_TYPE_U8:
1516 elements.u8 = *(uint8_t *)value;
1517 break;
1518
1519 case LWM2M_RES_TYPE_S64:
1520 elements.i64 = *(int64_t *)value;
1521 break;
1522
1523 case LWM2M_RES_TYPE_TIME:
1524 if (len == sizeof(time_t)) {
1525 elements.time = *(time_t *)value;
1526 } else if (len == sizeof(uint32_t)) {
1527 elements.time = (time_t) *((uint32_t *)value);
1528 } else {
1529 LOG_ERR("Not supporting size %d bytes for time", len);
1530 return;
1531 }
1532 break;
1533
1534 case LWM2M_RES_TYPE_S32:
1535 elements.i32 = *(int32_t *)value;
1536 break;
1537
1538 case LWM2M_RES_TYPE_S16:
1539 elements.i16 = *(int16_t *)value;
1540 break;
1541
1542 case LWM2M_RES_TYPE_S8:
1543 elements.i8 = *(int8_t *)value;
1544 break;
1545
1546 case LWM2M_RES_TYPE_BOOL:
1547 elements.b = *(bool *)value;
1548 break;
1549
1550 default:
1551 elements.f = *(double *)value;
1552 break;
1553 }
1554
1555 if (!lwm2m_cache_write(cache_entry, &elements)) {
1556 LOG_WRN("Data cache full");
1557 }
1558 }
1559 #endif /* CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT */
1560
1561 struct lwm2m_time_series_resource *
lwm2m_cache_entry_get_by_object(const struct lwm2m_obj_path * obj_path)1562 lwm2m_cache_entry_get_by_object(const struct lwm2m_obj_path *obj_path)
1563 {
1564 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1565 struct lwm2m_time_series_resource *entry;
1566
1567 if (obj_path->level < LWM2M_PATH_LEVEL_RESOURCE) {
1568 LOG_ERR("Path level wrong for cache %u", obj_path->level);
1569 return NULL;
1570 }
1571
1572 if (sys_slist_is_empty(&lwm2m_timed_cache_list)) {
1573 return NULL;
1574 }
1575
1576 SYS_SLIST_FOR_EACH_CONTAINER(&lwm2m_timed_cache_list, entry, node) {
1577 if (lwm2m_obj_path_equal(&entry->path, obj_path)) {
1578 return entry;
1579 }
1580 }
1581 #endif /* CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT */
1582 return NULL;
1583
1584 }
1585
lwm2m_enable_cache(const struct lwm2m_obj_path * path,struct lwm2m_time_series_elem * data_cache,size_t cache_len)1586 int lwm2m_enable_cache(const struct lwm2m_obj_path *path, struct lwm2m_time_series_elem *data_cache,
1587 size_t cache_len)
1588 {
1589 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1590 struct lwm2m_engine_obj_inst *obj_inst;
1591 struct lwm2m_engine_obj_field *obj_field;
1592 struct lwm2m_engine_res_inst *res_inst = NULL;
1593 struct lwm2m_time_series_resource *cache_entry;
1594 int ret = 0;
1595 size_t cache_entry_size = sizeof(struct lwm2m_time_series_elem);
1596
1597 /* look up resource obj */
1598 ret = path_to_objs(path, &obj_inst, &obj_field, NULL, &res_inst);
1599 if (ret < 0) {
1600 return ret;
1601 }
1602
1603 if (!res_inst) {
1604 LOG_ERR("res instance %d not found", path->res_inst_id);
1605 return -ENOENT;
1606 }
1607
1608 switch (obj_field->data_type) {
1609 case LWM2M_RES_TYPE_U32:
1610 case LWM2M_RES_TYPE_TIME:
1611 case LWM2M_RES_TYPE_U16:
1612 case LWM2M_RES_TYPE_U8:
1613 case LWM2M_RES_TYPE_S64:
1614 case LWM2M_RES_TYPE_S32:
1615 case LWM2M_RES_TYPE_S16:
1616 case LWM2M_RES_TYPE_S8:
1617 case LWM2M_RES_TYPE_BOOL:
1618 case LWM2M_RES_TYPE_FLOAT:
1619 /* Support only fixed width resource types */
1620 cache_entry = lwm2m_cache_entry_allocate(path);
1621 break;
1622 default:
1623 cache_entry = NULL;
1624 break;
1625 }
1626
1627 if (!cache_entry) {
1628 return -ENODATA;
1629 }
1630
1631 ring_buf_init(&cache_entry->rb, cache_entry_size * cache_len, (uint8_t *)data_cache);
1632
1633 return 0;
1634 #else
1635 LOG_ERR("LwM2M resource cache is only supported for "
1636 "CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT");
1637 return -ENOTSUP;
1638 #endif /* CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT */
1639 }
1640
1641 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
lwm2m_engine_data_cache_init(void)1642 static int lwm2m_engine_data_cache_init(void)
1643 {
1644 int i;
1645
1646 sys_slist_init(&lwm2m_timed_cache_list);
1647
1648 for (i = 0; i < ARRAY_SIZE(lwm2m_cache_entries); i++) {
1649 lwm2m_cache_entries[i].path.level = LWM2M_PATH_LEVEL_NONE;
1650 }
1651 return 0;
1652 }
1653 LWM2M_ENGINE_INIT(lwm2m_engine_data_cache_init);
1654 #endif
1655
lwm2m_cache_write(struct lwm2m_time_series_resource * cache_entry,struct lwm2m_time_series_elem * buf)1656 bool lwm2m_cache_write(struct lwm2m_time_series_resource *cache_entry,
1657 struct lwm2m_time_series_elem *buf)
1658 {
1659 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1660 uint32_t length;
1661 uint8_t *buf_ptr;
1662 uint32_t element_size = sizeof(struct lwm2m_time_series_elem);
1663
1664 if (ring_buf_space_get(&cache_entry->rb) < element_size) {
1665 /* No space */
1666 if (IS_ENABLED(CONFIG_LWM2M_CACHE_DROP_LATEST)) {
1667 return false;
1668 }
1669 /* Free entry */
1670 length = ring_buf_get_claim(&cache_entry->rb, &buf_ptr, element_size);
1671 ring_buf_get_finish(&cache_entry->rb, length);
1672 }
1673
1674 length = ring_buf_put_claim(&cache_entry->rb, &buf_ptr, element_size);
1675
1676 if (length != element_size) {
1677 ring_buf_put_finish(&cache_entry->rb, 0);
1678 LOG_ERR("Allocation failed %u", length);
1679 return false;
1680 }
1681
1682 ring_buf_put_finish(&cache_entry->rb, length);
1683 /* Store data */
1684 memcpy(buf_ptr, buf, element_size);
1685 return true;
1686 #else
1687 return NULL;
1688 #endif
1689 }
1690
lwm2m_cache_read(struct lwm2m_time_series_resource * cache_entry,struct lwm2m_time_series_elem * buf)1691 bool lwm2m_cache_read(struct lwm2m_time_series_resource *cache_entry,
1692 struct lwm2m_time_series_elem *buf)
1693 {
1694 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1695 uint32_t length;
1696 uint8_t *buf_ptr;
1697 uint32_t element_size = sizeof(struct lwm2m_time_series_elem);
1698
1699 if (ring_buf_is_empty(&cache_entry->rb)) {
1700 return false;
1701 }
1702
1703 length = ring_buf_get_claim(&cache_entry->rb, &buf_ptr, element_size);
1704
1705 if (length != element_size) {
1706 LOG_ERR("Cache read fail %u", length);
1707 ring_buf_get_finish(&cache_entry->rb, 0);
1708 return false;
1709 }
1710
1711 /* Read Data */
1712 memcpy(buf, buf_ptr, element_size);
1713 ring_buf_get_finish(&cache_entry->rb, length);
1714 return true;
1715
1716 #else
1717 return NULL;
1718 #endif
1719 }
1720
lwm2m_cache_size(const struct lwm2m_time_series_resource * cache_entry)1721 size_t lwm2m_cache_size(const struct lwm2m_time_series_resource *cache_entry)
1722 {
1723 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1724 uint32_t bytes_available;
1725
1726 if (ring_buf_is_empty(&cache_entry->rb)) {
1727 return 0;
1728 }
1729
1730 bytes_available = ring_buf_size_get(&cache_entry->rb);
1731
1732 return (bytes_available / sizeof(struct lwm2m_time_series_elem));
1733 #else
1734 return 0;
1735 #endif
1736 }
1737
lwm2m_set_bulk(const struct lwm2m_res_item res_list[],size_t res_list_size)1738 int lwm2m_set_bulk(const struct lwm2m_res_item res_list[], size_t res_list_size)
1739 {
1740 int ret;
1741
1742 k_mutex_lock(®istry_lock, K_FOREVER);
1743 for (int i = 0; i < res_list_size; i++) {
1744
1745 ret = lwm2m_engine_set(res_list[i].path, res_list[i].value, res_list[i].size);
1746
1747 if (ret) {
1748 k_mutex_unlock(®istry_lock);
1749 return ret;
1750 }
1751 }
1752 k_mutex_unlock(®istry_lock);
1753
1754 return 0;
1755 }
1756