60#ifndef LWM2M_ENGINE_CLIENT_ENDPOINT_NAME
66#define LOG_MODULE "lwm2m-eng"
67#define LOG_LEVEL LOG_LEVEL_LWM2M
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
73#define LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX "Contiki-NG"
77#ifdef LWM2M_ENGINE_CONF_USE_RD_CLIENT
78#define USE_RD_CLIENT LWM2M_ENGINE_CONF_USE_RD_CLIENT
80#define USE_RD_CLIENT 1
84#if LWM2M_QUEUE_MODE_ENABLED
86#define USE_RD_CLIENT 1
93#if LWM2M_QUEUE_MODE_ENABLED
96#if LWM2M_QUEUE_MODE_OBJECT_ENABLED
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)
108#define NO_INSTANCE 0xffffffff
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
121static lwm2m_object_instance_t instance_buffer;
124static uint16_t lwm2m_buf_lock[4];
125static uint64_t lwm2m_buf_lock_timeout = 0;
127static lwm2m_write_opaque_callback current_opaque_callback;
128static int current_opaque_offset = 0;
130static coap_handler_status_t lwm2m_handler_callback(coap_message_t *request,
131 coap_message_t *response,
133 uint16_t buffer_size,
135static lwm2m_object_instance_t *
136next_object_instance(
const lwm2m_context_t *context, lwm2m_object_t *
object, lwm2m_object_instance_t *last);
140 uint16_t instance_id;
142 uint8_t token[COAP_TOKEN_LEN];
146COAP_HANDLER(lwm2m_handler, lwm2m_handler_callback);
148LIST(generic_object_list);
151static lwm2m_object_t *
154 lwm2m_object_t *object;
155 for(
object =
list_head(generic_object_list);
157 object =
object->next) {
158 if(object->impl && object->impl->object_id == object_id) {
166has_non_generic_object(uint16_t object_id)
168 lwm2m_object_instance_t *instance;
171 instance = instance->next) {
172 if(instance->object_id == object_id) {
180static lwm2m_object_instance_t *
181get_instance(uint16_t object_id, uint16_t instance_id, lwm2m_object_t **o)
183 lwm2m_object_instance_t *instance;
184 lwm2m_object_t *object;
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) {
206 if(instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
207 return object->impl->get_first(NULL);
209 return object->impl->get_by_id(instance_id, NULL);
215static lwm2m_object_instance_t *
216get_instance_by_context(
const lwm2m_context_t *context, lwm2m_object_t **o)
218 if(context->level < 2) {
219 return get_instance(context->object_id, LWM2M_OBJECT_INSTANCE_NONE, o);
221 return get_instance(context->object_id, context->object_instance_id, o);
225call_instance(lwm2m_object_instance_t *instance, lwm2m_context_t *context)
227 if(context->level < 3) {
228 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
231 if(instance == NULL) {
233 return LWM2M_STATUS_NOT_FOUND;
236 if(instance->callback == NULL) {
237 return LWM2M_STATUS_ERROR;
240 return instance->callback(instance, context);
248double_buffer_flush(lwm2m_buffer_t *ctxbuf, lwm2m_buffer_t *outbuf,
int size)
253 if(ctxbuf->len < size) {
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],
269static inline const char *
272 if(method == METHOD_GET) {
274 }
else if(method == METHOD_POST) {
276 }
else if(method == METHOD_PUT) {
278 }
else if(method == METHOD_DELETE) {
286get_status_as_string(lwm2m_status_t status)
288 static char buffer[14];
290 case LWM2M_STATUS_OK:
292 case LWM2M_STATUS_ERROR:
294 case LWM2M_STATUS_WRITE_ERROR:
295 return "WRITE ERROR";
296 case LWM2M_STATUS_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:
304 case LWM2M_STATUS_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";
317 snprintf(buffer,
sizeof(buffer) - 1,
"<%u>", status);
323parse_path(
const char *path,
int path_len,
324 uint16_t *oid, uint16_t *iid, uint16_t *rid)
332 LOG_DBG(
"parse PATH: \"");
333 LOG_DBG_COAP_STRING(path, path_len);
341 while(pos < path_len && (c = path[pos]) >=
'0' && c <=
'9') {
342 val = val * 10 + (c -
'0');
346 if(c ==
'/' || pos == path_len) {
348 if(ret == 0) *oid = val;
349 if(ret == 1) *iid = val;
350 if(ret == 2) *rid = val;
357 }
while(pos < path_len);
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)
368 if(context == NULL || path == NULL) {
372 ret = parse_path(path, path_len, &context->object_id,
373 &context->object_instance_id, &context->resource_id);
376 context->level = ret;
383void lwm2m_engine_set_opaque_callback(lwm2m_context_t *ctx, lwm2m_write_opaque_callback cb)
387 LOG_DBG(
"Setting opaque handler - offset: %"PRIu32
",%d\n",
388 ctx->offset, ctx->outbuf->len);
390 current_opaque_offset = 0;
391 current_opaque_callback = cb;
395lwm2m_engine_set_rd_data(lwm2m_buffer_t *outbuf,
int block)
398 static lwm2m_object_t *object;
399 static lwm2m_object_instance_t *instance;
402 int maxsize = outbuf->size;
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]);
414 LOG_DBG(
"Starting RD generation\n");
419 if(instance == NULL) {
426 if(object->impl != NULL) {
427 instance =
object->impl->get_first(NULL);
431 lwm2m_buf_lock[0] = 1;
432 lwm2m_buf_lock[1] = 0xffff;
433 lwm2m_buf_lock[2] = 0xffff;
434 lwm2m_buf_lock[3] = 0xffff;
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);
460 lwm2m_buf.len += len;
461 if(instance != NULL) {
462 instance = next_object_instance(NULL,
object, instance);
465 if(instance == NULL) {
476 object =
object->next;
479 if(
object != NULL && object->impl != NULL) {
480 instance =
object->impl->get_first(NULL);
483 if(instance == NULL &&
object == NULL && lwm2m_buf.len <= maxsize) {
489 if(lwm2m_buf.len >= maxsize) {
491 LOG_DBG(
"**** CoAP MAX BLOCK Reached!!! **** SEND\n");
494 double_buffer_flush(&lwm2m_buf, outbuf, maxsize);
500 double_buffer_flush(&lwm2m_buf, outbuf, maxsize);
502 lwm2m_buf_lock[0] = 0;
507lwm2m_engine_init(
void)
512#ifdef LWM2M_ENGINE_CLIENT_ENDPOINT_NAME
513 const char *endpoint = LWM2M_ENGINE_CLIENT_ENDPOINT_NAME;
516 static char endpoint[32];
521 len = strlen(LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX);
523 if(len >
sizeof(endpoint) - 13) {
524 len =
sizeof(endpoint) - 13;
527 for(i = 0; i < len; i++) {
528 if(LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX[i] ==
' ') {
531 endpoint[i] = LWM2M_ENGINE_CLIENT_ENDPOINT_PREFIX[i];
536 for(i = 0; i < UIP_DS6_ADDR_NB; i++) {
546 for(i = 0; i < 6; i++) {
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);
565 coap_add_handler(&lwm2m_handler);
568 lwm2m_rd_client_init(endpoint);
571#if LWM2M_QUEUE_MODE_ENABLED && LWM2M_QUEUE_MODE_OBJECT_ENABLED
572 lwm2m_queue_mode_object_init();
585lwm2m_engine_select_writer(lwm2m_context_t *context,
unsigned int accept)
590 context->writer = &lwm2m_tlv_writer;
592 case LWM2M_TEXT_PLAIN:
594 context->writer = &lwm2m_plain_text_writer;
598 case APPLICATION_JSON:
599 context->writer = &lwm2m_json_writer;
602 LOG_WARN(
"Unknown Accept type %u, using LWM2M plain text\n",
accept);
603 context->writer = &lwm2m_plain_text_writer;
605 accept = LWM2M_TEXT_PLAIN;
608 context->content_type =
accept;
619lwm2m_engine_select_reader(lwm2m_context_t *context,
unsigned int content_format)
621 switch(content_format) {
624 context->reader = &lwm2m_tlv_reader;
628 context->reader = &lwm2m_plain_text_reader;
630 case LWM2M_TEXT_PLAIN:
632 context->reader = &lwm2m_plain_text_reader;
635 LOG_WARN(
"Unknown content type %u, using LWM2M plain text\n",
637 context->reader = &lwm2m_plain_text_reader;
645static uint32_t last_instance_id = NO_INSTANCE;
646static int last_rsc_pos;
650perform_multi_resource_read_op(lwm2m_object_t *
object,
651 lwm2m_object_instance_t *instance,
652 lwm2m_context_t *ctx)
654 int size = ctx->outbuf->size;
656 uint8_t initialized = 0;
657 uint8_t num_read = 0;
658 lwm2m_buffer_t *outbuf;
660 if(instance == NULL) {
662 return LWM2M_STATUS_NOT_FOUND;
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;
674 outbuf = ctx->outbuf;
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;
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);
692 ctx->outbuf = &lwm2m_buf;
694 if(ctx->offset == 0) {
697 ((uint32_t)instance->object_id << 16) | instance->instance_id;
700 current_opaque_callback = NULL;
702 lwm2m_buf_lock[0] = 1;
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;
710 instance = get_instance(last_instance_id >> 16, last_instance_id & 0xffff,
715 ctx->writer_flags |= WRITER_OUTPUT_VALUE;
716 if(instance == NULL) {
718 ctx->outbuf->buffer[0] =
' ';
723 while(instance != NULL) {
725 if(instance->resource_ids != NULL && instance->resource_count > 0) {
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);
734 if(ctx->level < 3 || ctx->resource_id == RSC_ID(instance->resource_ids[last_rsc_pos])) {
738 if(ctx->operation == LWM2M_OP_DISCOVER) {
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);
753 ctx->outbuf->len += len;
754 if(len < 0 || ctx->outbuf->len >= size) {
755 double_buffer_flush(ctx->outbuf, outbuf, size);
757 LOG_DBG(
"Copied lwm2m buf - remaining: %d\n", lwm2m_buf.len);
759 ctx->outbuf = outbuf;
760 ctx->writer_flags |= WRITER_HAS_MORE;
762 return LWM2M_STATUS_OK;
765 }
else if(ctx->operation == LWM2M_OP_READ) {
766 lwm2m_status_t success = 0;
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;
778 ctx->resource_id = RSC_ID(instance->resource_ids[last_rsc_pos]);
781 ctx->object_instance_id = instance->instance_id;
784 if(RSC_READABLE(instance->resource_ids[last_rsc_pos])) {
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);
794 if(current_opaque_callback == NULL) {
795 LOG_DBG(
"Doing the callback to the resource %d\n", ctx->outbuf->len);
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));
801 if(success != LWM2M_STATUS_OK) {
803 LOG_DBG(
"Callback failed: %s\n", get_status_as_string(success));
805 if(success == LWM2M_STATUS_NOT_FOUND) {
809 lwm2m_buf_lock[0] = 0;
813 lwm2m_buf_lock[0] = 0;
818 if(current_opaque_callback != NULL) {
819 uint32_t old_offset = ctx->offset;
820 int num_write = COAP_MAX_BLOCK_SIZE - ctx->outbuf->len;
824 ctx->offset = current_opaque_offset;
826 success = current_opaque_callback(instance, ctx, num_write);
827 if((ctx->writer_flags & WRITER_HAS_MORE) == 0) {
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;
835 current_opaque_offset += num_write;
836 ctx->offset = old_offset;
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));
850 LOG_DBG(
"Resource %u not readable\n",
851 RSC_ID(instance->resource_ids[last_rsc_pos]));
855 if(current_opaque_callback == NULL) {
859 LOG_DBG(
"Opaque is set - continue with that.\n");
862 if(ctx->outbuf->len >= COAP_MAX_BLOCK_SIZE) {
863 LOG_DBG(
"**** CoAP MAX BLOCK Reached!!! **** SEND\n");
866 if(ctx->outbuf->len < 2 * COAP_MAX_BLOCK_SIZE) {
868 double_buffer_flush(ctx->outbuf, outbuf, size);
870 LOG_DBG(
"Copied lwm2m buf - remaining: %d\n", lwm2m_buf.len);
872 ctx->outbuf = outbuf;
873 ctx->writer_flags |= WRITER_HAS_MORE;
876 return LWM2M_STATUS_OK;
878 LOG_WARN(
"*** ERROR Overflow?\n");
879 return LWM2M_STATUS_ERROR;
884 instance = next_object_instance(ctx,
object, instance);
885 if(instance != NULL) {
887 ((uint32_t)instance->object_id << 16) | instance->instance_id;
889 last_instance_id = NO_INSTANCE;
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);
903 if(num_read == 0 && ctx->level == 3) {
904 lwm2m_buf_lock[0] = 0;
905 return LWM2M_STATUS_NOT_FOUND;
909 len = double_buffer_flush(ctx->outbuf, outbuf, size);
910 ctx->outbuf = outbuf;
915 if(lwm2m_buf.len > 0) {
916 ctx->writer_flags |= WRITER_HAS_MORE;
919 lwm2m_buf_lock[0] = 0;
922 LOG_DBG(
"At END: Copied lwm2m buf %d\n", len);
924 return LWM2M_STATUS_OK;
927static lwm2m_object_instance_t *
928create_instance(lwm2m_context_t *context, lwm2m_object_t *
object)
930 lwm2m_object_instance_t *instance;
931 if(
object == NULL || object->impl == NULL ||
932 object->impl->create_instance == NULL) {
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);
942 lwm2m_rd_client_set_update_rd();
949#define MODE_INSTANCE 1
953static lwm2m_object_instance_t *
954get_or_create_instance(lwm2m_context_t *ctx, lwm2m_object_t *
object,
957 lwm2m_object_instance_t *instance;
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);
964 *c = LWM2M_OBJECT_INSTANCE_NONE;
966 if(instance == NULL) {
967 instance = create_instance(ctx,
object);
968 if(instance != NULL) {
970 *c = instance->instance_id;
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);
982check_write(lwm2m_context_t *ctx, lwm2m_object_instance_t *instance,
int rid)
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])) {
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) {
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);
1019static lwm2m_status_t
1020process_tlv_write(lwm2m_context_t *ctx, lwm2m_object_t *
object,
1021 int rid, uint8_t *data,
int len)
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;
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);
1037 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1040 return LWM2M_STATUS_ERROR;
1043static int last_tlv_id = 0;
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)
1051 uint16_t oid = 0, iid = 0, rid = 0;
1058 uint16_t created = LWM2M_OBJECT_INSTANCE_NONE;
1061 inbuf = ctx->inbuf->buffer;
1062 inpos = ctx->inbuf->pos;
1063 insize = ctx->inbuf->size;
1065 if(format == LWM2M_JSON || format == LWM2M_OLD_JSON) {
1066 struct json_data json;
1068 while(lwm2m_json_next_token(ctx, &json)) {
1070 for(i = 0; i < json.name_len; i++) {
1071 LOG_DBG_(
"%c", json.name[i]);
1074 for(i = 0; i < json.value_len; i++) {
1075 LOG_DBG_(
"%c", json.value[i]);
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);
1082 if(ctx->level == 1) {
1084 ctx->object_instance_id = oid;
1085 ctx->resource_id = iid;
1087 instance = get_or_create_instance(ctx,
object, &created);
1089 if(instance != NULL && instance->callback != NULL) {
1090 mode |= MODE_INSTANCE;
1093 return LWM2M_STATUS_ERROR;
1100 inbuf = ctx->inbuf->buffer;
1101 inpos = ctx->inbuf->pos;
1103 ctx->inbuf->buffer = json.value;
1104 ctx->inbuf->pos = 0;
1105 ctx->inbuf->size = json.value_len;
1108 if(mode == MODE_READY) {
1110 if(!check_write(ctx, instance, ctx->resource_id)) {
1111 return LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1113 if(instance->callback(instance, ctx) != LWM2M_STATUS_OK) {
1117 ctx->inbuf->buffer = inbuf;
1118 ctx->inbuf->pos = inpos;
1119 ctx->inbuf->size = insize;
1123 }
else if(format == LWM2M_TLV || format == LWM2M_OLD_TLV) {
1127 lwm2m_status_t status;
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));
1150 status = process_tlv_write(ctx,
object, last_tlv_id,
1151 inbuf, MIN(size, insize));
1156 while(tlvpos < insize) {
1157 len = lwm2m_tlv_read(&tlv, &inbuf[tlvpos], insize - tlvpos);
1160 LOG_WARN(
"Failed to read TLV at offset %d/%d\n",
1161 (
int)tlvpos, (
int)insize);
1162 return LWM2M_STATUS_BAD_REQUEST;
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) {
1171 ctx->object_instance_id = tlv.id;
1172 if(tlv.length == 0) {
1174 if((instance = create_instance(ctx,
object)) == NULL) {
1175 return LWM2M_STATUS_ERROR;
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) {
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) {
1198 coap_set_status_code(ctx->response, CHANGED_2_04);
1202 }
else if(format == LWM2M_TEXT_PLAIN ||
1203 format == TEXT_PLAIN ||
1204 format == LWM2M_OLD_OPAQUE) {
1205 return call_instance(instance, ctx);
1209 return LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT;
1213 return LWM2M_STATUS_OK;
1216lwm2m_object_instance_t *
1217lwm2m_engine_get_instance_buffer(
void)
1219 return &instance_buffer;
1223lwm2m_engine_has_instance(uint16_t object_id, uint16_t instance_id)
1225 return get_instance(object_id, instance_id, NULL) != NULL;
1229lwm2m_engine_add_object(lwm2m_object_instance_t *
object)
1231 lwm2m_object_instance_t *instance;
1232 uint16_t min_id = 0xffff;
1233 uint16_t max_id = 0;
1236 if(
object == NULL || object->callback == NULL) {
1238 LOG_DBG(
"failed to register NULL object\n");
1243 LOG_DBG(
"object with id %u already registered\n", object->object_id);
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);
1258 if(instance->instance_id > max_id) {
1259 max_id = instance->instance_id;
1261 if(instance->instance_id < min_id) {
1262 min_id = instance->instance_id;
1267 if(object->instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
1271 object->instance_id = 0;
1272 }
else if(min_id > 0) {
1273 object->instance_id = min_id - 1;
1275 object->instance_id = max_id + 1;
1280 lwm2m_rd_client_set_update_rd();
1286lwm2m_engine_remove_object(lwm2m_object_instance_t *
object)
1290 lwm2m_rd_client_set_update_rd();
1295lwm2m_engine_add_generic_object(lwm2m_object_t *
object)
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");
1304 if(
get_object(object->impl->object_id) != NULL) {
1306 LOG_WARN(
"object with id %u already registered\n",
1307 object->impl->object_id);
1310 if(has_non_generic_object(object->impl->object_id)) {
1312 LOG_WARN(
"object with id %u already registered\n",
1313 object->impl->object_id);
1316 list_add(generic_object_list,
object);
1319 lwm2m_rd_client_set_update_rd();
1326lwm2m_engine_remove_generic_object(lwm2m_object_t *
object)
1330 lwm2m_rd_client_set_update_rd();
1334static lwm2m_object_instance_t *
1335next_object_instance(
const lwm2m_context_t *context, lwm2m_object_t *
object,
1336 lwm2m_object_instance_t *last)
1338 if(context != NULL && context->level >= 2) {
1348 if(
object == NULL) {
1349 for(last = last->next; last != NULL; last = last->next) {
1351 if(context == NULL || last->object_id == context->object_id) {
1357 return object->impl->get_next(last, NULL);
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)
1366 unsigned int format;
1369 lwm2m_context_t context;
1370 lwm2m_object_t *object;
1371 lwm2m_object_instance_t *instance;
1376 lwm2m_status_t success;
1377 lwm2m_buffer_t inbuf;
1378 lwm2m_buffer_t outbuf;
1381 memset(&context, 0,
sizeof(context));
1382 memset(&outbuf, 0,
sizeof(outbuf));
1383 memset(&inbuf, 0,
sizeof(inbuf));
1385 context.outbuf = &outbuf;
1386 context.inbuf = &inbuf;
1389 context.request = request;
1390 context.response = response;
1393 context.outbuf->buffer = buffer;
1394 context.outbuf->size = buffer_size;
1397 if(offset != NULL) {
1398 context.offset = *offset;
1400 context.inbuf->size = coap_get_payload(request, (
const uint8_t **)&context.inbuf->buffer);
1401 context.inbuf->pos = 0;
1403#if LWM2M_QUEUE_MODE_ENABLED
1404lwm2m_queue_mode_request_received();
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)) {
1413 coap_get_header_block1(request, NULL, NULL, &bsize, NULL);
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;
1426 context.reader = &lwm2m_plain_text_reader;
1427 context.writer = &lwm2m_tlv_writer;
1430 url_len = coap_get_header_uri_path(request, &url);
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;
1438 depth = lwm2m_engine_parse_context(url, url_len, request, response,
1439 buffer, buffer_size, &context);
1442 return COAP_HANDLER_STATUS_CONTINUE;
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);
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) {
1455 format = TEXT_PLAIN;
1457 if(!coap_get_header_accept(request, &
accept)) {
1458 if(format == TEXT_PLAIN && depth < 3) {
1459 LOG_DBG(
"No Accept header, assume JSON\n");
1462 LOG_DBG(
"No Accept header, using same as content-format: %d\n", format);
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);
1480 for(
object =
list_head(generic_object_list);
1482 object =
object->next) {
1483 if(object->impl != NULL && object->impl->delete_instance != NULL) {
1484 object->impl->delete_instance(LWM2M_OBJECT_INSTANCE_NONE, NULL);
1488 lwm2m_rd_client_set_update_rd();
1490 return COAP_HANDLER_STATUS_PROCESSED;
1492 return COAP_HANDLER_STATUS_CONTINUE;
1495 instance = get_instance_by_context(&context, &
object);
1501 if(instance == NULL &&
object == NULL) {
1503 return COAP_HANDLER_STATUS_CONTINUE;
1506 LOG_INFO(
"Context: %u/%u/%u found: %d\n",
1507 context.object_id, context.object_instance_id,
1508 context.resource_id, depth);
1514 lwm2m_engine_select_reader(&context, format);
1515 lwm2m_engine_select_writer(&context,
accept);
1517 switch(coap_get_method_type(request)) {
1520 context.operation = LWM2M_OP_WRITE;
1521 coap_set_status_code(response, CHANGED_2_04);
1524 if(context.level < 2) {
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);
1534 if(
accept == APPLICATION_LINK_FORMAT) {
1535 context.operation = LWM2M_OP_DISCOVER;
1537 context.operation = LWM2M_OP_READ;
1539 coap_set_status_code(response, CONTENT_2_05);
1542 context.operation = LWM2M_OP_DELETE;
1543 coap_set_status_code(response, DELETED_2_02);
1549 if(LOG_DBG_ENABLED) {
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) {
1559 const uint8_t *data;
1560 int plen = coap_get_payload(request, &data);
1563 LOG_DBG_COAP_STRING((
const char *)data, plen);
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;
1577 switch(context.operation) {
1578 case LWM2M_OP_DISCOVER:
1580 success = perform_multi_resource_read_op(
object, instance, &context);
1583 success = perform_multi_resource_read_op(
object, instance, &context);
1585 case LWM2M_OP_WRITE:
1586 success = perform_multi_resource_write_op(
object, instance, &context, format);
1588 case LWM2M_OP_EXECUTE:
1589 success = call_instance(instance, &context);
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);
1596 lwm2m_rd_client_set_update_rd();
1599 success = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1603 success = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1607 if(success == LWM2M_STATUS_OK) {
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);
1615 if(context.outbuf->len > 0) {
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);
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;
1634 LOG_DBG_COAP_STRING(url, url_len);
1635 LOG_DBG_(
"] no data in reply\n");
1639 case LWM2M_STATUS_FORBIDDEN:
1640 coap_set_status_code(response, FORBIDDEN_4_03);
1642 case LWM2M_STATUS_NOT_FOUND:
1643 coap_set_status_code(response, NOT_FOUND_4_04);
1645 case LWM2M_STATUS_OPERATION_NOT_ALLOWED:
1646 coap_set_status_code(response, METHOD_NOT_ALLOWED_4_05);
1648 case LWM2M_STATUS_NOT_ACCEPTABLE:
1649 coap_set_status_code(response, NOT_ACCEPTABLE_4_06);
1651 case LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT:
1652 coap_set_status_code(response, UNSUPPORTED_MEDIA_TYPE_4_15);
1656 coap_set_status_code(response, INTERNAL_SERVER_ERROR_5_00);
1660 LOG_WARN_COAP_STRING(url, url_len);
1661 LOG_WARN(
"] resource failed: %s\n", get_status_as_string(success));
1663 return COAP_HANDLER_STATUS_PROCESSED;
1667lwm2m_send_notification(
char* path)
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();
1674 coap_notify_observers_sub(NULL, path);
1678lwm2m_notify_object_observers(lwm2m_object_instance_t *obj,
1683 snprintf(path, 20,
"%d/%d/%d", obj->object_id, obj->instance_id, resource);
1686#if LWM2M_QUEUE_MODE_ENABLED
1688 if(coap_has_observers(path)) {
1690 if(!lwm2m_rd_client_is_client_awake()) {
1691 lwm2m_notification_queue_add_notification_path(obj->object_id, obj->instance_id, resource);
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();
1700 lwm2m_send_notification(path);
1704 lwm2m_send_notification(path);
Default definitions of C compiler quirk work-arounds.
Collection of constants specified in the CoAP standard.
CoAP engine implementation.
static radio_result_t get_object(radio_param_t param, void *dest, size_t size)
static volatile uint64_t count
Num.
static uint64_t coap_timer_uptime(void)
Get the time since boot in milliseconds.
coap_resource_flags_t
Resource flags for allowed methods and special functionalities.
static void list_init(list_t list)
Initialize a list.
#define LIST(name)
Declare a linked list.
void list_add(list_t list, void *item)
Add an item at the end of a list.
void list_remove(list_t list, const void *item)
Remove a specific element from a list.
static void * list_head(const_list_t list)
Get a pointer to the first element of a list.
static uint8_t accept(uint8_t in)
#define ADDR_TENTATIVE
Possible states for the an address (RFC 4862).
uip_ds6_netif_t uip_ds6_if
The single interface.
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.