Contiki-NG
Loading...
Searching...
No Matches
lwm2m-engine.c
Go to the documentation of this file.
1/*
2 * Copyright (c) 2015-2018, Yanzi Networks AB.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holder nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
20 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
21 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28 * OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * \addtogroup lwm2m
33 * @{
34 */
35
36/**
37 * \file
38 * Implementation of the Contiki OMA LWM2M engine
39 * \author
40 * Joakim Eriksson <joakime@sics.se>
41 * Niclas Finne <nfi@sics.se>
42 * Carlos Gonzalo Peces <carlosgp143@gmail.com>
43 */
44
45#include "lwm2m-engine.h"
46#include "lwm2m-object.h"
47#include "lwm2m-device.h"
48#include "lwm2m-plain-text.h"
49#include "lwm2m-json.h"
50#include "coap-constants.h"
51#include "coap-engine.h"
52#include "lwm2m-tlv.h"
53#include "lwm2m-tlv-reader.h"
54#include "lwm2m-tlv-writer.h"
55#include "lib/list.h"
56#include "sys/cc.h"
57#include <stdio.h>
58#include <string.h>
59#include <inttypes.h>
60#ifndef LWM2M_ENGINE_CLIENT_ENDPOINT_NAME
61#include "net/ipv6/uip-ds6.h"
62#endif /* LWM2M_ENGINE_CLIENT_ENDPOINT_NAME */
63
64/* Log configuration */
65#include "coap-log.h"
66#define LOG_MODULE "lwm2m-eng"
67#define LOG_LEVEL LOG_LEVEL_LWM2M
68
69#ifndef LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX
70#ifdef LWM2M_DEVICE_MODEL_NUMBER
71#define LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX "Contiki-NG-"LWM2M_DEVICE_MODEL_NUMBER
72#else /* LWM2M_DEVICE_MODEL_NUMBER */
73#define LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX "Contiki-NG"
74#endif /* LWM2M_DEVICE_MODEL_NUMBER */
75#endif /* LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX */
76
77#ifdef LWM2M_ENGINE_CONF_USE_RD_CLIENT
78#define USE_RD_CLIENT LWM2M_ENGINE_CONF_USE_RD_CLIENT
79#else
80#define USE_RD_CLIENT 1
81#endif /* LWM2M_ENGINE_CONF_USE_RD_CLIENT */
82
83
84#if LWM2M_QUEUE_MODE_ENABLED
85 /* Queue Mode is handled using the RD Client and the Q-Mode object */
86#define USE_RD_CLIENT 1
87#endif
88
89#if USE_RD_CLIENT
90#include "lwm2m-rd-client.h"
91#endif
92
93#if LWM2M_QUEUE_MODE_ENABLED
94#include "lwm2m-queue-mode.h"
96#if LWM2M_QUEUE_MODE_OBJECT_ENABLED
98#endif /* LWM2M_QUEUE_MODE_OBJECT_ENABLED */
99#endif /* LWM2M_QUEUE_MODE_ENABLED */
100
101/* MACRO for getting out resource ID from resource array ID + flags */
102#define RSC_ID(x) ((uint16_t)(x & 0xffff))
103#define RSC_READABLE(x) ((x & LWM2M_RESOURCE_READ) > 0)
104#define RSC_WRITABLE(x) ((x & LWM2M_RESOURCE_WRITE) > 0)
105#define RSC_UNSPECIFIED(x) ((x & LWM2M_RESOURCE_OP_MASK) == 0)
106
107/* invalid instance ID - ffff object ID */
108#define NO_INSTANCE 0xffffffff
109
110/* This is a double-buffer for generating BLOCKs in CoAP - the idea
111 is that typical LWM2M resources will fit 1 block unless they themselves
112 handle BLOCK transfer - having a double sized buffer makes it possible
113 to allow writing more than one block before sending the full block.
114 The RFC seems to indicate that all blocks execept the last one should
115 be full.
116*/
117static uint8_t d_buf[COAP_MAX_BLOCK_SIZE * 2];
118static lwm2m_buffer_t lwm2m_buf = {
119 .len = 0, .size = COAP_MAX_BLOCK_SIZE * 2, .buffer = d_buf
120};
121static lwm2m_object_instance_t instance_buffer;
122
123/* obj-id / ... */
124static uint16_t lwm2m_buf_lock[4];
125static uint64_t lwm2m_buf_lock_timeout = 0;
126
127static lwm2m_write_opaque_callback current_opaque_callback;
128static int current_opaque_offset = 0;
129
130static coap_handler_status_t lwm2m_handler_callback(coap_message_t *request,
131 coap_message_t *response,
132 uint8_t *buffer,
133 uint16_t buffer_size,
134 int32_t *offset);
135static lwm2m_object_instance_t *
136next_object_instance(const lwm2m_context_t *context, lwm2m_object_t *object, lwm2m_object_instance_t *last);
137
138static struct {
139 uint16_t object_id;
140 uint16_t instance_id;
141 uint16_t token_len;
142 uint8_t token[COAP_TOKEN_LEN];
143 /* in the future also a timeout */
144} created;
145
146COAP_HANDLER(lwm2m_handler, lwm2m_handler_callback);
147LIST(object_list);
148LIST(generic_object_list);
149
150/*---------------------------------------------------------------------------*/
151static lwm2m_object_t *
152get_object(uint16_t object_id)
153{
154 lwm2m_object_t *object;
155 for(object = list_head(generic_object_list);
156 object != NULL;
157 object = object->next) {
158 if(object->impl && object->impl->object_id == object_id) {
159 return object;
160 }
161 }
162 return NULL;
163}
164/*---------------------------------------------------------------------------*/
165static int
166has_non_generic_object(uint16_t object_id)
167{
168 lwm2m_object_instance_t *instance;
169 for(instance = list_head(object_list);
170 instance != NULL;
171 instance = instance->next) {
172 if(instance->object_id == object_id) {
173 return 1;
174 }
175 }
176
177 return 0;
178}
179/*---------------------------------------------------------------------------*/
180static lwm2m_object_instance_t *
181get_instance(uint16_t object_id, uint16_t instance_id, lwm2m_object_t **o)
182{
183 lwm2m_object_instance_t *instance;
184 lwm2m_object_t *object;
185
186 if(o) {
187 *o = NULL;
188 }
189
190 for(instance = list_head(object_list);
191 instance != NULL;
192 instance = instance->next) {
193 if(instance->object_id == object_id) {
194 if(instance->instance_id == instance_id ||
195 instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
196 return instance;
197 }
198 }
199 }
200
201 object = get_object(object_id);
202 if(object != NULL) {
203 if(o) {
204 *o = object;
205 }
206 if(instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
207 return object->impl->get_first(NULL);
208 }
209 return object->impl->get_by_id(instance_id, NULL);
210 }
211
212 return NULL;
213}
214/*---------------------------------------------------------------------------*/
215static lwm2m_object_instance_t *
216get_instance_by_context(const lwm2m_context_t *context, lwm2m_object_t **o)
217{
218 if(context->level < 2) {
219 return get_instance(context->object_id, LWM2M_OBJECT_INSTANCE_NONE, o);
220 }
221 return get_instance(context->object_id, context->object_instance_id, o);
222}
223/*---------------------------------------------------------------------------*/
224static lwm2m_status_t
225call_instance(lwm2m_object_instance_t *instance, lwm2m_context_t *context)
226{
227 if(context->level < 3) {
228 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
229 }
230
231 if(instance == NULL) {
232 /* No instance */
233 return LWM2M_STATUS_NOT_FOUND;
234 }
235
236 if(instance->callback == NULL) {
237 return LWM2M_STATUS_ERROR;
238 }
239
240 return instance->callback(instance, context);
241}
242/*---------------------------------------------------------------------------*/
243/* This is intended to switch out a block2 transfer buffer
244 * It assumes that ctx containts the double buffer and that the outbuf is to
245 * be the new buffer in ctx.
246 */
247static int
248double_buffer_flush(lwm2m_buffer_t *ctxbuf, lwm2m_buffer_t *outbuf, int size)
249{
250 /* Copy the data from the double buffer in ctx to the outbuf and move data */
251 /* If the buffer is less than size - we will output all and get remaining down
252 to zero */
253 if(ctxbuf->len < size) {
254 size = ctxbuf->len;
255 }
256 if(ctxbuf->len >= size && outbuf->size >= size) {
257 LOG_DBG("Double buffer - copying out %d bytes remaining: %d\n",
258 size, ctxbuf->len - size);
259 memcpy(outbuf->buffer, ctxbuf->buffer, size);
260 memcpy(ctxbuf->buffer, &ctxbuf->buffer[size],
261 ctxbuf->len - size);
262 ctxbuf->len -= size;
263 outbuf->len = size;
264 return outbuf->len;
265 }
266 return 0;
267}
268/*---------------------------------------------------------------------------*/
269static inline const char *
270get_method_as_string(coap_resource_flags_t method)
271{
272 if(method == METHOD_GET) {
273 return "GET";
274 } else if(method == METHOD_POST) {
275 return "POST";
276 } else if(method == METHOD_PUT) {
277 return "PUT";
278 } else if(method == METHOD_DELETE) {
279 return "DELETE";
280 } else {
281 return "UNKNOWN";
282 }
283}
284/*--------------------------------------------------------------------------*/
285static const char *
286get_status_as_string(lwm2m_status_t status)
287{
288 static char buffer[14];
289 switch(status) {
290 case LWM2M_STATUS_OK:
291 return "OK";
292 case LWM2M_STATUS_ERROR:
293 return "ERROR";
294 case LWM2M_STATUS_WRITE_ERROR:
295 return "WRITE ERROR";
296 case LWM2M_STATUS_READ_ERROR:
297 return "READ ERROR";
298 case LWM2M_STATUS_BAD_REQUEST:
299 return "BAD REQUEST";
300 case LWM2M_STATUS_UNAUTHORIZED:
301 return "UNAUTHORIZED";
302 case LWM2M_STATUS_FORBIDDEN:
303 return "FORBIDDEN";
304 case LWM2M_STATUS_NOT_FOUND:
305 return "NOT FOUND";
306 case LWM2M_STATUS_OPERATION_NOT_ALLOWED:
307 return "OPERATION NOT ALLOWED";
308 case LWM2M_STATUS_NOT_ACCEPTABLE:
309 return "NOT ACCEPTABLE";
310 case LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT:
311 return "UNSUPPORTED CONTENT FORMAT";
312 case LWM2M_STATUS_NOT_IMPLEMENTED:
313 return "NOT IMPLEMENTED";
314 case LWM2M_STATUS_SERVICE_UNAVAILABLE:
315 return "SERVICE UNAVAILABLE";
316 default:
317 snprintf(buffer, sizeof(buffer) - 1, "<%u>", status);
318 return buffer;
319 }
320}
321/*--------------------------------------------------------------------------*/
322static int
323parse_path(const char *path, int path_len,
324 uint16_t *oid, uint16_t *iid, uint16_t *rid)
325{
326 int ret;
327 int pos;
328 uint16_t val;
329 char c = 0;
330
331 /* get object id */
332 LOG_DBG("parse PATH: \"");
333 LOG_DBG_COAP_STRING(path, path_len);
334 LOG_DBG_("\"\n");
335
336 ret = 0;
337 pos = 0;
338 do {
339 val = 0;
340 /* we should get a value first - consume all numbers */
341 while(pos < path_len && (c = path[pos]) >= '0' && c <= '9') {
342 val = val * 10 + (c - '0');
343 pos++;
344 }
345 /* Slash will mote thing forward - and the end will be when pos == pl */
346 if(c == '/' || pos == path_len) {
347 /* PRINTF("Setting %u = %u\n", ret, val); */
348 if(ret == 0) *oid = val;
349 if(ret == 1) *iid = val;
350 if(ret == 2) *rid = val;
351 ret++;
352 pos++;
353 } else {
354 /* PRINTF("Error: illegal char '%c' at pos:%d\n", c, pos); */
355 return -1;
356 }
357 } while(pos < path_len);
358 return ret;
359}
360/*--------------------------------------------------------------------------*/
361static int
362lwm2m_engine_parse_context(const char *path, int path_len,
363 coap_message_t *request, coap_message_t *response,
364 uint8_t *outbuf, size_t outsize,
365 lwm2m_context_t *context)
366{
367 int ret;
368 if(context == NULL || path == NULL) {
369 return 0;
370 }
371
372 ret = parse_path(path, path_len, &context->object_id,
373 &context->object_instance_id, &context->resource_id);
374
375 if(ret > 0) {
376 context->level = ret;
377 }
378
379 return ret;
380}
381
382/*---------------------------------------------------------------------------*/
383void lwm2m_engine_set_opaque_callback(lwm2m_context_t *ctx, lwm2m_write_opaque_callback cb)
384{
385 /* Here we should set the callback for the opaque that we are currently generating... */
386 /* And we should in the future associate the callback with the CoAP message info - MID */
387 LOG_DBG("Setting opaque handler - offset: %"PRIu32",%d\n",
388 ctx->offset, ctx->outbuf->len);
389
390 current_opaque_offset = 0;
391 current_opaque_callback = cb;
392}
393/*---------------------------------------------------------------------------*/
394int
395lwm2m_engine_set_rd_data(lwm2m_buffer_t *outbuf, int block)
396{
397 /* remember things here - need to lock lwm2m buffer also!!! */
398 static lwm2m_object_t *object;
399 static lwm2m_object_instance_t *instance;
400 int len;
401 /* pick size from outbuf */
402 int maxsize = outbuf->size;
403
404 if(lwm2m_buf_lock[0] != 0 && (lwm2m_buf_lock_timeout > coap_timer_uptime()) &&
405 ((lwm2m_buf_lock[1] != 0xffff) ||
406 (lwm2m_buf_lock[2] != 0xffff))) {
407 LOG_DBG("Set-RD: already exporting resource: %d/%d/%d\n",
408 lwm2m_buf_lock[1], lwm2m_buf_lock[2], lwm2m_buf_lock[3]);
409 /* fail - what should we return here? */
410 return 0;
411 }
412
413 if(block == 0) {
414 LOG_DBG("Starting RD generation\n");
415 /* start with simple object instances */
416 instance = list_head(object_list);
417 object = NULL;
418
419 if(instance == NULL) {
420 /* No simple object instances available */
421 object = list_head(generic_object_list);
422 if(object == NULL) {
423 /* No objects of any kind available */
424 return 0;
425 }
426 if(object->impl != NULL) {
427 instance = object->impl->get_first(NULL);
428 }
429 }
430
431 lwm2m_buf_lock[0] = 1; /* lock "flag" */
432 lwm2m_buf_lock[1] = 0xffff;
433 lwm2m_buf_lock[2] = 0xffff;
434 lwm2m_buf_lock[3] = 0xffff;
435 } else {
436 /* object and instance was static... */
437 }
438
439 lwm2m_buf_lock_timeout = coap_timer_uptime() + 1000;
440
441 LOG_DBG("Generating RD list:");
442 while(instance != NULL || object != NULL) {
443 int pos = lwm2m_buf.len;
444 if(instance != NULL) {
445 len = snprintf((char *) &lwm2m_buf.buffer[pos],
446 lwm2m_buf.size - pos, (pos > 0 || block > 0) ? ",</%d/%d>" : "</%d/%d>",
447 instance->object_id, instance->instance_id);
448 LOG_DBG_((pos > 0 || block > 0) ? ",</%d/%d>" : "</%d/%d>",
449 instance->object_id, instance->instance_id);
450 } else if(object->impl != NULL) {
451 len = snprintf((char *) &lwm2m_buf.buffer[pos],
452 lwm2m_buf.size - pos,
453 (pos > 0 || block > 0) ? ",</%d>" : "</%d>",
454 object->impl->object_id);
455 LOG_DBG_((pos > 0 || block > 0) ? ",</%d>" : "</%d>",
456 object->impl->object_id);
457 } else {
458 len = 0;
459 }
460 lwm2m_buf.len += len;
461 if(instance != NULL) {
462 instance = next_object_instance(NULL, object, instance);
463 }
464
465 if(instance == NULL) {
466 if(object == NULL) {
467 /*
468 * No object and no instance - we are done with simple object instances.
469 */
470 object = list_head(generic_object_list);
471 } else {
472 /*
473 * Object exists but not an instance - instances for this object are
474 * done - go to next.
475 */
476 object = object->next;
477 }
478
479 if(object != NULL && object->impl != NULL) {
480 instance = object->impl->get_first(NULL);
481 }
482
483 if(instance == NULL && object == NULL && lwm2m_buf.len <= maxsize) {
484 /* Data generation is done. No more messages are needed after this. */
485 break;
486 }
487 }
488
489 if(lwm2m_buf.len >= maxsize) {
490 LOG_DBG_("\n");
491 LOG_DBG("**** CoAP MAX BLOCK Reached!!! **** SEND\n");
492 /* If the produced data is larger than a CoAP block we need to send
493 this now */
494 double_buffer_flush(&lwm2m_buf, outbuf, maxsize);
495 /* there will be more - keep lock! */
496 return 1;
497 }
498 }
499 LOG_DBG_("\n");
500 double_buffer_flush(&lwm2m_buf, outbuf, maxsize);
501 /* unlock the buffer */
502 lwm2m_buf_lock[0] = 0;
503 return 0;
504}
505/*---------------------------------------------------------------------------*/
506void
507lwm2m_engine_init(void)
508{
509 list_init(object_list);
510 list_init(generic_object_list);
511
512#ifdef LWM2M_ENGINE_CLIENT_ENDPOINT_NAME
513 const char *endpoint = LWM2M_ENGINE_CLIENT_ENDPOINT_NAME;
514
515#else /* LWM2M_ENGINE_CLIENT_ENDPOINT_NAME */
516 static char endpoint[32];
517 int len, i;
518 uint8_t state;
519 uip_ipaddr_t *ipaddr;
520
521 len = strlen(LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX);
522 /* ensure that this fits with the hex-nums */
523 if(len > sizeof(endpoint) - 13) {
524 len = sizeof(endpoint) - 13;
525 }
526
527 for(i = 0; i < len; i++) {
528 if(LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX[i] == ' ') {
529 endpoint[i] = '-';
530 } else {
531 endpoint[i] = LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX[i];
532 }
533 }
534 /* pick an IP address that is PREFERRED or TENTATIVE */
535 ipaddr = NULL;
536 for(i = 0; i < UIP_DS6_ADDR_NB; i++) {
537 state = uip_ds6_if.addr_list[i].state;
538 if(uip_ds6_if.addr_list[i].isused &&
539 (state == ADDR_TENTATIVE || state == ADDR_PREFERRED)) {
540 ipaddr = &(uip_ds6_if.addr_list[i]).ipaddr;
541 break;
542 }
543 }
544
545 if(ipaddr != NULL) {
546 for(i = 0; i < 6; i++) {
547 /* assume IPv6 for now */
548 uint8_t b = ipaddr->u8[10 + i];
549 endpoint[len++] = (b >> 4) > 9 ? 'A' - 10 + (b >> 4) : '0' + (b >> 4);
550 endpoint[len++] = (b & 0xf) > 9 ? 'A' - 10 + (b & 0xf) : '0' + (b & 0xf);
551 }
552 }
553
554 /* a zero at end of string */
555 endpoint[len] = 0;
556
557#endif /* LWM2M_ENGINE_CLIENT_ENDPOINT_NAME */
558
559 /* Initialize CoAP engine. Contiki-NG already does that from the main,
560 * but for standalone use of lwm2m, this is required here. coap_engine_init()
561 * checks for double-initialization and can be called twice safely. */
562 coap_engine_init();
563
564 /* Register the CoAP handler for lightweight object handling */
565 coap_add_handler(&lwm2m_handler);
566
567#if USE_RD_CLIENT
568 lwm2m_rd_client_init(endpoint);
569#endif
570
571#if LWM2M_QUEUE_MODE_ENABLED && LWM2M_QUEUE_MODE_OBJECT_ENABLED
572 lwm2m_queue_mode_object_init();
573#endif
574}
575/*---------------------------------------------------------------------------*/
576/*
577 * Set the writer pointer to the proper writer based on the Accept: header
578 *
579 * param[in] context LWM2M context to operate on
580 * param[in] accept Accept type number from CoAP headers
581 *
582 * return The content type of the response if the selected writer is used
583 */
584static unsigned int
585lwm2m_engine_select_writer(lwm2m_context_t *context, unsigned int accept)
586{
587 switch(accept) {
588 case LWM2M_TLV:
589 case LWM2M_OLD_TLV:
590 context->writer = &lwm2m_tlv_writer;
591 break;
592 case LWM2M_TEXT_PLAIN:
593 case TEXT_PLAIN:
594 context->writer = &lwm2m_plain_text_writer;
595 break;
596 case LWM2M_JSON:
597 case LWM2M_OLD_JSON:
598 case APPLICATION_JSON:
599 context->writer = &lwm2m_json_writer;
600 break;
601 default:
602 LOG_WARN("Unknown Accept type %u, using LWM2M plain text\n", accept);
603 context->writer = &lwm2m_plain_text_writer;
604 /* Set the response type to plain text */
605 accept = LWM2M_TEXT_PLAIN;
606 break;
607 }
608 context->content_type = accept;
609 return accept;
610}
611/*---------------------------------------------------------------------------*/
612/*
613 * Set the reader pointer to the proper reader based on the Content-format: header
614 *
615 * param[in] context LWM2M context to operate on
616 * param[in] content_format Content-type type number from CoAP headers
617 */
618static void
619lwm2m_engine_select_reader(lwm2m_context_t *context, unsigned int content_format)
620{
621 switch(content_format) {
622 case LWM2M_TLV:
623 case LWM2M_OLD_TLV:
624 context->reader = &lwm2m_tlv_reader;
625 break;
626 case LWM2M_JSON:
627 case LWM2M_OLD_JSON:
628 context->reader = &lwm2m_plain_text_reader;
629 break;
630 case LWM2M_TEXT_PLAIN:
631 case TEXT_PLAIN:
632 context->reader = &lwm2m_plain_text_reader;
633 break;
634 default:
635 LOG_WARN("Unknown content type %u, using LWM2M plain text\n",
636 content_format);
637 context->reader = &lwm2m_plain_text_reader;
638 break;
639 }
640}
641
642/*---------------------------------------------------------------------------*/
643/* Lightweight object instances */
644/*---------------------------------------------------------------------------*/
645static uint32_t last_instance_id = NO_INSTANCE;
646static int last_rsc_pos;
647
648/* Multi read will handle read of JSON / TLV or Discovery (Link Format) */
649static lwm2m_status_t
650perform_multi_resource_read_op(lwm2m_object_t *object,
651 lwm2m_object_instance_t *instance,
652 lwm2m_context_t *ctx)
653{
654 int size = ctx->outbuf->size;
655 int len = 0;
656 uint8_t initialized = 0; /* used for commas, etc */
657 uint8_t num_read = 0;
658 lwm2m_buffer_t *outbuf;
659
660 if(instance == NULL) {
661 /* No existing instance */
662 return LWM2M_STATUS_NOT_FOUND;
663 }
664
665 if(ctx->level < 3 &&
666 (ctx->content_type == LWM2M_TEXT_PLAIN ||
667 ctx->content_type == TEXT_PLAIN ||
668 ctx->content_type == LWM2M_OLD_OPAQUE)) {
669 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
670 }
671
672 /* copy out the out-buffer as read will use its own - will be same for disoc when
673 read is fixed */
674 outbuf = ctx->outbuf;
675
676 /* Currently we only handle one incoming read request at a time - so we return
677 BUZY or service unavailable */
678 if(lwm2m_buf_lock[0] != 0 && (lwm2m_buf_lock_timeout > coap_timer_uptime()) &&
679 ((lwm2m_buf_lock[1] != ctx->object_id) ||
680 (lwm2m_buf_lock[2] != ctx->object_instance_id) ||
681 (lwm2m_buf_lock[3] != ctx->resource_id))) {
682 LOG_DBG("Multi-read: already exporting resource: %d/%d/%d\n",
683 lwm2m_buf_lock[1], lwm2m_buf_lock[2], lwm2m_buf_lock[3]);
684 return LWM2M_STATUS_SERVICE_UNAVAILABLE;
685 }
686
687 LOG_DBG("MultiRead: %d/%d/%d lv:%d offset:%"PRIu32"\n",
688 ctx->object_id, ctx->object_instance_id, ctx->resource_id,
689 ctx->level, ctx->offset);
690
691 /* Make use of the double buffer */
692 ctx->outbuf = &lwm2m_buf;
693
694 if(ctx->offset == 0) {
695 /* First GET request - need to setup all buffers and reset things here */
696 last_instance_id =
697 ((uint32_t)instance->object_id << 16) | instance->instance_id;
698 last_rsc_pos = 0;
699 /* reset any callback */
700 current_opaque_callback = NULL;
701 /* reset lwm2m_buf_len - so that we can use the double-size buffer */
702 lwm2m_buf_lock[0] = 1; /* lock "flag" */
703 lwm2m_buf_lock[1] = ctx->object_id;
704 lwm2m_buf_lock[2] = ctx->object_instance_id;
705 lwm2m_buf_lock[3] = ctx->resource_id;
706 lwm2m_buf.len = 0;
707 /* Here we should print top node */
708 } else {
709 /* offset > 0 - assume that we are already in a disco or multi get*/
710 instance = get_instance(last_instance_id >> 16, last_instance_id & 0xffff,
711 &object);
712
713 /* we assume that this was initialized */
714 initialized = 1;
715 ctx->writer_flags |= WRITER_OUTPUT_VALUE;
716 if(instance == NULL) {
717 ctx->offset = -1;
718 ctx->outbuf->buffer[0] = ' ';
719 }
720 }
721 lwm2m_buf_lock_timeout = coap_timer_uptime() + 1000;
722
723 while(instance != NULL) {
724 /* Do the discovery or read */
725 if(instance->resource_ids != NULL && instance->resource_count > 0) {
726 /* show all the available resources (or read all) */
727 while(last_rsc_pos < instance->resource_count) {
728 LOG_DBG("READ: 0x%"PRIx32" 0x%x 0x%x lv:%d\n",
729 instance->resource_ids[last_rsc_pos],
730 RSC_ID(instance->resource_ids[last_rsc_pos]),
731 ctx->resource_id, ctx->level);
732
733 /* Check if this is a object read or if it is the correct resource */
734 if(ctx->level < 3 || ctx->resource_id == RSC_ID(instance->resource_ids[last_rsc_pos])) {
735 /* ---------- Discovery operation ------------- */
736 /* If this is a discovery all the object, instance, and resource triples should be
737 generted */
738 if(ctx->operation == LWM2M_OP_DISCOVER) {
739 int dim = 0;
740 len = snprintf((char *) &ctx->outbuf->buffer[ctx->outbuf->len],
741 ctx->outbuf->size - ctx->outbuf->len,
742 (ctx->outbuf->len == 0 && ctx->offset == 0) ? "</%d/%d/%d>":",</%d/%d/%d>",
743 instance->object_id, instance->instance_id,
744 RSC_ID(instance->resource_ids[last_rsc_pos]));
745 if(instance->resource_dim_callback != NULL &&
746 (dim = instance->resource_dim_callback(instance,
747 RSC_ID(instance->resource_ids[last_rsc_pos]))) > 0) {
748 len += snprintf((char *) &ctx->outbuf->buffer[ctx->outbuf->len + len],
749 ctx->outbuf->size - ctx->outbuf->len - len, ";dim=%d", dim);
750 }
751 /* here we have "read" out something */
752 num_read++;
753 ctx->outbuf->len += len;
754 if(len < 0 || ctx->outbuf->len >= size) {
755 double_buffer_flush(ctx->outbuf, outbuf, size);
756
757 LOG_DBG("Copied lwm2m buf - remaining: %d\n", lwm2m_buf.len);
758 /* switch buffer */
759 ctx->outbuf = outbuf;
760 ctx->writer_flags |= WRITER_HAS_MORE;
761 ctx->offset += size;
762 return LWM2M_STATUS_OK;
763 }
764 /* ---------- Read operation ------------- */
765 } else if(ctx->operation == LWM2M_OP_READ) {
766 lwm2m_status_t success = 0;
767 uint8_t lv;
768
769 lv = ctx->level;
770
771 /* Do not allow a read on a non-readable */
772 if(lv == 3 && !RSC_READABLE(instance->resource_ids[last_rsc_pos])) {
773 lwm2m_buf_lock[0] = 0;
774 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
775 }
776 /* Set the resource ID is ctx->level < 3 */
777 if(lv < 3) {
778 ctx->resource_id = RSC_ID(instance->resource_ids[last_rsc_pos]);
779 }
780 if(lv < 2) {
781 ctx->object_instance_id = instance->instance_id;
782 }
783
784 if(RSC_READABLE(instance->resource_ids[last_rsc_pos])) {
785 ctx->level = 3;
786 if(!initialized) {
787 /* Now we need to initialize the object writing for this new object */
788 len = ctx->writer->init_write(ctx);
789 ctx->outbuf->len += len;
790 LOG_DBG("INIT WRITE len:%d size:%"PRIu16"\n", len, ctx->outbuf->size);
791 initialized = 1;
792 }
793
794 if(current_opaque_callback == NULL) {
795 LOG_DBG("Doing the callback to the resource %d\n", ctx->outbuf->len);
796 /* No special opaque callback to handle - use regular callback */
797 success = instance->callback(instance, ctx);
798 LOG_DBG("After the callback to the resource %d: %s\n",
799 ctx->outbuf->len, get_status_as_string(success));
800
801 if(success != LWM2M_STATUS_OK) {
802 /* What to do here? */
803 LOG_DBG("Callback failed: %s\n", get_status_as_string(success));
804 if(lv < 3) {
805 if(success == LWM2M_STATUS_NOT_FOUND) {
806 /* ok with a not found during a multi read - what more
807 is ok? */
808 } else {
809 lwm2m_buf_lock[0] = 0;
810 return success;
811 }
812 } else {
813 lwm2m_buf_lock[0] = 0;
814 return success;
815 }
816 }
817 }
818 if(current_opaque_callback != NULL) {
819 uint32_t old_offset = ctx->offset;
820 int num_write = COAP_MAX_BLOCK_SIZE - ctx->outbuf->len;
821 /* Check if the callback did set a opaque callback function - then
822 we should produce data via that callback until the opaque has fully
823 been handled */
824 ctx->offset = current_opaque_offset;
825 /* LOG_DBG("Calling the opaque handler %x\n", ctx->writer_flags); */
826 success = current_opaque_callback(instance, ctx, num_write);
827 if((ctx->writer_flags & WRITER_HAS_MORE) == 0) {
828 /* This opaque stream is now done! */
829 /* LOG_DBG("Setting opaque callback to null - it is done!\n"); */
830 current_opaque_callback = NULL;
831 } else if(ctx->outbuf->len < COAP_MAX_BLOCK_SIZE) {
832 lwm2m_buf_lock[0] = 0;
833 return LWM2M_STATUS_ERROR;
834 }
835 current_opaque_offset += num_write;
836 ctx->offset = old_offset;
837 /* LOG_DBG("Setting back offset to: %d\n", ctx->offset); */
838 }
839
840 /* here we have "read" out something */
841 num_read++;
842 /* We will need to handle no-success and other things */
843 LOG_DBG("Called %u/%u/%u outlen:%u %s\n",
844 ctx->object_id, ctx->object_instance_id, ctx->resource_id,
845 ctx->outbuf->len, get_status_as_string(success));
846
847 /* we need to handle full buffer, etc here also! */
848 ctx->level = lv;
849 } else {
850 LOG_DBG("Resource %u not readable\n",
851 RSC_ID(instance->resource_ids[last_rsc_pos]));
852 }
853 }
854 }
855 if(current_opaque_callback == NULL) {
856 /* This resource is now done - (only when the opaque is also done) */
857 last_rsc_pos++;
858 } else {
859 LOG_DBG("Opaque is set - continue with that.\n");
860 }
861
862 if(ctx->outbuf->len >= COAP_MAX_BLOCK_SIZE) {
863 LOG_DBG("**** CoAP MAX BLOCK Reached!!! **** SEND\n");
864 /* If the produced data is larger than a CoAP block we need to send
865 this now */
866 if(ctx->outbuf->len < 2 * COAP_MAX_BLOCK_SIZE) {
867 /* We assume that size is equal to COAP_MAX_BLOCK_SIZE here */
868 double_buffer_flush(ctx->outbuf, outbuf, size);
869
870 LOG_DBG("Copied lwm2m buf - remaining: %d\n", lwm2m_buf.len);
871 /* switch buffer */
872 ctx->outbuf = outbuf;
873 ctx->writer_flags |= WRITER_HAS_MORE;
874 ctx->offset += size;
875 /* OK - everything went well... but we have more. - keep the lock here! */
876 return LWM2M_STATUS_OK;
877 } else {
878 LOG_WARN("*** ERROR Overflow?\n");
879 return LWM2M_STATUS_ERROR;
880 }
881 }
882 }
883 }
884 instance = next_object_instance(ctx, object, instance);
885 if(instance != NULL) {
886 last_instance_id =
887 ((uint32_t)instance->object_id << 16) | instance->instance_id;
888 } else {
889 last_instance_id = NO_INSTANCE;
890 }
891 if(ctx->operation == LWM2M_OP_READ) {
892 LOG_DBG("END Writer %d ->", ctx->outbuf->len);
893 len = ctx->writer->end_write(ctx);
894 ctx->outbuf->len += len;
895 LOG_DBG("%d\n", ctx->outbuf->len);
896 }
897
898 initialized = 0;
899 last_rsc_pos = 0;
900 }
901
902 /* did not read anything even if we should have - on single item */
903 if(num_read == 0 && ctx->level == 3) {
904 lwm2m_buf_lock[0] = 0;
905 return LWM2M_STATUS_NOT_FOUND;
906 }
907
908 /* seems like we are done! - flush buffer */
909 len = double_buffer_flush(ctx->outbuf, outbuf, size);
910 ctx->outbuf = outbuf;
911 ctx->offset += len;
912
913 /* If there is still data in the double-buffer - indicate that so that we get another
914 callback */
915 if(lwm2m_buf.len > 0) {
916 ctx->writer_flags |= WRITER_HAS_MORE;
917 } else {
918 /* OK - everything went well we are done, unlock and return */
919 lwm2m_buf_lock[0] = 0;
920 }
921
922 LOG_DBG("At END: Copied lwm2m buf %d\n", len);
923
924 return LWM2M_STATUS_OK;
925}
926/*---------------------------------------------------------------------------*/
927static lwm2m_object_instance_t *
928create_instance(lwm2m_context_t *context, lwm2m_object_t *object)
929{
930 lwm2m_object_instance_t *instance;
931 if(object == NULL || object->impl == NULL ||
932 object->impl->create_instance == NULL) {
933 return NULL;
934 }
935
936 /* NOTE: context->object_instance_id needs to be set before calling */
937 instance = object->impl->create_instance(context->object_instance_id, NULL);
938 if(instance != NULL) {
939 LOG_DBG("Created instance: %u/%u\n", context->object_id, context->object_instance_id);
940 coap_set_status_code(context->response, CREATED_2_01);
941#if USE_RD_CLIENT
942 lwm2m_rd_client_set_update_rd();
943#endif
944 }
945 return instance;
946}
947/*---------------------------------------------------------------------------*/
948#define MODE_NONE 0
949#define MODE_INSTANCE 1
950#define MODE_VALUE 2
951#define MODE_READY 3
952
953static lwm2m_object_instance_t *
954get_or_create_instance(lwm2m_context_t *ctx, lwm2m_object_t *object,
955 uint16_t *c)
956{
957 lwm2m_object_instance_t *instance;
958
959 instance = get_instance_by_context(ctx, NULL);
960 LOG_DBG("Instance: %u/%u/%u = %p\n", ctx->object_id,
961 ctx->object_instance_id, ctx->resource_id, instance);
962 /* by default we assume that the instance is not created... so we set flag to zero */
963 if(c != NULL) {
964 *c = LWM2M_OBJECT_INSTANCE_NONE;
965 }
966 if(instance == NULL) {
967 instance = create_instance(ctx, object);
968 if(instance != NULL) {
969 if(c != NULL) {
970 *c = instance->instance_id;
971 }
972 created.instance_id = instance->instance_id;
973 created.object_id = instance->object_id;
974 created.token_len = MIN(COAP_TOKEN_LEN, ctx->request->token_len);
975 memcpy(&created.token, ctx->request->token, created.token_len);
976 }
977 }
978 return instance;
979}
980/*---------------------------------------------------------------------------*/
981static int
982check_write(lwm2m_context_t *ctx, lwm2m_object_instance_t *instance, int rid)
983{
984 int i;
985 if(instance->resource_ids != NULL && instance->resource_count > 0) {
986 int count = instance->resource_count;
987 for(i = 0; i < count; i++) {
988 if(RSC_ID(instance->resource_ids[i]) == rid) {
989 if(RSC_WRITABLE(instance->resource_ids[i])) {
990 /* yes - writable */
991 return 1;
992 }
993 if(RSC_UNSPECIFIED(instance->resource_ids[i]) &&
994 created.instance_id == instance->instance_id &&
995 created.object_id == instance->object_id &&
996 created.token_len == ctx->request->token_len &&
997 memcmp(&created.token, ctx->request->token,
998 created.token_len) == 0) {
999 /* yes - writeable at create - never otherwise - sec / srv */
1000 return 1;
1001 }
1002 break;
1003 }
1004 }
1005 }
1006 /* Resource did not exist... - Ignore to avoid problems. */
1007 if(created.instance_id == instance->instance_id &&
1008 created.object_id == instance->object_id &&
1009 created.token_len == ctx->request->token_len &&
1010 memcmp(&created.token, ctx->request->token,
1011 created.token_len) == 0) {
1012 LOG_DBG("Ignoring resource %u/%u/%d in newly created instance\n",
1013 created.object_id, created.instance_id, rid);
1014 return 1;
1015 }
1016 return 0;
1017}
1018/*---------------------------------------------------------------------------*/
1019static lwm2m_status_t
1020process_tlv_write(lwm2m_context_t *ctx, lwm2m_object_t *object,
1021 int rid, uint8_t *data, int len)
1022{
1023 lwm2m_object_instance_t *instance;
1024 uint16_t created = LWM2M_OBJECT_INSTANCE_NONE;
1025 ctx->inbuf->buffer = data;
1026 ctx->inbuf->pos = 0;
1027 ctx->inbuf->size = len;
1028 ctx->level = 3;
1029 ctx->resource_id = rid;
1030 LOG_DBG(" Doing callback to %u/%u/%u\n", ctx->object_id,
1031 ctx->object_instance_id, ctx->resource_id);
1032 instance = get_or_create_instance(ctx, object, &created);
1033 if(instance != NULL && instance->callback != NULL) {
1034 if(check_write(ctx, instance, rid)) {
1035 return instance->callback(instance, ctx);
1036 } else {
1037 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1038 }
1039 }
1040 return LWM2M_STATUS_ERROR;
1041}
1042/*---------------------------------------------------------------------------*/
1043static int last_tlv_id = 0;
1044
1045static lwm2m_status_t
1046perform_multi_resource_write_op(lwm2m_object_t *object,
1047 lwm2m_object_instance_t *instance,
1048 lwm2m_context_t *ctx, int format)
1049{
1050 /* Only for JSON and TLV formats */
1051 uint16_t oid = 0, iid = 0, rid = 0;
1052 uint8_t olv = 0;
1053 uint8_t mode = 0;
1054 uint8_t *inbuf;
1055 int inpos;
1056 size_t insize;
1057 int i;
1058 uint16_t created = LWM2M_OBJECT_INSTANCE_NONE;
1059
1060 olv = ctx->level;
1061 inbuf = ctx->inbuf->buffer;
1062 inpos = ctx->inbuf->pos;
1063 insize = ctx->inbuf->size;
1064
1065 if(format == LWM2M_JSON || format == LWM2M_OLD_JSON) {
1066 struct json_data json;
1067
1068 while(lwm2m_json_next_token(ctx, &json)) {
1069 LOG_DBG("JSON: '");
1070 for(i = 0; i < json.name_len; i++) {
1071 LOG_DBG_("%c", json.name[i]);
1072 }
1073 LOG_DBG_("':'");
1074 for(i = 0; i < json.value_len; i++) {
1075 LOG_DBG_("%c", json.value[i]);
1076 }
1077 LOG_DBG_("'\n");
1078
1079 if(json.name != NULL && json.name_len > 0 && json.name[0] == 'n') {
1080 i = parse_path((const char *) json.value, json.value_len, &oid, &iid, &rid);
1081 if(i > 0) {
1082 if(ctx->level == 1) {
1083 ctx->level = 3;
1084 ctx->object_instance_id = oid;
1085 ctx->resource_id = iid;
1086
1087 instance = get_or_create_instance(ctx, object, &created);
1088 }
1089 if(instance != NULL && instance->callback != NULL) {
1090 mode |= MODE_INSTANCE;
1091 } else {
1092 /* Failure... */
1093 return LWM2M_STATUS_ERROR;
1094 }
1095 }
1096 } else {
1097 /* HACK - assume value node - can it be anything else? */
1098 mode |= MODE_VALUE;
1099 /* update values */
1100 inbuf = ctx->inbuf->buffer;
1101 inpos = ctx->inbuf->pos;
1102
1103 ctx->inbuf->buffer = json.value;
1104 ctx->inbuf->pos = 0;
1105 ctx->inbuf->size = json.value_len;
1106 }
1107
1108 if(mode == MODE_READY) {
1109 /* allow write if just created - otherwise not */
1110 if(!check_write(ctx, instance, ctx->resource_id)) {
1111 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1112 }
1113 if(instance->callback(instance, ctx) != LWM2M_STATUS_OK) {
1114 /* TODO what to do here */
1115 }
1116 mode = MODE_NONE;
1117 ctx->inbuf->buffer = inbuf;
1118 ctx->inbuf->pos = inpos;
1119 ctx->inbuf->size = insize;
1120 ctx->level = olv;
1121 }
1122 }
1123 } else if(format == LWM2M_TLV || format == LWM2M_OLD_TLV) {
1124 size_t len;
1125 lwm2m_tlv_t tlv;
1126 int tlvpos = 0;
1127 lwm2m_status_t status;
1128
1129 /* For handling blockwise (BLOCK1) write */
1130 uint32_t num;
1131 uint8_t more;
1132 uint16_t size;
1133 uint32_t offset;
1134
1135 /* NOTE: this assumes that a BLOCK1 non-first block is not a part of a
1136 small TLV but rather a large opaque - this needs to be fixed in the
1137 future */
1138
1139 if(coap_get_header_block1(ctx->request, &num, &more, &size, &offset)) {
1140 LOG_DBG("CoAP BLOCK1: %"PRIu32"/%d/%d offset:%"PRIu32
1141 " LWM2M CTX->offset=%"PRIu32"\n",
1142 num, more, size, offset, ctx->offset);
1143 LOG_DBG("Last TLV ID:%d final:%d\n", last_tlv_id,
1144 lwm2m_object_is_final_incoming(ctx));
1145 if(offset > 0) {
1146 /* The BLOCK1 size is derived from the option's SZX field and is
1147 independent of the actual payload length. Clamp it to the bytes
1148 actually present so the resource callback cannot read past the
1149 payload buffer. */
1150 status = process_tlv_write(ctx, object, last_tlv_id,
1151 inbuf, MIN(size, insize));
1152 return status;
1153 }
1154 }
1155
1156 while(tlvpos < insize) {
1157 len = lwm2m_tlv_read(&tlv, &inbuf[tlvpos], insize - tlvpos);
1158 if(len == 0) {
1159 /* Malformed or truncated TLV - stop parsing to avoid a stuck loop. */
1160 LOG_WARN("Failed to read TLV at offset %d/%d\n",
1161 (int)tlvpos, (int)insize);
1162 return LWM2M_STATUS_BAD_REQUEST;
1163 }
1164 LOG_DBG("Got TLV format First is: type:%d id:%d len:%d (p:%d len:%d/%d)\n",
1165 tlv.type, tlv.id, (int) tlv.length,
1166 (int) tlvpos, (int) len, (int) insize);
1167 if(tlv.type == LWM2M_TLV_TYPE_OBJECT_INSTANCE) {
1168 lwm2m_tlv_t tlv2;
1169 int len2;
1170 int pos = 0;
1171 ctx->object_instance_id = tlv.id;
1172 if(tlv.length == 0) {
1173 /* Create only - no data */
1174 if((instance = create_instance(ctx, object)) == NULL) {
1175 return LWM2M_STATUS_ERROR;
1176 }
1177 }
1178 while(pos < tlv.length && (len2 = lwm2m_tlv_read(&tlv2, &tlv.value[pos],
1179 tlv.length - pos))) {
1180 LOG_DBG(" TLV type:%d id:%d len:%d (len:%d/%d)\n",
1181 tlv2.type, tlv2.id, (int)tlv2.length,
1182 (int)len2, (int)insize);
1183 if(tlv2.type == LWM2M_TLV_TYPE_RESOURCE) {
1184 last_tlv_id = tlv2.id;
1185 status = process_tlv_write(ctx, object, tlv2.id,
1186 (uint8_t *)&tlv.value[pos], len2);
1187 if(status != LWM2M_STATUS_OK) {
1188 return status;
1189 }
1190 }
1191 pos += len2;
1192 }
1193 } else if(tlv.type == LWM2M_TLV_TYPE_RESOURCE) {
1194 status = process_tlv_write(ctx, object, tlv.id, &inbuf[tlvpos], len);
1195 if(status != LWM2M_STATUS_OK) {
1196 return status;
1197 }
1198 coap_set_status_code(ctx->response, CHANGED_2_04);
1199 }
1200 tlvpos += len;
1201 }
1202 } else if(format == LWM2M_TEXT_PLAIN ||
1203 format == TEXT_PLAIN ||
1204 format == LWM2M_OLD_OPAQUE) {
1205 return call_instance(instance, ctx);
1206
1207 } else {
1208 /* Unsupported format */
1209 return LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT;
1210 }
1211
1212 /* Here we have a success! */
1213 return LWM2M_STATUS_OK;
1214}
1215/*---------------------------------------------------------------------------*/
1216lwm2m_object_instance_t *
1217lwm2m_engine_get_instance_buffer(void)
1218{
1219 return &instance_buffer;
1220}
1221/*---------------------------------------------------------------------------*/
1222int
1223lwm2m_engine_has_instance(uint16_t object_id, uint16_t instance_id)
1224{
1225 return get_instance(object_id, instance_id, NULL) != NULL;
1226}
1227/*---------------------------------------------------------------------------*/
1228int
1229lwm2m_engine_add_object(lwm2m_object_instance_t *object)
1230{
1231 lwm2m_object_instance_t *instance;
1232 uint16_t min_id = 0xffff;
1233 uint16_t max_id = 0;
1234 int found = 0;
1235
1236 if(object == NULL || object->callback == NULL) {
1237 /* Insufficient object configuration */
1238 LOG_DBG("failed to register NULL object\n");
1239 return 0;
1240 }
1241 if(get_object(object->object_id) != NULL) {
1242 /* A generic object with this id has already been registered */
1243 LOG_DBG("object with id %u already registered\n", object->object_id);
1244 return 0;
1245 }
1246
1247 for(instance = list_head(object_list);
1248 instance != NULL;
1249 instance = instance->next) {
1250 if(object->object_id == instance->object_id) {
1251 if(object->instance_id == instance->instance_id) {
1252 LOG_DBG("object with id %u/%u already registered\n",
1253 instance->object_id, instance->instance_id);
1254 return 0;
1255 }
1256
1257 found++;
1258 if(instance->instance_id > max_id) {
1259 max_id = instance->instance_id;
1260 }
1261 if(instance->instance_id < min_id) {
1262 min_id = instance->instance_id;
1263 }
1264 }
1265 }
1266
1267 if(object->instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
1268 /* No instance id has been assigned yet */
1269 if(found == 0) {
1270 /* First object with this id */
1271 object->instance_id = 0;
1272 } else if(min_id > 0) {
1273 object->instance_id = min_id - 1;
1274 } else {
1275 object->instance_id = max_id + 1;
1276 }
1277 }
1278 list_add(object_list, object);
1279#if USE_RD_CLIENT
1280 lwm2m_rd_client_set_update_rd();
1281#endif
1282 return 1;
1283}
1284/*---------------------------------------------------------------------------*/
1285void
1286lwm2m_engine_remove_object(lwm2m_object_instance_t *object)
1287{
1288 list_remove(object_list, object);
1289#if USE_RD_CLIENT
1290 lwm2m_rd_client_set_update_rd();
1291#endif
1292}
1293/*---------------------------------------------------------------------------*/
1294int
1295lwm2m_engine_add_generic_object(lwm2m_object_t *object)
1296{
1297 if(object == NULL || object->impl == NULL
1298 || object->impl->get_first == NULL
1299 || object->impl->get_next == NULL
1300 || object->impl->get_by_id == NULL) {
1301 LOG_WARN("failed to register NULL object\n");
1302 return 0;
1303 }
1304 if(get_object(object->impl->object_id) != NULL) {
1305 /* A generic object with this id has already been registered */
1306 LOG_WARN("object with id %u already registered\n",
1307 object->impl->object_id);
1308 return 0;
1309 }
1310 if(has_non_generic_object(object->impl->object_id)) {
1311 /* An object with this id has already been registered */
1312 LOG_WARN("object with id %u already registered\n",
1313 object->impl->object_id);
1314 return 0;
1315 }
1316 list_add(generic_object_list, object);
1317
1318#if USE_RD_CLIENT
1319 lwm2m_rd_client_set_update_rd();
1320#endif
1321
1322 return 1;
1323}
1324/*---------------------------------------------------------------------------*/
1325void
1326lwm2m_engine_remove_generic_object(lwm2m_object_t *object)
1327{
1328 list_remove(generic_object_list, object);
1329#if USE_RD_CLIENT
1330 lwm2m_rd_client_set_update_rd();
1331#endif
1332}
1333/*---------------------------------------------------------------------------*/
1334static lwm2m_object_instance_t *
1335next_object_instance(const lwm2m_context_t *context, lwm2m_object_t *object,
1336 lwm2m_object_instance_t *last)
1337{
1338 if(context != NULL && context->level >= 2) {
1339 /* Only single instance */
1340 return NULL;
1341 }
1342
1343 /* There has to be a last to get a next */
1344 if(last == NULL) {
1345 return NULL;
1346 }
1347
1348 if(object == NULL) {
1349 for(last = last->next; last != NULL; last = last->next) {
1350 /* if no context is given - this will just give the next object */
1351 if(context == NULL || last->object_id == context->object_id) {
1352 return last;
1353 }
1354 }
1355 return NULL;
1356 }
1357 return object->impl->get_next(last, NULL);
1358}
1359/*---------------------------------------------------------------------------*/
1360static coap_handler_status_t
1361lwm2m_handler_callback(coap_message_t *request, coap_message_t *response,
1362 uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
1363{
1364 const char *url;
1365 int url_len;
1366 unsigned int format;
1367 unsigned int accept;
1368 int depth;
1369 lwm2m_context_t context;
1370 lwm2m_object_t *object;
1371 lwm2m_object_instance_t *instance;
1372 uint32_t bnum;
1373 uint8_t bmore;
1374 uint16_t bsize;
1375 uint32_t boffset;
1376 lwm2m_status_t success;
1377 lwm2m_buffer_t inbuf;
1378 lwm2m_buffer_t outbuf;
1379
1380 /* Initialize the context */
1381 memset(&context, 0, sizeof(context));
1382 memset(&outbuf, 0, sizeof(outbuf));
1383 memset(&inbuf, 0, sizeof(inbuf));
1384
1385 context.outbuf = &outbuf;
1386 context.inbuf = &inbuf;
1387
1388 /* Set CoAP request/response for now */
1389 context.request = request;
1390 context.response = response;
1391
1392 /* Set out buffer */
1393 context.outbuf->buffer = buffer;
1394 context.outbuf->size = buffer_size;
1395
1396 /* Set input buffer */
1397 if(offset != NULL) {
1398 context.offset = *offset;
1399 }
1400 context.inbuf->size = coap_get_payload(request, (const uint8_t **)&context.inbuf->buffer);
1401 context.inbuf->pos = 0;
1402
1403#if LWM2M_QUEUE_MODE_ENABLED
1404lwm2m_queue_mode_request_received();
1405#endif /* LWM2M_QUEUE_MODE_ENABLED */
1406
1407 /* Maybe this should be part of CoAP itself - this seems not to be working
1408 with the leshan server */
1409#define LWM2M_CONF_ENTITY_TOO_LARGE_BLOCK1 0
1410#if LWM2M_CONF_ENTITY_TOO_LARGE_BLOCK1
1411 if(coap_is_option(request, COAP_OPTION_BLOCK1)) {
1412 uint16_t bsize;
1413 coap_get_header_block1(request, NULL, NULL, &bsize, NULL);
1414
1415 LOG_DBG("Block1 size:%d\n", bsize);
1416 if(bsize > COAP_MAX_BLOCK_SIZE) {
1417 LOG_WARN("Entity too large: %u...\n", bsize);
1418 coap_set_status_code(response, REQUEST_ENTITY_TOO_LARGE_4_13);
1419 coap_set_header_size1(response, COAP_MAX_BLOCK_SIZE);
1420 return COAP_HANDLER_STATUS_PROCESSED;
1421 }
1422 }
1423#endif
1424
1425 /* Set default reader/writer */
1426 context.reader = &lwm2m_plain_text_reader;
1427 context.writer = &lwm2m_tlv_writer;
1428
1429
1430 url_len = coap_get_header_uri_path(request, &url);
1431
1432 if(url_len == 2 && strncmp("bs", url, 2) == 0) {
1433 LOG_INFO("BOOTSTRAPPED!!!\n");
1434 coap_set_status_code(response, CHANGED_2_04);
1435 return COAP_HANDLER_STATUS_PROCESSED;
1436 }
1437
1438 depth = lwm2m_engine_parse_context(url, url_len, request, response,
1439 buffer, buffer_size, &context);
1440 if(depth < 0) {
1441 /* Not a LWM2M context */
1442 return COAP_HANDLER_STATUS_CONTINUE;
1443 }
1444
1445 LOG_DBG("%s URL:'", get_method_as_string(coap_get_method_type(request)));
1446 LOG_DBG_COAP_STRING(url, url_len);
1447 LOG_DBG_("' CTX:%u/%u/%u dp:%u bs:%d\n", context.object_id, context.object_instance_id,
1448 context.resource_id, depth, buffer_size);
1449 /* Get format and accept */
1450 if(!coap_get_header_content_format(request, &format)) {
1451 LOG_DBG("No format given. Assume text plain...\n");
1452 format = TEXT_PLAIN;
1453 } else if(format == LWM2M_TEXT_PLAIN) {
1454 /* CoAP content format text plain - assume LWM2M text plain */
1455 format = TEXT_PLAIN;
1456 }
1457 if(!coap_get_header_accept(request, &accept)) {
1458 if(format == TEXT_PLAIN && depth < 3) {
1459 LOG_DBG("No Accept header, assume JSON\n");
1460 accept = LWM2M_JSON;
1461 } else {
1462 LOG_DBG("No Accept header, using same as content-format: %d\n", format);
1463 accept = format;
1464 }
1465 }
1466
1467 /*
1468 * 1 => Object only
1469 * 2 => Object and Instance
1470 * 3 => Object and Instance and Resource
1471 */
1472 if(depth < 1) {
1473 /* No possible object id found in URL - ignore request unless delete all */
1474 if(coap_get_method_type(request) == METHOD_DELETE) {
1475 LOG_DBG("This is a delete all - for bootstrap...\n");
1476 context.operation = LWM2M_OP_DELETE;
1477 coap_set_status_code(response, DELETED_2_02);
1478
1479 /* Delete all dynamic objects that can be deleted */
1480 for(object = list_head(generic_object_list);
1481 object != NULL;
1482 object = object->next) {
1483 if(object->impl != NULL && object->impl->delete_instance != NULL) {
1484 object->impl->delete_instance(LWM2M_OBJECT_INSTANCE_NONE, NULL);
1485 }
1486 }
1487#if USE_RD_CLIENT
1488 lwm2m_rd_client_set_update_rd();
1489#endif
1490 return COAP_HANDLER_STATUS_PROCESSED;
1491 }
1492 return COAP_HANDLER_STATUS_CONTINUE;
1493 }
1494
1495 instance = get_instance_by_context(&context, &object);
1496
1497 /*
1498 * Check if we found either instance or object. Instance means we found an
1499 * existing instance and generic objects means we might create an instance.
1500 */
1501 if(instance == NULL && object == NULL) {
1502 /* No matching object/instance found - ignore request */
1503 return COAP_HANDLER_STATUS_CONTINUE;
1504 }
1505
1506 LOG_INFO("Context: %u/%u/%u found: %d\n",
1507 context.object_id, context.object_instance_id,
1508 context.resource_id, depth);
1509
1510 /*
1511 * Select reader and writer based on provided Content type and
1512 * Accept headers.
1513 */
1514 lwm2m_engine_select_reader(&context, format);
1515 lwm2m_engine_select_writer(&context, accept);
1516
1517 switch(coap_get_method_type(request)) {
1518 case METHOD_PUT:
1519 /* can also be write atts */
1520 context.operation = LWM2M_OP_WRITE;
1521 coap_set_status_code(response, CHANGED_2_04);
1522 break;
1523 case METHOD_POST:
1524 if(context.level < 2) {
1525 /* write to a instance */
1526 context.operation = LWM2M_OP_WRITE;
1527 coap_set_status_code(response, CHANGED_2_04);
1528 } else if(context.level == 3) {
1529 context.operation = LWM2M_OP_EXECUTE;
1530 coap_set_status_code(response, CHANGED_2_04);
1531 }
1532 break;
1533 case METHOD_GET:
1534 if(accept == APPLICATION_LINK_FORMAT) {
1535 context.operation = LWM2M_OP_DISCOVER;
1536 } else {
1537 context.operation = LWM2M_OP_READ;
1538 }
1539 coap_set_status_code(response, CONTENT_2_05);
1540 break;
1541 case METHOD_DELETE:
1542 context.operation = LWM2M_OP_DELETE;
1543 coap_set_status_code(response, DELETED_2_02);
1544 break;
1545 default:
1546 break;
1547 }
1548
1549 if(LOG_DBG_ENABLED) {
1550 /* for debugging */
1551 LOG_DBG("[");
1552 LOG_DBG_COAP_STRING(url, url_len);
1553 LOG_DBG_("] %s Format:%d ID:%d bsize:%u offset:%"PRId32"\n",
1554 get_method_as_string(coap_get_method_type(request)),
1555 format, context.object_id, buffer_size,
1556 offset != NULL ? *offset : 0);
1557 if(format == TEXT_PLAIN) {
1558 /* a string */
1559 const uint8_t *data;
1560 int plen = coap_get_payload(request, &data);
1561 if(plen > 0) {
1562 LOG_DBG("Data: '");
1563 LOG_DBG_COAP_STRING((const char *)data, plen);
1564 LOG_DBG_("'\n");
1565 }
1566 }
1567 }
1568
1569 /* PUT/POST - e.g. write will not send in offset here - Maybe in the future? */
1570 if((offset != NULL && *offset == 0) &&
1571 coap_is_option(request, COAP_OPTION_BLOCK1)) {
1572 coap_get_header_block1(request, &bnum, &bmore, &bsize, &boffset);
1573 context.offset = boffset;
1574 }
1575
1576 /* This is a discovery operation */
1577 switch(context.operation) {
1578 case LWM2M_OP_DISCOVER:
1579 /* Assume only one disco at a time... */
1580 success = perform_multi_resource_read_op(object, instance, &context);
1581 break;
1582 case LWM2M_OP_READ:
1583 success = perform_multi_resource_read_op(object, instance, &context);
1584 break;
1585 case LWM2M_OP_WRITE:
1586 success = perform_multi_resource_write_op(object, instance, &context, format);
1587 break;
1588 case LWM2M_OP_EXECUTE:
1589 success = call_instance(instance, &context);
1590 break;
1591 case LWM2M_OP_DELETE:
1592 if(object != NULL && object->impl != NULL &&
1593 object->impl->delete_instance != NULL) {
1594 object->impl->delete_instance(context.object_instance_id, &success);
1595#if USE_RD_CLIENT
1596 lwm2m_rd_client_set_update_rd();
1597#endif
1598 } else {
1599 success = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1600 }
1601 break;
1602 default:
1603 success = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1604 break;
1605 }
1606
1607 if(success == LWM2M_STATUS_OK) {
1608 /* Handle blockwise 1 */
1609 if(coap_is_option(request, COAP_OPTION_BLOCK1)) {
1610 LOG_DBG("Setting BLOCK 1 num:%"PRIu32" o2:%"PRIu32" o:%"PRId32"\n", bnum, boffset,
1611 (offset != NULL ? *offset : 0));
1612 coap_set_header_block1(response, bnum, 0, bsize);
1613 }
1614
1615 if(context.outbuf->len > 0) {
1616 LOG_DBG("[");
1617 LOG_DBG_COAP_STRING(url, url_len);
1618 LOG_DBG_("] replying with %u bytes\n", context.outbuf->len);
1619 coap_set_payload(response, context.outbuf->buffer, context.outbuf->len);
1620 coap_set_header_content_format(response, context.content_type);
1621
1622 if(offset != NULL) {
1623 LOG_DBG("Setting new offset: oo %"PRIu32
1624 ", no: %"PRIu32"\n", *offset, context.offset);
1625 if(context.writer_flags & WRITER_HAS_MORE) {
1626 *offset = context.offset;
1627 } else {
1628 /* this signals to CoAP that there is no more CoAP messages to expect */
1629 *offset = -1;
1630 }
1631 }
1632 } else {
1633 LOG_DBG("[");
1634 LOG_DBG_COAP_STRING(url, url_len);
1635 LOG_DBG_("] no data in reply\n");
1636 }
1637 } else {
1638 switch(success) {
1639 case LWM2M_STATUS_FORBIDDEN:
1640 coap_set_status_code(response, FORBIDDEN_4_03);
1641 break;
1642 case LWM2M_STATUS_NOT_FOUND:
1643 coap_set_status_code(response, NOT_FOUND_4_04);
1644 break;
1645 case LWM2M_STATUS_OPERATION_NOT_ALLOWED:
1646 coap_set_status_code(response, METHOD_NOT_ALLOWED_4_05);
1647 break;
1648 case LWM2M_STATUS_NOT_ACCEPTABLE:
1649 coap_set_status_code(response, NOT_ACCEPTABLE_4_06);
1650 break;
1651 case LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT:
1652 coap_set_status_code(response, UNSUPPORTED_MEDIA_TYPE_4_15);
1653 break;
1654 default:
1655 /* Failed to handle the request */
1656 coap_set_status_code(response, INTERNAL_SERVER_ERROR_5_00);
1657 break;
1658 }
1659 LOG_WARN("[");
1660 LOG_WARN_COAP_STRING(url, url_len);
1661 LOG_WARN("] resource failed: %s\n", get_status_as_string(success));
1662 }
1663 return COAP_HANDLER_STATUS_PROCESSED;
1664}
1665/*---------------------------------------------------------------------------*/
1666static void
1667lwm2m_send_notification(char* path)
1668{
1669#if LWM2M_QUEUE_MODE_ENABLED && LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION
1670 if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
1671 lwm2m_queue_mode_set_handler_from_notification();
1672 }
1673#endif
1674 coap_notify_observers_sub(NULL, path);
1675}
1676/*---------------------------------------------------------------------------*/
1677void
1678lwm2m_notify_object_observers(lwm2m_object_instance_t *obj,
1679 uint16_t resource)
1680{
1681 char path[20]; /* 60000/60000/60000 */
1682 if(obj != NULL) {
1683 snprintf(path, 20, "%d/%d/%d", obj->object_id, obj->instance_id, resource);
1684 }
1685
1686#if LWM2M_QUEUE_MODE_ENABLED
1687
1688 if(coap_has_observers(path)) {
1689 /* Client is sleeping -> add the notification to the list */
1690 if(!lwm2m_rd_client_is_client_awake()) {
1691 lwm2m_notification_queue_add_notification_path(obj->object_id, obj->instance_id, resource);
1692
1693 /* if it is the first notification -> wake up and send update */
1694 if(!lwm2m_queue_mode_is_waked_up_by_notification()) {
1695 lwm2m_queue_mode_set_waked_up_by_notification();
1696 lwm2m_rd_client_fsm_execute_queue_mode_update();
1697 }
1698 /* Client is awake -> send the notification */
1699 } else {
1700 lwm2m_send_notification(path);
1701 }
1702 }
1703#else
1704 lwm2m_send_notification(path);
1705#endif
1706}
1707/*---------------------------------------------------------------------------*/
1708/** @} */
Default definitions of C compiler quirk work-arounds.
Collection of constants specified in the CoAP standard.
CoAP engine implementation.
Log support for CoAP.
static radio_result_t get_object(radio_param_t param, void *dest, size_t size)
Definition cc2538-rf.c:1069
static volatile uint64_t count
Num.
Definition clock.c:50
static uint64_t coap_timer_uptime(void)
Get the time since boot in milliseconds.
Definition coap-timer.h:83
coap_resource_flags_t
Resource flags for allowed methods and special functionalities.
static void list_init(list_t list)
Initialize a list.
Definition list.h:152
#define LIST(name)
Declare a linked list.
Definition list.h:90
void list_add(list_t list, void *item)
Add an item at the end of a list.
Definition list.c:71
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
Definition list.c:134
static void * list_head(const_list_t list)
Get a pointer to the first element of a list.
Definition list.h:169
static uint8_t accept(uint8_t in)
Definition mpl.c:1390
#define ADDR_TENTATIVE
Possible states for the an address (RFC 4862).
Definition uip-ds6.h:156
uip_ds6_netif_t uip_ds6_if
The single interface.
Definition uip-ds6.c:74
Linked list manipulation routines.
Header file for the Contiki OMA LWM2M device.
Header file for the Contiki OMA LWM2M engine.
Header file for the Contiki OMA LWM2M JSON writer.
Header file for functions to manage the queue to store notifications when waiting for the response to...
Header file for the LWM2M object API.
Header file for the Contiki OMA LWM2M plain text reader / writer.
Header file for the Contiki OMA LWM2M Queue Mode object to manage the parameters from the server side...
Header file for the Contiki OMA LWM2M Queue Mode implementation to manage the parameters.
Header file for the Contiki OMA LWM2M Registration and Bootstrap Client.
Header file for the Contiki OMA LWM2M TLV reader.
Header file for the Contiki OMA LWM2M TLV writer.
Header file for the Contiki OMA LWM2M TLV.
Header file for IPv6-related data structures.
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Definition uip-nd6.c:116