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 117 static uint8_t d_buf[COAP_MAX_BLOCK_SIZE * 2];
118 static lwm2m_buffer_t lwm2m_buf = {
119 .len = 0, .size = COAP_MAX_BLOCK_SIZE * 2, .buffer = d_buf
121 static lwm2m_object_instance_t instance_buffer;
124 static uint16_t lwm2m_buf_lock[4];
125 static uint64_t lwm2m_buf_lock_timeout = 0;
127 static lwm2m_write_opaque_callback current_opaque_callback;
128 static int current_opaque_offset = 0;
130 static coap_handler_status_t lwm2m_handler_callback(coap_message_t *request,
131 coap_message_t *response,
133 uint16_t buffer_size,
135 static lwm2m_object_instance_t *
136 next_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];
146 COAP_HANDLER(lwm2m_handler, lwm2m_handler_callback);
148 LIST(generic_object_list);
151 static lwm2m_object_t *
152 get_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) {
166 has_non_generic_object(uint16_t object_id)
168 lwm2m_object_instance_t *instance;
171 instance = instance->next) {
172 if(instance->object_id == object_id) {
180 static lwm2m_object_instance_t *
181 get_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);
215 static lwm2m_object_instance_t *
216 get_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);
224 static lwm2m_status_t
225 call_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);
248 double_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],
269 static 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) {
286 get_status_as_string(lwm2m_status_t status)
288 static char buffer[8];
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);
323 parse_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);
362 lwm2m_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;
383 void 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;
395 lwm2m_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;
507 lwm2m_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();
585 lwm2m_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;
619 lwm2m_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;
645 static uint32_t last_instance_id = NO_INSTANCE;
646 static int last_rsc_pos;
649 static lwm2m_status_t
650 perform_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;
927 static lwm2m_object_instance_t *
928 create_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 953 static lwm2m_object_instance_t *
954 get_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);
982 check_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);
1019 static lwm2m_status_t
1020 process_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;
1043 static int last_tlv_id = 0;
1045 static lwm2m_status_t
1046 perform_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;
1206 lwm2m_object_instance_t *
1207 lwm2m_engine_get_instance_buffer(
void)
1209 return &instance_buffer;
1213 lwm2m_engine_has_instance(uint16_t object_id, uint16_t instance_id)
1215 return get_instance(object_id, instance_id, NULL) != NULL;
1219 lwm2m_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();
1276 lwm2m_engine_remove_object(lwm2m_object_instance_t *
object)
1280 lwm2m_rd_client_set_update_rd();
1285 lwm2m_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();
1316 lwm2m_engine_remove_generic_object(lwm2m_object_t *
object)
1320 lwm2m_rd_client_set_update_rd();
1324 static lwm2m_object_instance_t *
1325 next_object_instance(
const lwm2m_context_t *context, lwm2m_object_t *
object,
1326 lwm2m_object_instance_t *last)
1328 if(context != NULL && context->level >= 2) {
1338 if(
object == NULL) {
1339 for(last = last->next; last != NULL; last = last->next) {
1341 if(context == NULL || last->object_id == context->object_id) {
1347 return object->impl->get_next(last, NULL);
1350 static coap_handler_status_t
1351 lwm2m_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;
1371 memset(&context, 0,
sizeof(context));
1372 memset(&outbuf, 0,
sizeof(outbuf));
1373 memset(&inbuf, 0,
sizeof(inbuf));
1375 context.outbuf = &outbuf;
1376 context.inbuf = &inbuf;
1379 context.request = request;
1380 context.response = response;
1383 context.outbuf->buffer = buffer;
1384 context.outbuf->size = buffer_size;
1387 if(offset != NULL) {
1388 context.offset = *offset;
1390 context.inbuf->size = coap_get_payload(request, (
const uint8_t **)&context.inbuf->buffer);
1391 context.inbuf->pos = 0;
1393 #if LWM2M_QUEUE_MODE_ENABLED 1394 lwm2m_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");
1450 accept = LWM2M_JSON;
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",
1497 context.object_id, context.object_instance_id,
1498 context.resource_id, depth);
1504 lwm2m_engine_select_reader(&context, format);
1505 lwm2m_engine_select_writer(&context, accept);
1507 switch(coap_get_method_type(request)) {
1510 context.operation = LWM2M_OP_WRITE;
1511 coap_set_status_code(response, CHANGED_2_04);
1514 if(context.level < 2) {
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);
1563 context.offset = boffset;
1567 switch(context.operation) {
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);
1605 if(context.outbuf->len > 0) {
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) {
1616 *offset = context.offset;
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;
1657 lwm2m_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);
1668 lwm2m_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);
static uip_ipaddr_t ipaddr
Pointer to prefix information option in uip_buf.
Header file for the LWM2M object API
static volatile uint64_t count
Num.
CoAP engine implementation.
Header file for the Contiki OMA LWM2M plain text reader / writer
static uint8_t accept(uint8_t in)
Header file for the Contiki OMA LWM2M TLV reader
Header file for IPv6-related data structures.
Header file for functions to manage the queue to store notifications when waiting for the respons...
Header file for the Contiki OMA LWM2M device
Header file for the Contiki OMA LWM2M JSON writer
Linked list manipulation routines.
Header file for the Contiki OMA LWM2M Queue Mode object to manage the parameters from the ...
void * list_head(list_t list)
Get a pointer to the first element of a list.
Header file for the Contiki OMA LWM2M Registration and Bootstrap Client.
static uint64_t coap_timer_uptime(void)
Get the time since boot in milliseconds.
Header file for the Contiki OMA LWM2M TLV writer
#define ADDR_TENTATIVE
Possible states for the an address (RFC 4862)
Header file for the Contiki OMA LWM2M TLV
void list_add(list_t list, void *item)
Add an item at the end of a list.
Header file for the Contiki OMA LWM2M Queue Mode implementation to manage the parameters ...
void list_init(list_t list)
Initialize a list.
#define LIST(name)
Declare a linked list.
uip_ds6_netif_t uip_ds6_if
The single interface.
Header file for the Contiki OMA LWM2M engine
Default definitions of C compiler quirk work-arounds.
coap_resource_flags_t
Resource flags for allowed methods and special functionalities.
void list_remove(list_t list, void *item)
Remove a specific element from a list.
Collection of constants specified in the CoAP standard.
static struct sicslowpan_addr_context * context
Addresses contexts for IPHC.