Dawn Framework 1.0
Universal data acquisition framework for embedded systems
objects.cxx
1// dawn/src/proto/wakaama/objects.cxx
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5
6#include "internal.hxx"
7
8#include <cstring>
9#include <limits>
10
11using namespace dawn;
12using namespace dawn::wakaama_internal;
13
14namespace
15{
16constexpr uint32_t SERVER_DISABLE_TIMEOUT_DEFAULT = 86400;
17constexpr uint32_t SERVER_COMMUNICATION_RETRY_TIMER_DEFAULT = 60;
18constexpr uint32_t SERVER_COMMUNICATION_SEQUENCE_DELAY_TIMER_DEFAULT = 86400;
19constexpr uint8_t SERVER_COMMUNICATION_RETRY_COUNT_DEFAULT = 5;
20constexpr uint8_t SERVER_COMMUNICATION_SEQUENCE_RETRY_COUNT_DEFAULT = 1;
21
22constexpr uint16_t SECURITY_RESOURCE_IDS[] = {LWM2M_SECURITY_URI_ID,
23 LWM2M_SECURITY_BOOTSTRAP_ID,
24 LWM2M_SECURITY_SECURITY_ID,
25 LWM2M_SECURITY_PUBLIC_KEY_ID,
26 LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID,
27 LWM2M_SECURITY_SECRET_KEY_ID,
28 LWM2M_SECURITY_SHORT_SERVER_ID,
29 LWM2M_SECURITY_HOLD_OFF_ID,
30 LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID};
31
32constexpr uint16_t SERVER_READ_RESOURCE_IDS[] = {LWM2M_SERVER_SHORT_ID_ID,
33 LWM2M_SERVER_LIFETIME_ID,
34 LWM2M_SERVER_MIN_PERIOD_ID,
35 LWM2M_SERVER_MAX_PERIOD_ID,
36 LWM2M_SERVER_TIMEOUT_ID,
37 LWM2M_SERVER_STORING_ID,
38 LWM2M_SERVER_BINDING_ID,
39 LWM2M_SERVER_REG_ORDER_ID,
40 LWM2M_SERVER_INITIAL_REG_DELAY_ID,
41 LWM2M_SERVER_REG_FAIL_BLOCK_ID,
42 LWM2M_SERVER_REG_FAIL_BOOTSTRAP_ID,
43 LWM2M_SERVER_COMM_RETRY_COUNT_ID,
44 LWM2M_SERVER_COMM_RETRY_TIMER_ID,
45 LWM2M_SERVER_SEQ_DELAY_TIMER_ID,
46 LWM2M_SERVER_SEQ_RETRY_COUNT_ID,
47 LWM2M_SERVER_PREFERRED_TRANSPORT_ID,
48 LWM2M_SERVER_MUTE_SEND_ID};
49
50constexpr uint16_t SERVER_DISCOVER_RESOURCE_IDS[] = {LWM2M_SERVER_SHORT_ID_ID,
51 LWM2M_SERVER_LIFETIME_ID,
52 LWM2M_SERVER_MIN_PERIOD_ID,
53 LWM2M_SERVER_MAX_PERIOD_ID,
54 LWM2M_SERVER_DISABLE_ID,
55 LWM2M_SERVER_TIMEOUT_ID,
56 LWM2M_SERVER_STORING_ID,
57 LWM2M_SERVER_BINDING_ID,
58 LWM2M_SERVER_UPDATE_ID,
59 LWM2M_SERVER_REG_ORDER_ID,
60 LWM2M_SERVER_INITIAL_REG_DELAY_ID,
61 LWM2M_SERVER_REG_FAIL_BLOCK_ID,
62 LWM2M_SERVER_REG_FAIL_BOOTSTRAP_ID,
63 LWM2M_SERVER_COMM_RETRY_COUNT_ID,
64 LWM2M_SERVER_COMM_RETRY_TIMER_ID,
65 LWM2M_SERVER_SEQ_DELAY_TIMER_ID,
66 LWM2M_SERVER_SEQ_RETRY_COUNT_ID,
67 LWM2M_SERVER_PREFERRED_TRANSPORT_ID,
68 LWM2M_SERVER_MUTE_SEND_ID};
69
70constexpr uint16_t DEVICE_RESOURCE_IDS[] = {
71 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_MANUFACTURER,
72 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_MODEL_NUMBER,
73 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_SERIAL_NUMBER,
74 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_FIRMWARE_VERSION,
75 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_ERROR_CODE,
76 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_CURRENT_TIME,
77 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BINDING_MODES,
78};
79
80bool encodeString(const char *str, lwm2m_data_t *data)
81{
82 lwm2m_data_encode_string(str == nullptr ? "" : str, data);
83 return data->type == LWM2M_TYPE_STRING;
84}
85
86bool encodeOpaque(const uint8_t *buffer, size_t len, lwm2m_data_t *data)
87{
88 lwm2m_data_encode_opaque(buffer, len, data);
89 return data->type == LWM2M_TYPE_OPAQUE;
90}
91
92#ifdef CONFIG_WAKAAMA_BOOTSTRAP
93bool bufferValue(const lwm2m_data_t &data, const uint8_t **buffer, size_t *length)
94{
95 if (buffer == nullptr || length == nullptr)
96 {
97 return false;
98 }
99
100 switch (data.type)
101 {
102 case LWM2M_TYPE_STRING:
103 case LWM2M_TYPE_OPAQUE:
104 case LWM2M_TYPE_CORE_LINK:
105 break;
106
107 default:
108 return false;
109 }
110
111 *buffer = data.value.asBuffer.buffer;
112 *length = data.value.asBuffer.length;
113 return *length == 0 || *buffer != nullptr;
114}
115#endif
116
117bool allocateResourceList(const uint16_t *ids, int count, int *numData, lwm2m_data_t **dataArray)
118{
119 *dataArray = lwm2m_data_new(count);
120 if (*dataArray == nullptr)
121 {
122 return false;
123 }
124
125 *numData = count;
126 for (int i = 0; i < count; i++)
127 {
128 (*dataArray)[i].id = ids[i];
129 }
130
131 return true;
132}
133
134template<typename T>
135bool decodeUint(lwm2m_data_t *data, T *value)
136{
137 int64_t intValue;
138
139 if (lwm2m_data_decode_int(data, &intValue) != 1 || intValue < 0 ||
140 static_cast<uint64_t>(intValue) > static_cast<uint64_t>(std::numeric_limits<T>::max()))
141 {
142 return false;
143 }
144
145 *value = static_cast<T>(intValue);
146 return true;
147}
148
149bool assignShortString(char *dest, size_t capacity, const lwm2m_data_t &data)
150{
151 if ((data.type != LWM2M_TYPE_STRING && data.type != LWM2M_TYPE_OPAQUE) ||
152 data.value.asBuffer.length == 0 || data.value.asBuffer.length >= capacity ||
153 data.value.asBuffer.buffer == nullptr)
154 {
155 return false;
156 }
157
158 std::memcpy(dest, data.value.asBuffer.buffer, data.value.asBuffer.length);
159 dest[data.value.asBuffer.length] = '\0';
160 return true;
161}
162
163void initServerDefaults(server_instance_s &inst)
164{
165 inst.binding[0] = 'U';
166 inst.binding[1] = '\0';
167 inst.preferredTransport[0] = 'U';
168 inst.preferredTransport[1] = '\0';
169 inst.disableTimeout = SERVER_DISABLE_TIMEOUT_DEFAULT;
170 inst.bootstrapOnRegistrationFailure = true;
171 inst.communicationRetryCount = SERVER_COMMUNICATION_RETRY_COUNT_DEFAULT;
172 inst.communicationRetryTimer = SERVER_COMMUNICATION_RETRY_TIMER_DEFAULT;
173 inst.communicationSequenceDelayTimer = SERVER_COMMUNICATION_SEQUENCE_DELAY_TIMER_DEFAULT;
174 inst.communicationSequenceRetryCount = SERVER_COMMUNICATION_SEQUENCE_RETRY_COUNT_DEFAULT;
175}
176} // namespace
177
178security_instance_s *dawn::wakaama_internal::allocateSecurityInstance(InstancePools *pools)
179{
180 if (pools == nullptr)
181 {
182 return nullptr;
183 }
184
185 for (size_t i = 0; i < pools->securityCapacity; i++)
186 {
187 security_instance_s *inst = &pools->security[i];
188
189 if (!inst->allocated)
190 {
191 std::memset(inst, 0, sizeof(*inst));
192 inst->allocated = true;
193 inst->uri = inst->uriBuffer;
194 return inst;
195 }
196 }
197
198 return nullptr;
199}
200
201void dawn::wakaama_internal::releaseSecurityInstance(security_instance_s *inst)
202{
203 if (inst != nullptr)
204 {
205 std::memset(inst, 0, sizeof(*inst));
206 }
207}
208
209server_instance_s *dawn::wakaama_internal::allocateServerInstance(InstancePools *pools)
210{
211 if (pools == nullptr)
212 {
213 return nullptr;
214 }
215
216 for (size_t i = 0; i < pools->serverCapacity; i++)
217 {
218 server_instance_s *inst = &pools->server[i];
219
220 if (!inst->allocated)
221 {
222 std::memset(inst, 0, sizeof(*inst));
223 inst->allocated = true;
224 initServerDefaults(*inst);
225 return inst;
226 }
227 }
228
229 return nullptr;
230}
231
232void dawn::wakaama_internal::releaseServerInstance(server_instance_s *inst)
233{
234 if (inst != nullptr)
235 {
236 std::memset(inst, 0, sizeof(*inst));
237 }
238}
239
240bool dawn::wakaama_internal::assignSecurityString(security_instance_s &inst,
241 const uint8_t *src,
242 size_t len)
243{
244 if (len > 0 && src == nullptr)
245 {
246 return false;
247 }
248
249 while (len > 0 && src[len - 1] == '\0')
250 {
251 len--;
252 }
253
254 if (len >= sizeof(inst.uriBuffer))
255 {
256 return false;
257 }
258
259 if (len > 0)
260 {
261 std::memcpy(inst.uriBuffer, src, len);
262 }
263
264 inst.uriBuffer[len] = '\0';
265 inst.uri = inst.uriBuffer;
266 return true;
267}
268
269bool dawn::wakaama_internal::assignSecurityBuffer(const uint8_t *src,
270 size_t len,
271 uint8_t *buffer,
272 size_t capacity,
273 const uint8_t **dst,
274 size_t *dstLen)
275{
276 if (len > capacity)
277 {
278 return false;
279 }
280
281 if (len > 0 && src == nullptr)
282 {
283 return false;
284 }
285
286 if (len > 0)
287 {
288 std::memcpy(buffer, src, len);
289 *dst = buffer;
290 }
291 else
292 {
293 *dst = nullptr;
294 }
295
296 *dstLen = len;
297 return true;
298}
299
300uint8_t dawn::wakaama_internal::securityRead(lwm2m_context_t *ctx,
301 uint16_t instanceId,
302 int *numData,
303 lwm2m_data_t **dataArray,
304 lwm2m_object_t *object)
305{
307
308 UNUSED(ctx);
309
310 inst = reinterpret_cast<security_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
311 if (inst == nullptr)
312 {
313 return COAP_404_NOT_FOUND;
314 }
315
316 if (*numData == 0)
317 {
318 if (!allocateResourceList(
319 SECURITY_RESOURCE_IDS,
320 static_cast<int>(sizeof(SECURITY_RESOURCE_IDS) / sizeof(SECURITY_RESOURCE_IDS[0])),
321 numData,
322 dataArray))
323 {
324 return COAP_500_INTERNAL_SERVER_ERROR;
325 }
326 }
327
328 for (int i = 0; i < *numData; i++)
329 {
330 switch ((*dataArray)[i].id)
331 {
332 case LWM2M_SECURITY_URI_ID:
333 if (!encodeString(inst->uri, &(*dataArray)[i]))
334 {
335 return COAP_500_INTERNAL_SERVER_ERROR;
336 }
337 break;
338
339 case LWM2M_SECURITY_BOOTSTRAP_ID:
340 lwm2m_data_encode_bool(inst->bootstrap, &(*dataArray)[i]);
341 break;
342
343 case LWM2M_SECURITY_SECURITY_ID:
344 lwm2m_data_encode_int(inst->securityMode, &(*dataArray)[i]);
345 break;
346
347 case LWM2M_SECURITY_PUBLIC_KEY_ID:
348 if (!encodeOpaque(inst->publicIdentity, inst->publicIdentityLen, &(*dataArray)[i]))
349 {
350 return COAP_500_INTERNAL_SERVER_ERROR;
351 }
352 break;
353
354 case LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID:
355 if (!encodeOpaque(nullptr, 0, &(*dataArray)[i]))
356 {
357 return COAP_500_INTERNAL_SERVER_ERROR;
358 }
359 break;
360
361 case LWM2M_SECURITY_SECRET_KEY_ID:
362 if (!encodeOpaque(inst->secretKey, inst->secretKeyLen, &(*dataArray)[i]))
363 {
364 return COAP_500_INTERNAL_SERVER_ERROR;
365 }
366 break;
367
368 case LWM2M_SECURITY_SHORT_SERVER_ID:
369 lwm2m_data_encode_int(inst->shortServerId, &(*dataArray)[i]);
370 break;
371
372 case LWM2M_SECURITY_HOLD_OFF_ID:
373 lwm2m_data_encode_int(inst->holdoff, &(*dataArray)[i]);
374 break;
375
376 case LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID:
377 lwm2m_data_encode_int(inst->bootstrapTimeout, &(*dataArray)[i]);
378 break;
379
380 default:
381 return COAP_404_NOT_FOUND;
382 }
383 }
384
385 return COAP_205_CONTENT;
386}
387
388uint8_t dawn::wakaama_internal::securityDiscover(lwm2m_context_t *ctx,
389 uint16_t instanceId,
390 int *numData,
391 lwm2m_data_t **dataArray,
392 lwm2m_object_t *object)
393{
395
396 UNUSED(ctx);
397
398 inst = reinterpret_cast<security_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
399 if (inst == nullptr)
400 {
401 return COAP_404_NOT_FOUND;
402 }
403
404 if (*numData == 0)
405 {
406 if (!allocateResourceList(
407 SECURITY_RESOURCE_IDS,
408 static_cast<int>(sizeof(SECURITY_RESOURCE_IDS) / sizeof(SECURITY_RESOURCE_IDS[0])),
409 numData,
410 dataArray))
411 {
412 return COAP_500_INTERNAL_SERVER_ERROR;
413 }
414 }
415
416 return COAP_205_CONTENT;
417}
418
419#ifdef CONFIG_WAKAAMA_BOOTSTRAP
420uint8_t dawn::wakaama_internal::securityWrite(lwm2m_context_t *ctx,
421 uint16_t instanceId,
422 int numData,
423 lwm2m_data_t *dataArray,
424 lwm2m_object_t *object,
425 lwm2m_write_type_t writeType)
426{
428
429 UNUSED(ctx);
430 UNUSED(writeType);
431
432 inst = reinterpret_cast<security_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
433 if (inst == nullptr)
434 {
435 return COAP_404_NOT_FOUND;
436 }
437
438 for (int i = 0; i < numData; i++)
439 {
440 int64_t intValue;
441 bool boolValue;
442
443 if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE)
444 {
445 return COAP_404_NOT_FOUND;
446 }
447
448 switch (dataArray[i].id)
449 {
450 case LWM2M_SECURITY_URI_ID:
451 {
452 const uint8_t *buffer;
453 size_t length;
454
455 if (!bufferValue(dataArray[i], &buffer, &length))
456 {
457 return COAP_400_BAD_REQUEST;
458 }
459
460 if (!assignSecurityString(*inst, buffer, length))
461 {
462 return COAP_500_INTERNAL_SERVER_ERROR;
463 }
464 }
465 break;
466
467 case LWM2M_SECURITY_BOOTSTRAP_ID:
468 if (lwm2m_data_decode_bool(dataArray + i, &boolValue) != 1)
469 {
470 return COAP_400_BAD_REQUEST;
471 }
472
473 inst->bootstrap = boolValue;
474 break;
475
476 case LWM2M_SECURITY_SECURITY_ID:
477 if (lwm2m_data_decode_int(dataArray + i, &intValue) != 1 || intValue < 0 ||
478 intValue > 3)
479 {
480 return COAP_400_BAD_REQUEST;
481 }
482
483 inst->securityMode = static_cast<uint8_t>(intValue);
484 break;
485
486 case LWM2M_SECURITY_PUBLIC_KEY_ID:
487 {
488 const uint8_t *buffer;
489 size_t length;
490
491 if (!bufferValue(dataArray[i], &buffer, &length))
492 {
493 return COAP_400_BAD_REQUEST;
494 }
495
496 if (!assignSecurityBuffer(buffer,
497 length,
498 inst->publicIdentityBuffer,
499 sizeof(inst->publicIdentityBuffer),
500 &inst->publicIdentity,
501 &inst->publicIdentityLen))
502 {
503 return COAP_500_INTERNAL_SERVER_ERROR;
504 }
505 }
506 break;
507
508 case LWM2M_SECURITY_SECRET_KEY_ID:
509 {
510 const uint8_t *buffer;
511 size_t length;
512
513 if (!bufferValue(dataArray[i], &buffer, &length))
514 {
515 return COAP_400_BAD_REQUEST;
516 }
517
518 if (!assignSecurityBuffer(buffer,
519 length,
520 inst->secretKeyBuffer,
521 sizeof(inst->secretKeyBuffer),
522 &inst->secretKey,
523 &inst->secretKeyLen))
524 {
525 return COAP_500_INTERNAL_SERVER_ERROR;
526 }
527 }
528 break;
529
530 case LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID:
531 break;
532
533 case LWM2M_SECURITY_SHORT_SERVER_ID:
534 if (lwm2m_data_decode_int(dataArray + i, &intValue) != 1 || intValue < 0 ||
535 intValue > UINT16_MAX)
536 {
537 return COAP_400_BAD_REQUEST;
538 }
539
540 inst->shortServerId = static_cast<uint16_t>(intValue);
541 break;
542
543 case LWM2M_SECURITY_HOLD_OFF_ID:
544 if (lwm2m_data_decode_int(dataArray + i, &intValue) != 1 || intValue < 0 ||
545 intValue > UINT32_MAX)
546 {
547 return COAP_400_BAD_REQUEST;
548 }
549
550 inst->holdoff = static_cast<uint32_t>(intValue);
551 break;
552
553 case LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID:
554 if (lwm2m_data_decode_int(dataArray + i, &intValue) != 1 || intValue < 0 ||
555 intValue > UINT32_MAX)
556 {
557 return COAP_400_BAD_REQUEST;
558 }
559
560 inst->bootstrapTimeout = static_cast<uint32_t>(intValue);
561 break;
562
563 default:
564 return COAP_400_BAD_REQUEST;
565 }
566 }
567
568 return COAP_204_CHANGED;
569}
570
571uint8_t dawn::wakaama_internal::securityCreate(lwm2m_context_t *ctx,
572 uint16_t instanceId,
573 int numData,
574 lwm2m_data_t *dataArray,
575 lwm2m_object_t *object)
576{
577 InstancePools *pools = static_cast<InstancePools *>(object->userData);
579 uint8_t result;
580
581 if (lwm2m_list_find(object->instanceList, instanceId) != nullptr)
582 {
583 return COAP_400_BAD_REQUEST;
584 }
585
586 inst = allocateSecurityInstance(pools);
587 if (inst == nullptr)
588 {
589 return COAP_500_INTERNAL_SERVER_ERROR;
590 }
591
592 inst->id = instanceId;
593 inst->securityMode = LWM2M_SECURITY_MODE_NONE;
594 inst->holdoff = 10;
595 object->instanceList = LWM2M_LIST_ADD(object->instanceList, inst);
596
597 result =
598 securityWrite(ctx, instanceId, numData, dataArray, object, LWM2M_WRITE_REPLACE_RESOURCES);
599 if (result != COAP_204_CHANGED)
600 {
601 lwm2m_list_t *removed;
602
603 object->instanceList = lwm2m_list_remove(object->instanceList, instanceId, &removed);
604 UNUSED(removed);
605 releaseSecurityInstance(inst);
606 return result;
607 }
608
609 return COAP_201_CREATED;
610}
611
612uint8_t dawn::wakaama_internal::securityDelete(lwm2m_context_t *ctx,
613 uint16_t instanceId,
614 lwm2m_object_t *object)
615{
616 lwm2m_list_t *removed;
617
618 UNUSED(ctx);
619
620 object->instanceList = lwm2m_list_remove(object->instanceList, instanceId, &removed);
621 if (removed == nullptr)
622 {
623 return COAP_404_NOT_FOUND;
624 }
625
626 releaseSecurityInstance(reinterpret_cast<security_instance_s *>(removed));
627 return COAP_202_DELETED;
628}
629#endif
630
631uint8_t dawn::wakaama_internal::serverRead(lwm2m_context_t *ctx,
632 uint16_t instanceId,
633 int *numData,
634 lwm2m_data_t **dataArray,
635 lwm2m_object_t *object)
636{
637 server_instance_s *inst;
638
639 UNUSED(ctx);
640
641 inst = reinterpret_cast<server_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
642 if (inst == nullptr)
643 {
644 return COAP_404_NOT_FOUND;
645 }
646
647 if (*numData == 0)
648 {
649 if (!allocateResourceList(SERVER_READ_RESOURCE_IDS,
650 static_cast<int>(sizeof(SERVER_READ_RESOURCE_IDS) /
651 sizeof(SERVER_READ_RESOURCE_IDS[0])),
652 numData,
653 dataArray))
654 {
655 return COAP_500_INTERNAL_SERVER_ERROR;
656 }
657 }
658
659 for (int i = 0; i < *numData; i++)
660 {
661 switch ((*dataArray)[i].id)
662 {
663 case LWM2M_SERVER_SHORT_ID_ID:
664 lwm2m_data_encode_int(inst->shortServerId, &(*dataArray)[i]);
665 break;
666
667 case LWM2M_SERVER_LIFETIME_ID:
668 lwm2m_data_encode_int(inst->lifetime, &(*dataArray)[i]);
669 break;
670
671 case LWM2M_SERVER_MIN_PERIOD_ID:
672 lwm2m_data_encode_int(inst->defaultMinPeriod, &(*dataArray)[i]);
673 break;
674
675 case LWM2M_SERVER_MAX_PERIOD_ID:
676 lwm2m_data_encode_int(inst->defaultMaxPeriod, &(*dataArray)[i]);
677 break;
678
679 case LWM2M_SERVER_DISABLE_ID:
680 case LWM2M_SERVER_UPDATE_ID:
681 case LWM2M_SERVER_TRIGGER_ID:
682 return COAP_405_METHOD_NOT_ALLOWED;
683
684 case LWM2M_SERVER_TIMEOUT_ID:
685 lwm2m_data_encode_int(inst->disableTimeout, &(*dataArray)[i]);
686 break;
687
688 case LWM2M_SERVER_STORING_ID:
689 lwm2m_data_encode_bool(inst->storing, &(*dataArray)[i]);
690 break;
691
692 case LWM2M_SERVER_BINDING_ID:
693 if (!encodeString(inst->binding, &(*dataArray)[i]))
694 {
695 return COAP_500_INTERNAL_SERVER_ERROR;
696 }
697 break;
698
699 case LWM2M_SERVER_REG_ORDER_ID:
700 lwm2m_data_encode_int(inst->registrationOrder, &(*dataArray)[i]);
701 break;
702
703 case LWM2M_SERVER_INITIAL_REG_DELAY_ID:
704 lwm2m_data_encode_int(inst->initialRegistrationDelay, &(*dataArray)[i]);
705 break;
706
707 case LWM2M_SERVER_REG_FAIL_BLOCK_ID:
708 lwm2m_data_encode_bool(inst->registrationFailureBlock, &(*dataArray)[i]);
709 break;
710
711 case LWM2M_SERVER_REG_FAIL_BOOTSTRAP_ID:
712 lwm2m_data_encode_bool(inst->bootstrapOnRegistrationFailure, &(*dataArray)[i]);
713 break;
714
715 case LWM2M_SERVER_COMM_RETRY_COUNT_ID:
716 lwm2m_data_encode_int(inst->communicationRetryCount, &(*dataArray)[i]);
717 break;
718
719 case LWM2M_SERVER_COMM_RETRY_TIMER_ID:
720 lwm2m_data_encode_int(inst->communicationRetryTimer, &(*dataArray)[i]);
721 break;
722
723 case LWM2M_SERVER_SEQ_DELAY_TIMER_ID:
724 lwm2m_data_encode_int(inst->communicationSequenceDelayTimer, &(*dataArray)[i]);
725 break;
726
727 case LWM2M_SERVER_SEQ_RETRY_COUNT_ID:
728 lwm2m_data_encode_int(inst->communicationSequenceRetryCount, &(*dataArray)[i]);
729 break;
730
731 case LWM2M_SERVER_PREFERRED_TRANSPORT_ID:
732 if (!encodeString(inst->preferredTransport, &(*dataArray)[i]))
733 {
734 return COAP_500_INTERNAL_SERVER_ERROR;
735 }
736 break;
737
738 case LWM2M_SERVER_MUTE_SEND_ID:
739 lwm2m_data_encode_bool(inst->muteSend, &(*dataArray)[i]);
740 break;
741
742 default:
743 return COAP_404_NOT_FOUND;
744 }
745 }
746
747 return COAP_205_CONTENT;
748}
749
750uint8_t dawn::wakaama_internal::serverDiscover(lwm2m_context_t *ctx,
751 uint16_t instanceId,
752 int *numData,
753 lwm2m_data_t **dataArray,
754 lwm2m_object_t *object)
755{
756 server_instance_s *inst;
757
758 UNUSED(ctx);
759
760 inst = reinterpret_cast<server_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
761 if (inst == nullptr)
762 {
763 return COAP_404_NOT_FOUND;
764 }
765
766 if (*numData == 0)
767 {
768 if (!allocateResourceList(SERVER_DISCOVER_RESOURCE_IDS,
769 static_cast<int>(sizeof(SERVER_DISCOVER_RESOURCE_IDS) /
770 sizeof(SERVER_DISCOVER_RESOURCE_IDS[0])),
771 numData,
772 dataArray))
773 {
774 return COAP_500_INTERNAL_SERVER_ERROR;
775 }
776 }
777
778 return COAP_205_CONTENT;
779}
780
781uint8_t dawn::wakaama_internal::serverExecute(lwm2m_context_t *ctx,
782 uint16_t instanceId,
783 uint16_t resourceId,
784 uint8_t *buffer,
785 int length,
786 lwm2m_object_t *object)
787{
788 server_instance_s *inst;
789
790 UNUSED(buffer);
791 UNUSED(length);
792
793 inst = reinterpret_cast<server_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
794 if (inst == nullptr)
795 {
796 return COAP_404_NOT_FOUND;
797 }
798
799 switch (resourceId)
800 {
801 case LWM2M_SERVER_UPDATE_ID:
802 return lwm2m_update_registration(ctx, inst->shortServerId, true) == COAP_NO_ERROR
803 ? COAP_204_CHANGED
804 : COAP_400_BAD_REQUEST;
805
806 case LWM2M_SERVER_DISABLE_ID:
807 case LWM2M_SERVER_TRIGGER_ID:
808 return COAP_405_METHOD_NOT_ALLOWED;
809
810 default:
811 return COAP_405_METHOD_NOT_ALLOWED;
812 }
813}
814
815uint8_t dawn::wakaama_internal::serverWrite(lwm2m_context_t *ctx,
816 uint16_t instanceId,
817 int numData,
818 lwm2m_data_t *dataArray,
819 lwm2m_object_t *object,
820 lwm2m_write_type_t writeType)
821{
822 server_instance_s *inst;
823
824 UNUSED(ctx);
825 UNUSED(writeType);
826
827 inst = reinterpret_cast<server_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
828 if (inst == nullptr)
829 {
830 return COAP_404_NOT_FOUND;
831 }
832
833 for (int i = 0; i < numData; i++)
834 {
835 bool boolValue;
836
837 if (dataArray[i].type == LWM2M_TYPE_MULTIPLE_RESOURCE)
838 {
839 return COAP_404_NOT_FOUND;
840 }
841
842 switch (dataArray[i].id)
843 {
844 case LWM2M_SERVER_SHORT_ID_ID:
845 if (!decodeUint(dataArray + i, &inst->shortServerId))
846 {
847 return COAP_400_BAD_REQUEST;
848 }
849 break;
850
851 case LWM2M_SERVER_LIFETIME_ID:
852 if (!decodeUint(dataArray + i, &inst->lifetime))
853 {
854 return COAP_400_BAD_REQUEST;
855 }
856 break;
857
858 case LWM2M_SERVER_MIN_PERIOD_ID:
859 if (!decodeUint(dataArray + i, &inst->defaultMinPeriod))
860 {
861 return COAP_400_BAD_REQUEST;
862 }
863 break;
864
865 case LWM2M_SERVER_MAX_PERIOD_ID:
866 if (!decodeUint(dataArray + i, &inst->defaultMaxPeriod))
867 {
868 return COAP_400_BAD_REQUEST;
869 }
870 break;
871
872 case LWM2M_SERVER_TIMEOUT_ID:
873 if (!decodeUint(dataArray + i, &inst->disableTimeout))
874 {
875 return COAP_400_BAD_REQUEST;
876 }
877 break;
878
879 case LWM2M_SERVER_STORING_ID:
880 if (lwm2m_data_decode_bool(dataArray + i, &boolValue) != 1)
881 {
882 return COAP_400_BAD_REQUEST;
883 }
884
885 inst->storing = boolValue;
886 break;
887
888 case LWM2M_SERVER_BINDING_ID:
889 if (!assignShortString(inst->binding, sizeof(inst->binding), dataArray[i]))
890 {
891 return COAP_400_BAD_REQUEST;
892 }
893 break;
894
895 case LWM2M_SERVER_UPDATE_ID:
896 case LWM2M_SERVER_DISABLE_ID:
897 case LWM2M_SERVER_TRIGGER_ID:
898 return COAP_405_METHOD_NOT_ALLOWED;
899
900 case LWM2M_SERVER_REG_ORDER_ID:
901 if (!decodeUint(dataArray + i, &inst->registrationOrder))
902 {
903 return COAP_400_BAD_REQUEST;
904 }
905 break;
906
907 case LWM2M_SERVER_INITIAL_REG_DELAY_ID:
908 if (!decodeUint(dataArray + i, &inst->initialRegistrationDelay))
909 {
910 return COAP_400_BAD_REQUEST;
911 }
912 break;
913
914 case LWM2M_SERVER_REG_FAIL_BLOCK_ID:
915 if (lwm2m_data_decode_bool(dataArray + i, &boolValue) != 1)
916 {
917 return COAP_400_BAD_REQUEST;
918 }
919
920 inst->registrationFailureBlock = boolValue;
921 break;
922
923 case LWM2M_SERVER_REG_FAIL_BOOTSTRAP_ID:
924 if (lwm2m_data_decode_bool(dataArray + i, &boolValue) != 1)
925 {
926 return COAP_400_BAD_REQUEST;
927 }
928
929 inst->bootstrapOnRegistrationFailure = boolValue;
930 break;
931
932 case LWM2M_SERVER_COMM_RETRY_COUNT_ID:
933 if (!decodeUint(dataArray + i, &inst->communicationRetryCount))
934 {
935 return COAP_400_BAD_REQUEST;
936 }
937 break;
938
939 case LWM2M_SERVER_COMM_RETRY_TIMER_ID:
940 if (!decodeUint(dataArray + i, &inst->communicationRetryTimer))
941 {
942 return COAP_400_BAD_REQUEST;
943 }
944 break;
945
946 case LWM2M_SERVER_SEQ_DELAY_TIMER_ID:
947 if (!decodeUint(dataArray + i, &inst->communicationSequenceDelayTimer))
948 {
949 return COAP_400_BAD_REQUEST;
950 }
951 break;
952
953 case LWM2M_SERVER_SEQ_RETRY_COUNT_ID:
954 if (!decodeUint(dataArray + i, &inst->communicationSequenceRetryCount))
955 {
956 return COAP_400_BAD_REQUEST;
957 }
958 break;
959
960 case LWM2M_SERVER_PREFERRED_TRANSPORT_ID:
961 if (!assignShortString(
962 inst->preferredTransport, sizeof(inst->preferredTransport), dataArray[i]))
963 {
964 return COAP_400_BAD_REQUEST;
965 }
966 break;
967
968 case LWM2M_SERVER_MUTE_SEND_ID:
969 if (lwm2m_data_decode_bool(dataArray + i, &boolValue) != 1)
970 {
971 return COAP_400_BAD_REQUEST;
972 }
973
974 inst->muteSend = boolValue;
975 break;
976
977 default:
978 return COAP_400_BAD_REQUEST;
979 }
980 }
981
982 return COAP_204_CHANGED;
983}
984
985#ifdef CONFIG_WAKAAMA_BOOTSTRAP
986uint8_t dawn::wakaama_internal::serverCreate(lwm2m_context_t *ctx,
987 uint16_t instanceId,
988 int numData,
989 lwm2m_data_t *dataArray,
990 lwm2m_object_t *object)
991{
992 InstancePools *pools = static_cast<InstancePools *>(object->userData);
993 server_instance_s *inst;
994 uint8_t result;
995
996 if (lwm2m_list_find(object->instanceList, instanceId) != nullptr)
997 {
998 return COAP_400_BAD_REQUEST;
999 }
1000
1001 inst = allocateServerInstance(pools);
1002 if (inst == nullptr)
1003 {
1004 return COAP_500_INTERNAL_SERVER_ERROR;
1005 }
1006
1007 inst->id = instanceId;
1008 inst->binding[0] = 'U';
1009 object->instanceList = LWM2M_LIST_ADD(object->instanceList, inst);
1010
1011 result = serverWrite(ctx, instanceId, numData, dataArray, object, LWM2M_WRITE_REPLACE_RESOURCES);
1012 if (result != COAP_204_CHANGED)
1013 {
1014 lwm2m_list_t *removed;
1015
1016 object->instanceList = lwm2m_list_remove(object->instanceList, instanceId, &removed);
1017 UNUSED(removed);
1018 releaseServerInstance(inst);
1019 return result;
1020 }
1021
1022 return COAP_201_CREATED;
1023}
1024
1025uint8_t dawn::wakaama_internal::serverDelete(lwm2m_context_t *ctx,
1026 uint16_t instanceId,
1027 lwm2m_object_t *object)
1028{
1029 lwm2m_list_t *removed;
1030
1031 UNUSED(ctx);
1032
1033 object->instanceList = lwm2m_list_remove(object->instanceList, instanceId, &removed);
1034 if (removed == nullptr)
1035 {
1036 return COAP_404_NOT_FOUND;
1037 }
1038
1039 releaseServerInstance(reinterpret_cast<server_instance_s *>(removed));
1040 return COAP_202_DELETED;
1041}
1042#endif
1043
1044// Battery resources (7/9/20) the Device object can additionally expose when
1045// bound to descriptor IOs via the device: config block. Kept here, next to the
1046// static DEVICE_RESOURCE_IDS, so the Device object owns its resource set.
1047
1048constexpr uint16_t DEVICE_BATTERY_RESOURCE_IDS[] = {
1049 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_POWER_SOURCE_VOLTAGE,
1050 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BATTERY_LEVEL,
1051 CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BATTERY_STATUS,
1052};
1053
1054// Build the Device instance resource id list: the static set plus any battery
1055// resources currently bound to an IO. 'out' must hold DEVICE_RESOURCE_IDS +
1056// DEVICE_BATTERY_RESOURCE_IDS entries.
1057
1058static int deviceResourceIds(CProtoWakaama *proto, uint16_t *out)
1059{
1060 int n = 0;
1061
1062 for (uint16_t id : DEVICE_RESOURCE_IDS)
1063 {
1064 out[n++] = id;
1065 }
1066
1067 for (uint16_t id : DEVICE_BATTERY_RESOURCE_IDS)
1068 {
1070 if (bind != nullptr && bind->io != nullptr)
1071 {
1072 out[n++] = id;
1073 }
1074 }
1075
1076 return n;
1077}
1078
1079// Map a dawn battery_state IO value (NuttX BATTERY_* code) to the LwM2M Device
1080// /3/0/20 Battery Status enum (0 Normal, 1 Charging, 2 Charge Complete,
1081// 3 Damaged, 4 Low Battery, 5 Not Installed, 6 Unknown).
1082
1083static int deviceBatteryStatusToLwm2m(int nuttxState)
1084{
1085 switch (nuttxState)
1086 {
1087 case 4: /* BATTERY_CHARGING */
1088 return 1; /* Charging */
1089 case 3: /* BATTERY_FULL */
1090 return 2; /* Charge Complete */
1091 case 2: /* BATTERY_IDLE */
1092 return 0; /* Normal */
1093 case 5: /* BATTERY_DISCHARGING */
1094 return 0; /* Normal */
1095 case 1: /* BATTERY_FAULT */
1096 return 3; /* Damaged */
1097 default: /* BATTERY_UNKNOWN */
1098 return 6; /* Unknown */
1099 }
1100}
1101
1102// Read one bound battery Device resource from its IO and encode it with the
1103// LwM2M Device-object type/units (voltage in mV, level in %, status enum).
1104
1105static bool deviceReadBattery(CProtoWakaama *proto, uint16_t resId, lwm2m_data_t *data)
1106{
1107 CProtoWakaama::SDeviceIoBind *bind = proto->deviceBatteryBind(resId);
1108 uint32_t val;
1109
1110 if (bind == nullptr || bind->io == nullptr || bind->data == nullptr)
1111 {
1112 return false;
1113 }
1114
1115 if (bind->io->getData(*bind->data, 1) < 0)
1116 {
1117 return false;
1118 }
1119
1120 val = *static_cast<uint32_t *>(bind->data->getDataPtr());
1121
1122 switch (resId)
1123 {
1124 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_POWER_SOURCE_VOLTAGE:
1125 lwm2m_data_encode_int(static_cast<int>(val), data); /* mV */
1126 break;
1127
1128 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BATTERY_LEVEL:
1129 lwm2m_data_encode_int(static_cast<int>(val), data); /* percent */
1130 break;
1131
1132 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BATTERY_STATUS:
1133 lwm2m_data_encode_int(deviceBatteryStatusToLwm2m(static_cast<int>(val)), data);
1134 break;
1135
1136 default:
1137 return false;
1138 }
1139
1140 return true;
1141}
1142
1143void dawn::wakaama_internal::deviceResolveBatteryBindings(CProtoWakaama *proto)
1144{
1145 for (uint16_t id : DEVICE_BATTERY_RESOURCE_IDS)
1146 {
1148
1149 if (bind == nullptr || bind->objid == 0)
1150 {
1151 continue;
1152 }
1153
1154 bind->io = proto->getIOObject(bind->objid);
1155 if (bind->io != nullptr)
1156 {
1157 bind->data = bind->io->ddata_alloc(1);
1158 }
1159 }
1160}
1161
1162uint8_t dawn::wakaama_internal::deviceRead(lwm2m_context_t *ctx,
1163 uint16_t instanceId,
1164 int *numData,
1165 lwm2m_data_t **dataArray,
1166 lwm2m_object_t *object)
1167{
1168 CProtoWakaama *proto;
1169 device_instance_s *inst;
1170
1171 UNUSED(ctx);
1172
1173 proto = static_cast<CProtoWakaama *>(object->userData);
1174 if (proto == nullptr)
1175 {
1176 return COAP_500_INTERNAL_SERVER_ERROR;
1177 }
1178
1179 inst = reinterpret_cast<device_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
1180 if (inst == nullptr)
1181 {
1182 return COAP_404_NOT_FOUND;
1183 }
1184
1185 if (*numData == 0)
1186 {
1187 uint16_t ids[(sizeof(DEVICE_RESOURCE_IDS) / sizeof(DEVICE_RESOURCE_IDS[0])) +
1188 (sizeof(DEVICE_BATTERY_RESOURCE_IDS) / sizeof(DEVICE_BATTERY_RESOURCE_IDS[0]))];
1189 int count = deviceResourceIds(proto, ids);
1190
1191 if (!allocateResourceList(ids, count, numData, dataArray))
1192 {
1193 return COAP_500_INTERNAL_SERVER_ERROR;
1194 }
1195 }
1196
1197 for (int i = 0; i < *numData; i++)
1198 {
1199 switch ((*dataArray)[i].id)
1200 {
1201 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_MANUFACTURER:
1202 if (!encodeString(proto->deviceManufacturer(), &(*dataArray)[i]))
1203 {
1204 return COAP_500_INTERNAL_SERVER_ERROR;
1205 }
1206 break;
1207
1208 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_MODEL_NUMBER:
1209 if (!encodeString(proto->deviceModelNumber(), &(*dataArray)[i]))
1210 {
1211 return COAP_500_INTERNAL_SERVER_ERROR;
1212 }
1213 break;
1214
1215 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_SERIAL_NUMBER:
1216 if (!encodeString(proto->deviceSerialNumber(), &(*dataArray)[i]))
1217 {
1218 return COAP_500_INTERNAL_SERVER_ERROR;
1219 }
1220 break;
1221
1222 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_FIRMWARE_VERSION:
1223 if (!encodeString(proto->deviceFirmwareVersion(), &(*dataArray)[i]))
1224 {
1225 return COAP_500_INTERNAL_SERVER_ERROR;
1226 }
1227 break;
1228
1229 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_ERROR_CODE:
1230 lwm2m_data_encode_int(0, &(*dataArray)[i]);
1231 break;
1232
1233 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_CURRENT_TIME:
1234 lwm2m_data_encode_int(lwm2m_gettime(), &(*dataArray)[i]);
1235 break;
1236
1237 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BINDING_MODES:
1238 if (!encodeString("U", &(*dataArray)[i]))
1239 {
1240 return COAP_500_INTERNAL_SERVER_ERROR;
1241 }
1242 break;
1243
1244 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_POWER_SOURCE_VOLTAGE:
1245 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BATTERY_LEVEL:
1246 case CProtoWakaama::WAKAAMA_DEVICE_RESOURCE_BATTERY_STATUS:
1247 if (!deviceReadBattery(proto, (*dataArray)[i].id, &(*dataArray)[i]))
1248 {
1249 return COAP_404_NOT_FOUND;
1250 }
1251 break;
1252
1253 default:
1254 return COAP_404_NOT_FOUND;
1255 }
1256 }
1257
1258 return COAP_205_CONTENT;
1259}
1260
1261uint8_t dawn::wakaama_internal::deviceDiscover(lwm2m_context_t *ctx,
1262 uint16_t instanceId,
1263 int *numData,
1264 lwm2m_data_t **dataArray,
1265 lwm2m_object_t *object)
1266{
1267 UNUSED(ctx);
1268
1269 if (lwm2m_list_find(object->instanceList, instanceId) == nullptr)
1270 {
1271 return COAP_404_NOT_FOUND;
1272 }
1273
1274 if (*numData == 0)
1275 {
1276 device_instance_s *inst =
1277 reinterpret_cast<device_instance_s *>(lwm2m_list_find(object->instanceList, instanceId));
1278
1279 if (inst == nullptr)
1280 {
1281 return COAP_404_NOT_FOUND;
1282 }
1283
1284 CProtoWakaama *proto = static_cast<CProtoWakaama *>(object->userData);
1285 uint16_t ids[(sizeof(DEVICE_RESOURCE_IDS) / sizeof(DEVICE_RESOURCE_IDS[0])) +
1286 (sizeof(DEVICE_BATTERY_RESOURCE_IDS) / sizeof(DEVICE_BATTERY_RESOURCE_IDS[0]))];
1287 int count;
1288
1289 if (proto == nullptr)
1290 {
1291 return COAP_500_INTERNAL_SERVER_ERROR;
1292 }
1293
1294 count = deviceResourceIds(proto, ids);
1295
1296 if (!allocateResourceList(ids, count, numData, dataArray))
1297 {
1298 return COAP_500_INTERNAL_SERVER_ERROR;
1299 }
1300 }
1301
1302 return COAP_205_CONTENT;
1303}
int getData(IODataCmn &data, size_t len, size_t offset=0)
Get data from I/O (public interface with stats tracking).
Definition common.hxx:372
io_ddata_t * ddata_alloc(size_t batch, size_t chunk_size=0)
Allocate data buffer for this I/O.
Definition common.cxx:247
Wakaama LwM2M client protocol implementation.
Definition wakaama.hxx:52
SDeviceIoBind * deviceBatteryBind(uint16_t resourceId)
Return the battery binding for a Device resource id, or nullptr.
Definition wakaama.cxx:449
Out-of-tree user-extension hooks for Dawn.
Definition bindable.hxx:13
Device-object resource backed by a descriptor IO.
Definition wakaama.hxx:248
void * getDataPtr(size_t batch=0)
Get pointer to data only (skips timestamp if present).
Definition ddata.hxx:180
Fixed pools backing bootstrap-mutable Security and Server objects.
Definition internal.hxx:109
Runtime representation of the single LwM2M Device instance.
Definition internal.hxx:101
Runtime representation of one LwM2M Security object instance.
Definition internal.hxx:53
Runtime representation of one LwM2M Server object instance.
Definition internal.hxx:75