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 *
152get_object(uint16_t object_id)
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) {
201 object = get_object(object_id);
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)
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)
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,
368 if(
context == NULL || path == NULL) {
372 ret = parse_path(path, path_len, &
context->object_id,
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;
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[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));
1146 status = process_tlv_write(ctx,
object, last_tlv_id,
1152 while(tlvpos < insize) {
1153 len = lwm2m_tlv_read(&tlv, &inbuf[tlvpos], insize - tlvpos);
1154 LOG_DBG(
"Got TLV format First is: type:%d id:%d len:%d (p:%d len:%d/%d)\n",
1155 tlv.type, tlv.id, (
int) tlv.length,
1156 (
int) tlvpos, (
int) len, (
int) insize);
1157 if(tlv.type == LWM2M_TLV_TYPE_OBJECT_INSTANCE) {
1161 ctx->object_instance_id = tlv.id;
1162 if(tlv.length == 0) {
1164 if((instance = create_instance(ctx,
object)) == NULL) {
1165 return LWM2M_STATUS_ERROR;
1168 while(pos < tlv.length && (len2 = lwm2m_tlv_read(&tlv2, &tlv.value[pos],
1169 tlv.length - pos))) {
1170 LOG_DBG(
" TLV type:%d id:%d len:%d (len:%d/%d)\n",
1171 tlv2.type, tlv2.id, (
int)tlv2.length,
1172 (
int)len2, (
int)insize);
1173 if(tlv2.type == LWM2M_TLV_TYPE_RESOURCE) {
1174 last_tlv_id = tlv2.id;
1175 status = process_tlv_write(ctx,
object, tlv2.id,
1176 (uint8_t *)&tlv.value[pos], len2);
1177 if(status != LWM2M_STATUS_OK) {
1183 }
else if(tlv.type == LWM2M_TLV_TYPE_RESOURCE) {
1184 status = process_tlv_write(ctx,
object, tlv.id, &inbuf[tlvpos], len);
1185 if(status != LWM2M_STATUS_OK) {
1188 coap_set_status_code(ctx->response, CHANGED_2_04);
1192 }
else if(format == LWM2M_TEXT_PLAIN ||
1193 format == TEXT_PLAIN ||
1194 format == LWM2M_OLD_OPAQUE) {
1195 return call_instance(instance, ctx);
1199 return LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT;
1203 return LWM2M_STATUS_OK;
1206lwm2m_object_instance_t *
1207lwm2m_engine_get_instance_buffer(
void)
1209 return &instance_buffer;
1213lwm2m_engine_has_instance(uint16_t object_id, uint16_t instance_id)
1215 return get_instance(object_id, instance_id, NULL) != NULL;
1219lwm2m_engine_add_object(lwm2m_object_instance_t *
object)
1221 lwm2m_object_instance_t *instance;
1222 uint16_t min_id = 0xffff;
1223 uint16_t max_id = 0;
1226 if(
object == NULL || object->callback == NULL) {
1228 LOG_DBG(
"failed to register NULL object\n");
1231 if(get_object(object->object_id) != NULL) {
1233 LOG_DBG(
"object with id %u already registered\n", object->object_id);
1239 instance = instance->next) {
1240 if(object->object_id == instance->object_id) {
1241 if(object->instance_id == instance->instance_id) {
1242 LOG_DBG(
"object with id %u/%u already registered\n",
1243 instance->object_id, instance->instance_id);
1248 if(instance->instance_id > max_id) {
1249 max_id = instance->instance_id;
1251 if(instance->instance_id < min_id) {
1252 min_id = instance->instance_id;
1257 if(object->instance_id == LWM2M_OBJECT_INSTANCE_NONE) {
1261 object->instance_id = 0;
1262 }
else if(min_id > 0) {
1263 object->instance_id = min_id - 1;
1265 object->instance_id = max_id + 1;
1270 lwm2m_rd_client_set_update_rd();
1276lwm2m_engine_remove_object(lwm2m_object_instance_t *
object)
1280 lwm2m_rd_client_set_update_rd();
1285lwm2m_engine_add_generic_object(lwm2m_object_t *
object)
1287 if(
object == NULL || object->impl == NULL
1288 || object->impl->get_first == NULL
1289 || object->impl->get_next == NULL
1290 || object->impl->get_by_id == NULL) {
1291 LOG_WARN(
"failed to register NULL object\n");
1294 if(get_object(object->impl->object_id) != NULL) {
1296 LOG_WARN(
"object with id %u already registered\n",
1297 object->impl->object_id);
1300 if(has_non_generic_object(object->impl->object_id)) {
1302 LOG_WARN(
"object with id %u already registered\n",
1303 object->impl->object_id);
1306 list_add(generic_object_list,
object);
1309 lwm2m_rd_client_set_update_rd();
1316lwm2m_engine_remove_generic_object(lwm2m_object_t *
object)
1320 lwm2m_rd_client_set_update_rd();
1324static lwm2m_object_instance_t *
1325next_object_instance(
const lwm2m_context_t *
context, lwm2m_object_t *
object,
1326 lwm2m_object_instance_t *last)
1338 if(
object == NULL) {
1339 for(last = last->next; last != NULL; last = last->next) {
1347 return object->impl->get_next(last, NULL);
1350static coap_handler_status_t
1351lwm2m_handler_callback(coap_message_t *request, coap_message_t *response,
1352 uint8_t *buffer, uint16_t buffer_size, int32_t *offset)
1356 unsigned int format;
1360 lwm2m_object_t *object;
1361 lwm2m_object_instance_t *instance;
1366 lwm2m_status_t success;
1367 lwm2m_buffer_t inbuf;
1368 lwm2m_buffer_t outbuf;
1372 memset(&outbuf, 0,
sizeof(outbuf));
1373 memset(&inbuf, 0,
sizeof(inbuf));
1383 context.outbuf->buffer = buffer;
1384 context.outbuf->size = buffer_size;
1387 if(offset != NULL) {
1390 context.inbuf->size = coap_get_payload(request, (
const uint8_t **)&
context.inbuf->buffer);
1393#if LWM2M_QUEUE_MODE_ENABLED
1394lwm2m_queue_mode_request_received();
1399#define LWM2M_CONF_ENTITY_TOO_LARGE_BLOCK1 0
1400#if LWM2M_CONF_ENTITY_TOO_LARGE_BLOCK1
1401 if(coap_is_option(request, COAP_OPTION_BLOCK1)) {
1403 coap_get_header_block1(request, NULL, NULL, &bsize, NULL);
1405 LOG_DBG(
"Block1 size:%d\n", bsize);
1406 if(bsize > COAP_MAX_BLOCK_SIZE) {
1407 LOG_WARN(
"Entity too large: %u...\n", bsize);
1408 coap_set_status_code(response, REQUEST_ENTITY_TOO_LARGE_4_13);
1409 coap_set_header_size1(response, COAP_MAX_BLOCK_SIZE);
1410 return COAP_HANDLER_STATUS_PROCESSED;
1416 context.reader = &lwm2m_plain_text_reader;
1417 context.writer = &lwm2m_tlv_writer;
1420 url_len = coap_get_header_uri_path(request, &url);
1422 if(url_len == 2 && strncmp(
"bs", url, 2) == 0) {
1423 LOG_INFO(
"BOOTSTRAPPED!!!\n");
1424 coap_set_status_code(response, CHANGED_2_04);
1425 return COAP_HANDLER_STATUS_PROCESSED;
1428 depth = lwm2m_engine_parse_context(url, url_len, request, response,
1429 buffer, buffer_size, &
context);
1432 return COAP_HANDLER_STATUS_CONTINUE;
1435 LOG_DBG(
"%s URL:'", get_method_as_string(coap_get_method_type(request)));
1436 LOG_DBG_COAP_STRING(url, url_len);
1437 LOG_DBG_(
"' CTX:%u/%u/%u dp:%u bs:%d\n",
context.object_id,
context.object_instance_id,
1438 context.resource_id, depth, buffer_size);
1440 if(!coap_get_header_content_format(request, &format)) {
1441 LOG_DBG(
"No format given. Assume text plain...\n");
1442 format = TEXT_PLAIN;
1443 }
else if(format == LWM2M_TEXT_PLAIN) {
1445 format = TEXT_PLAIN;
1447 if(!coap_get_header_accept(request, &
accept)) {
1448 if(format == TEXT_PLAIN && depth < 3) {
1449 LOG_DBG(
"No Accept header, assume JSON\n");
1452 LOG_DBG(
"No Accept header, using same as content-format: %d\n", format);
1464 if(coap_get_method_type(request) == METHOD_DELETE) {
1465 LOG_DBG(
"This is a delete all - for bootstrap...\n");
1466 context.operation = LWM2M_OP_DELETE;
1467 coap_set_status_code(response, DELETED_2_02);
1470 for(
object =
list_head(generic_object_list);
1472 object =
object->next) {
1473 if(object->impl != NULL && object->impl->delete_instance != NULL) {
1474 object->impl->delete_instance(LWM2M_OBJECT_INSTANCE_NONE, NULL);
1478 lwm2m_rd_client_set_update_rd();
1480 return COAP_HANDLER_STATUS_PROCESSED;
1482 return COAP_HANDLER_STATUS_CONTINUE;
1485 instance = get_instance_by_context(&
context, &
object);
1491 if(instance == NULL &&
object == NULL) {
1493 return COAP_HANDLER_STATUS_CONTINUE;
1496 LOG_INFO(
"Context: %u/%u/%u found: %d\n",
1504 lwm2m_engine_select_reader(&
context, format);
1507 switch(coap_get_method_type(request)) {
1510 context.operation = LWM2M_OP_WRITE;
1511 coap_set_status_code(response, CHANGED_2_04);
1516 context.operation = LWM2M_OP_WRITE;
1517 coap_set_status_code(response, CHANGED_2_04);
1518 }
else if(
context.level == 3) {
1519 context.operation = LWM2M_OP_EXECUTE;
1520 coap_set_status_code(response, CHANGED_2_04);
1524 if(
accept == APPLICATION_LINK_FORMAT) {
1525 context.operation = LWM2M_OP_DISCOVER;
1527 context.operation = LWM2M_OP_READ;
1529 coap_set_status_code(response, CONTENT_2_05);
1532 context.operation = LWM2M_OP_DELETE;
1533 coap_set_status_code(response, DELETED_2_02);
1539 if(LOG_DBG_ENABLED) {
1542 LOG_DBG_COAP_STRING(url, url_len);
1543 LOG_DBG_(
"] %s Format:%d ID:%d bsize:%u offset:%"PRId32
"\n",
1544 get_method_as_string(coap_get_method_type(request)),
1545 format,
context.object_id, buffer_size,
1546 offset != NULL ? *offset : 0);
1547 if(format == TEXT_PLAIN) {
1549 const uint8_t *data;
1550 int plen = coap_get_payload(request, &data);
1553 LOG_DBG_COAP_STRING((
const char *)data, plen);
1560 if((offset != NULL && *offset == 0) &&
1561 coap_is_option(request, COAP_OPTION_BLOCK1)) {
1562 coap_get_header_block1(request, &bnum, &bmore, &bsize, &boffset);
1568 case LWM2M_OP_DISCOVER:
1570 success = perform_multi_resource_read_op(
object, instance, &
context);
1573 success = perform_multi_resource_read_op(
object, instance, &
context);
1575 case LWM2M_OP_WRITE:
1576 success = perform_multi_resource_write_op(
object, instance, &
context, format);
1578 case LWM2M_OP_EXECUTE:
1579 success = call_instance(instance, &
context);
1581 case LWM2M_OP_DELETE:
1582 if(
object != NULL && object->impl != NULL &&
1583 object->impl->delete_instance != NULL) {
1584 object->impl->delete_instance(
context.object_instance_id, &success);
1586 lwm2m_rd_client_set_update_rd();
1589 success = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1593 success = LWM2M_STATUS_OPERATION_NOT_ALLOWED;
1597 if(success == LWM2M_STATUS_OK) {
1599 if(coap_is_option(request, COAP_OPTION_BLOCK1)) {
1600 LOG_DBG(
"Setting BLOCK 1 num:%"PRIu32
" o2:%"PRIu32
" o:%"PRId32
"\n", bnum, boffset,
1601 (offset != NULL ? *offset : 0));
1602 coap_set_header_block1(response, bnum, 0, bsize);
1607 LOG_DBG_COAP_STRING(url, url_len);
1608 LOG_DBG_(
"] replying with %u bytes\n",
context.outbuf->len);
1609 coap_set_payload(response,
context.outbuf->buffer,
context.outbuf->len);
1610 coap_set_header_content_format(response,
context.content_type);
1612 if(offset != NULL) {
1613 LOG_DBG(
"Setting new offset: oo %"PRIu32
1614 ", no: %"PRIu32
"\n", *offset,
context.offset);
1615 if(
context.writer_flags & WRITER_HAS_MORE) {
1624 LOG_DBG_COAP_STRING(url, url_len);
1625 LOG_DBG_(
"] no data in reply\n");
1629 case LWM2M_STATUS_FORBIDDEN:
1630 coap_set_status_code(response, FORBIDDEN_4_03);
1632 case LWM2M_STATUS_NOT_FOUND:
1633 coap_set_status_code(response, NOT_FOUND_4_04);
1635 case LWM2M_STATUS_OPERATION_NOT_ALLOWED:
1636 coap_set_status_code(response, METHOD_NOT_ALLOWED_4_05);
1638 case LWM2M_STATUS_NOT_ACCEPTABLE:
1639 coap_set_status_code(response, NOT_ACCEPTABLE_4_06);
1641 case LWM2M_STATUS_UNSUPPORTED_CONTENT_FORMAT:
1642 coap_set_status_code(response, UNSUPPORTED_MEDIA_TYPE_4_15);
1646 coap_set_status_code(response, INTERNAL_SERVER_ERROR_5_00);
1650 LOG_WARN_COAP_STRING(url, url_len);
1651 LOG_WARN(
"] resource failed: %s\n", get_status_as_string(success));
1653 return COAP_HANDLER_STATUS_PROCESSED;
1657lwm2m_send_notification(
char* path)
1659#if LWM2M_QUEUE_MODE_ENABLED && LWM2M_QUEUE_MODE_INCLUDE_DYNAMIC_ADAPTATION
1660 if(lwm2m_queue_mode_get_dynamic_adaptation_flag()) {
1661 lwm2m_queue_mode_set_handler_from_notification();
1664 coap_notify_observers_sub(NULL, path);
1668lwm2m_notify_object_observers(lwm2m_object_instance_t *obj,
1673 snprintf(path, 20,
"%d/%d/%d", obj->object_id, obj->instance_id, resource);
1676#if LWM2M_QUEUE_MODE_ENABLED
1678 if(coap_has_observers(path)) {
1680 if(!lwm2m_rd_client_is_client_awake()) {
1681 lwm2m_notification_queue_add_notification_path(obj->object_id, obj->instance_id, resource);
1684 if(!lwm2m_queue_mode_is_waked_up_by_notification()) {
1685 lwm2m_queue_mode_set_waked_up_by_notification();
1686 lwm2m_rd_client_fsm_execute_queue_mode_update();
1690 lwm2m_send_notification(path);
1694 lwm2m_send_notification(path);
Default definitions of C compiler quirk work-arounds.
Collection of constants specified in the CoAP standard.
CoAP engine implementation.
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.
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.
void * list_head(const_list_t list)
Get a pointer to the first element of a list.
static uint8_t accept(uint8_t in)
static struct sicslowpan_addr_context * context
Addresses contexts for IPHC.
#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.