diff --git a/subsys/net/lib/lwm2m/CMakeLists.txt b/subsys/net/lib/lwm2m/CMakeLists.txt index 827ddfb739b..d11d0347027 100644 --- a/subsys/net/lib/lwm2m/CMakeLists.txt +++ b/subsys/net/lib/lwm2m/CMakeLists.txt @@ -9,6 +9,7 @@ zephyr_library_sources( lwm2m_obj_security.c lwm2m_obj_server.c lwm2m_obj_device.c + lwm2m_rw_link_format.c lwm2m_rw_plain_text.c lwm2m_rw_oma_tlv.c lwm2m_util.c diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.c b/subsys/net/lib/lwm2m/lwm2m_engine.c index d96aa54ecea..79bf71e3946 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.c +++ b/subsys/net/lib/lwm2m/lwm2m_engine.c @@ -40,6 +40,7 @@ LOG_MODULE_REGISTER(LOG_MODULE_NAME); #include "lwm2m_object.h" #include "lwm2m_engine.h" +#include "lwm2m_rw_link_format.h" #include "lwm2m_rw_plain_text.h" #include "lwm2m_rw_oma_tlv.h" #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT @@ -1165,7 +1166,7 @@ static int select_writer(struct lwm2m_output_context *out, uint16_t accept) switch (accept) { case LWM2M_FORMAT_APP_LINK_FORMAT: - /* TODO: rewrite do_discover as content formatter */ + out->writer = &link_format_writer; break; case LWM2M_FORMAT_PLAIN_TEXT: @@ -3246,86 +3247,22 @@ move_forward: return ret; } -static int print_attr(struct lwm2m_output_context *out, - uint8_t *buf, uint16_t buflen, void *ref) +int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap) { - struct lwm2m_attr *attr; - int i, used, base, ret; - uint8_t digit; - int32_t fraction; - - for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) { - if (ref != write_attr_pool[i].ref) { - continue; - } - - attr = write_attr_pool + i; - - /* assuming integer will have float_val.val2 set as 0 */ - - used = snprintk(buf, buflen, ";%s=%s%d%s", - LWM2M_ATTR_STR[attr->type], - attr->float_val.val1 == 0 && - attr->float_val.val2 < 0 ? "-" : "", - attr->float_val.val1, - attr->float_val.val2 != 0 ? "." : ""); - - base = 100000; - fraction = attr->float_val.val2 < 0 ? - -attr->float_val.val2 : attr->float_val.val2; - while (fraction && used < buflen && base > 0) { - digit = fraction / base; - buf[used++] = '0' + digit; - fraction -= digit * base; - base /= 10; - } - - ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), buf, used); - if (ret < 0) { - return ret; - } - } - - return 0; -} - -static int print_resource_dimension(struct lwm2m_output_context *out, - uint8_t *buf, uint16_t buflen, - struct lwm2m_engine_res *res) -{ - int ret, i, inst_count = 0; - - if (res->multi_res_inst) { - for (i = 0; i < res->res_inst_count; i++) { - if (res->res_instances[i].res_inst_id != - RES_INSTANCE_NOT_CREATED) { - inst_count++; - } - } - - snprintk(buf, buflen, ";dim=%d", inst_count); - ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), buf, - strlen(buf)); - if (ret < 0) { - return ret; - } - } - - return 0; -} - -static int do_discover_op(struct lwm2m_message *msg) -{ - static char disc_buf[24]; struct lwm2m_engine_obj *obj; struct lwm2m_engine_obj_inst *obj_inst; int ret; bool reported = false; - /* object ID is required in Device Management Discovery (5.4.2). - */ - if (msg->path.level == 0U || - msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID) { + /* Object ID is required in Device Management Discovery (5.4.2). */ + if (!is_bootstrap && + (msg->path.level == LWM2M_PATH_LEVEL_NONE || + msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID)) { + return -EPERM; + } + + /* Bootstrap discovery allows to specify at most Object ID. */ + if (is_bootstrap && msg->path.level > LWM2M_PATH_LEVEL_OBJECT) { return -EPERM; } @@ -3343,117 +3280,117 @@ static int do_discover_op(struct lwm2m_message *msg) return ret; } - /* Report object attributes only when Object ID (alone) was - * provided (and do it only once in case of multiple instances). + /* + * Add required prefix for bootstrap discovery (5.2.7.3). + * For device management discovery, `engine_put_begin()` adds nothing. */ - if (msg->path.level == 1) { - SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) { - if (obj->obj_id != msg->path.obj_id) { - continue; - } + engine_put_begin(&msg->out, &msg->path); - snprintk(disc_buf, sizeof(disc_buf), "%s", - reported ? "," : "", obj->obj_id); + SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) { + /* Skip unrelated objects */ + if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) { + continue; + } - ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), - disc_buf, strlen(disc_buf)); - if (ret < 0) { - return ret; - } + /* For bootstrap discover, only report object ID when no + * instance is available. + * For device management discovery, only report object ID with + * attributes if object ID (alone) was provided. + */ + if ((is_bootstrap && obj->instance_count == 0U) || + (!is_bootstrap && msg->path.level == LWM2M_PATH_LEVEL_OBJECT)) { + struct lwm2m_obj_path path = { + .obj_id = obj->obj_id, + .level = LWM2M_PATH_LEVEL_OBJECT, + }; - /* report object attrs (5.4.2) */ - ret = print_attr(&msg->out, disc_buf, sizeof(disc_buf), - obj); + ret = engine_put_corelink(&msg->out, &path); if (ret < 0) { return ret; } reported = true; - break; - } - } - SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst, node) { - if (obj_inst->obj->obj_id != msg->path.obj_id) { - continue; + if (is_bootstrap) { + continue; + } } - /* skip unrelated object instance */ - if (msg->path.level > 1 && - msg->path.obj_inst_id != obj_inst->obj_inst_id) { - continue; - } - - /* Report object instances only if Resource ID is missing. */ - if (msg->path.level <= 2U) { - snprintk(disc_buf, sizeof(disc_buf), "%s", - reported ? "," : "", - obj_inst->obj->obj_id, obj_inst->obj_inst_id); - - ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), - disc_buf, strlen(disc_buf)); - if (ret < 0) { - return ret; + SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, + obj_inst, node) { + if (obj_inst->obj->obj_id != obj->obj_id) { + continue; } - /* Report object instance attributes only when Instance - * ID was specified (5.4.2). + /* Skip unrelated object instance. */ + if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT && + msg->path.obj_inst_id != obj_inst->obj_inst_id) { + continue; + } + + /* Report object instances only if no Resource ID is + * provided. */ - if (msg->path.level == 2U) { - ret = print_attr(&msg->out, disc_buf, - sizeof(disc_buf), obj_inst); + if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT_INST) { + struct lwm2m_obj_path path = { + .obj_id = obj_inst->obj->obj_id, + .obj_inst_id = obj_inst->obj_inst_id, + .level = LWM2M_PATH_LEVEL_OBJECT_INST, + }; + + ret = engine_put_corelink(&msg->out, &path); if (ret < 0) { return ret; } + + reported = true; } - reported = true; - } - - for (int i = 0; i < obj_inst->resource_count; i++) { - /* skip unrelated resources */ - if (msg->path.level == 3U && - msg->path.res_id != obj_inst->resources[i].res_id) { + /* Do not report resources in bootstrap discovery. */ + if (is_bootstrap) { continue; } - snprintk(disc_buf, sizeof(disc_buf), - "%s", - reported ? "," : "", - obj_inst->obj->obj_id, - obj_inst->obj_inst_id, - obj_inst->resources[i].res_id); + for (int i = 0; i < obj_inst->resource_count; i++) { + /* Skip unrelated resources. */ + if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE && + msg->path.res_id != obj_inst->resources[i].res_id) { + continue; + } - ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), - disc_buf, strlen(disc_buf)); - if (ret < 0) { - return ret; - } + struct lwm2m_obj_path path = { + .obj_id = obj_inst->obj->obj_id, + .obj_inst_id = obj_inst->obj_inst_id, + .res_id = obj_inst->resources[i].res_id, + .level = LWM2M_PATH_LEVEL_RESOURCE, + }; - /* report resource attrs when path > 1 (5.4.2) */ - if (msg->path.level > 1) { - ret = print_resource_dimension( - &msg->out, disc_buf, sizeof(disc_buf), - &obj_inst->resources[i]); + ret = engine_put_corelink(&msg->out, &path); if (ret < 0) { return ret; } - ret = print_attr(&msg->out, - disc_buf, sizeof(disc_buf), - &obj_inst->resources[i]); - if (ret < 0) { - return ret; - } + reported = true; } - - reported = true; } } return reported ? 0 : -ENOENT; } +static int do_discover_op(struct lwm2m_message *msg, uint16_t content_format) +{ + switch (content_format) { + case LWM2M_FORMAT_APP_LINK_FORMAT: + return do_discover_op_link_format( + msg, msg->ctx->bootstrap_mode); + + default: + LOG_ERR("Unsupported format: %u", content_format); + return -ENOMSG; + } +} + int lwm2m_get_or_create_engine_obj(struct lwm2m_message *msg, struct lwm2m_engine_obj_inst **obj_inst, uint8_t *created) @@ -3628,149 +3565,6 @@ static int bootstrap_delete(struct lwm2m_message *msg) return ret; } - -static inline int bs_discover_fill_enabler(struct lwm2m_message *msg) -{ - char buf[sizeof("lwm2m='" LWM2M_PROTOCOL_VERSION "'")]; - - snprintk(buf, sizeof(buf), "lwm2m=\"%s\"", LWM2M_PROTOCOL_VERSION); - return buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), buf, strlen(buf)); -} - -static inline int bs_discover_fill_object(struct lwm2m_engine_obj *obj, - struct lwm2m_message *msg) -{ - char buf[sizeof(",")]; - - snprintk(buf, sizeof(buf), ",", obj->obj_id); - return buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), buf, strlen(buf)); -} - -static int bs_discover_fill_instance(struct lwm2m_engine_obj_inst *obj_inst, - struct lwm2m_message *msg) -{ - char buf[sizeof(",")]; - bool bootstrap_inst; - uint16_t server_id; - int ret; - - snprintk(buf, sizeof(buf), ",", obj_inst->obj->obj_id, - obj_inst->obj_inst_id); - - ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), buf, strlen(buf)); - if (ret < 0) { - return ret; - } - - /* Security and Server instnaces shall report Short Server ID associated - * with them (but only if not bootstrap instance). - */ - if (obj_inst->obj->obj_id == LWM2M_OBJECT_SECURITY_ID) { - snprintk(buf, sizeof(buf), "0/%d/1", obj_inst->obj_inst_id); - ret = lwm2m_engine_get_bool(buf, &bootstrap_inst); - if (ret < 0) { - return ret; - } - - if (!bootstrap_inst) { - snprintk(buf, sizeof(buf), "0/%d/10", - obj_inst->obj_inst_id); - ret = lwm2m_engine_get_u16(buf, &server_id); - if (ret < 0) { - return ret; - } - - snprintk(buf, sizeof(buf), ";ssid=%d", server_id); - ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), - buf, strlen(buf)); - if (ret < 0) { - return ret; - } - } - } - - if (obj_inst->obj->obj_id == LWM2M_OBJECT_SERVER_ID) { - snprintk(buf, sizeof(buf), "1/%d/0", obj_inst->obj_inst_id); - ret = lwm2m_engine_get_u16(buf, &server_id); - if (ret < 0) { - return ret; - } - - snprintk(buf, sizeof(buf), ";ssid=%d", server_id); - ret = buf_append(CPKT_BUF_WRITE(msg->out.out_cpkt), - buf, strlen(buf)); - if (ret < 0) { - return ret; - } - } - - return 0; -} - -static int bootstrap_discover(struct lwm2m_message *msg) -{ - struct lwm2m_engine_obj *obj; - struct lwm2m_engine_obj_inst *obj_inst; - int ret; - bool reported = false; - - /* set output content-format */ - ret = coap_append_option_int(msg->out.out_cpkt, - COAP_OPTION_CONTENT_FORMAT, - LWM2M_FORMAT_APP_LINK_FORMAT); - if (ret < 0) { - LOG_ERR("Error setting response content-format: %d", ret); - return ret; - } - - ret = coap_packet_append_payload_marker(msg->out.out_cpkt); - if (ret < 0) { - return ret; - } - - /* - * lwm2m spec 20170208-A sec 5.2.7.3 bootstrap discover on "/" - * - prefixed w/ lwm2m enabler version. e.g. lwm2m="1.0" - * - returns object and object instances only - */ - ret = bs_discover_fill_enabler(msg); - if (ret < 0) { - return ret; - } - - SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) { - if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) { - continue; - } - - /* Only report when no instance available */ - if (obj->instance_count == 0U) { - ret = bs_discover_fill_object(obj, msg); - if (ret < 0) { - return ret; - } - - reported = true; - continue; - } - - SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, - obj_inst, node) { - if (obj_inst->obj->obj_id != obj->obj_id) { - continue; - } - - ret = bs_discover_fill_instance(obj_inst, msg); - if (ret < 0) { - return ret; - } - - reported = true; - } - } - - return reported ? 0 : -ENOENT; -} #endif static int handle_request(struct coap_packet *request, @@ -4074,13 +3868,7 @@ static int handle_request(struct coap_packet *request, break; case LWM2M_OP_DISCOVER: -#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP) - if (msg->ctx->bootstrap_mode) { - r = bootstrap_discover(msg); - break; - } -#endif - r = do_discover_op(msg); + r = do_discover_op(msg, accept); break; case LWM2M_OP_WRITE: diff --git a/subsys/net/lib/lwm2m/lwm2m_engine.h b/subsys/net/lib/lwm2m/lwm2m_engine.h index 5b7f9094ace..6680885f858 100644 --- a/subsys/net/lib/lwm2m/lwm2m_engine.h +++ b/subsys/net/lib/lwm2m/lwm2m_engine.h @@ -97,6 +97,8 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_obj_field *obj_field, struct lwm2m_message *msg); +int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap); + enum coap_block_size lwm2m_default_block_size(void); int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms); diff --git a/subsys/net/lib/lwm2m/lwm2m_object.h b/subsys/net/lib/lwm2m/lwm2m_object.h index b0fbed9f85c..d4a9a93a5f0 100644 --- a/subsys/net/lib/lwm2m/lwm2m_object.h +++ b/subsys/net/lib/lwm2m/lwm2m_object.h @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -522,6 +523,8 @@ struct lwm2m_writer { size_t (*put_objlnk)(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, struct lwm2m_objlnk *value); + ssize_t (*put_corelink)(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path); }; struct lwm2m_reader { @@ -737,6 +740,16 @@ static inline size_t engine_put_objlnk(struct lwm2m_output_context *out, return out->writer->put_objlnk(out, path, value); } +static inline ssize_t engine_put_corelink(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path) +{ + if (out->writer->put_corelink) { + return out->writer->put_corelink(out, path); + } + + return -ENOTSUP; +} + static inline size_t engine_get_s32(struct lwm2m_input_context *in, int32_t *value) { diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_link_format.c b/subsys/net/lib/lwm2m/lwm2m_rw_link_format.c new file mode 100644 index 00000000000..73503217521 --- /dev/null +++ b/subsys/net/lib/lwm2m/lwm2m_rw_link_format.c @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "lwm2m_engine.h" +#include "lwm2m_rw_link_format.h" + +LOG_MODULE_REGISTER(net_lwm2m_link_format, CONFIG_LWM2M_LOG_LEVEL); + +#define CORELINK_BUF_SIZE 24 + +struct link_format_out_formatter_data { + uint8_t request_level; + bool is_bootstrap : 1; + bool is_first : 1; +}; + +static size_t put_begin(struct lwm2m_output_context *out, + struct lwm2m_obj_path *path) +{ + char enabler_ver[] = "lwm2m=\"" LWM2M_PROTOCOL_VERSION "\""; + struct link_format_out_formatter_data *fd; + int ret; + + fd = engine_get_out_user_data(out); + if (fd == NULL) { + return 0; + } + + if (!fd->is_bootstrap) { + /* Nothing to add in device management mode. */ + return 0; + } + + fd->is_first = false; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), enabler_ver, + strlen(enabler_ver)); + if (ret < 0) { + return 0; + } + + return strlen(enabler_ver); +} + +static int put_corelink_separator(struct lwm2m_output_context *out) +{ + char comma = ','; + int ret; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), &comma, sizeof(comma)); + if (ret < 0) { + return ret; + } + + return (int)sizeof(comma); +} + +static int put_corelink_dimension(struct lwm2m_output_context *out, + const struct lwm2m_engine_res *res, + uint8_t *buf, uint16_t buflen) +{ + int ret, inst_count = 0, len = 0; + + if (res->multi_res_inst) { + for (int i = 0; i < res->res_inst_count; i++) { + if (res->res_instances[i].res_inst_id != + RES_INSTANCE_NOT_CREATED) { + inst_count++; + } + } + + len = snprintk(buf, buflen, ";dim=%d", inst_count); + if (len < 0 || len >= buflen) { + return -ENOMEM; + } + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), buf, len); + if (ret < 0) { + return ret; + } + } + + return len; +} + +static int put_corelink_attributes(struct lwm2m_output_context *out, + const void *ref, uint8_t *buf, + uint16_t buflen) +{ + struct lwm2m_attr *attr = NULL; + int used, base, ret; + uint8_t digit; + int32_t fraction; + int len = 0; + + while ((attr = lwm2m_engine_get_next_attr(ref, attr)) != NULL) { + const char *name = lwm2m_engine_get_attr_name(attr); + + if (name == NULL) { + /* Invalid attribute, ignore. */ + continue; + } + + /* Assuming integer will have float_val.val2 set as 0. */ + used = snprintk(buf, buflen, ";%s=%s%d%s", + name, + attr->float_val.val1 == 0 && + attr->float_val.val2 < 0 ? "-" : "", + attr->float_val.val1, + attr->float_val.val2 != 0 ? "." : ""); + if (used < 0 || used >= buflen) { + return -ENOMEM; + } + + base = 100000; + fraction = attr->float_val.val2 < 0 ? + -attr->float_val.val2 : attr->float_val.val2; + while (fraction && used < buflen && base > 0) { + digit = fraction / base; + buf[used++] = '0' + digit; + fraction -= digit * base; + base /= 10; + } + + len += used; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), buf, used); + if (ret < 0) { + return ret; + } + } + + return len; +} + +static int put_corelink_ssid(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path, + uint8_t *buf, uint16_t buflen) +{ + uint16_t server_id = 0; + int ret; + int len; + + switch (path->obj_id) { + case LWM2M_OBJECT_SECURITY_ID: { + bool bootstrap_inst; + + ret = snprintk(buf, buflen, "0/%d/1", + path->obj_inst_id); + if (ret < 0 || ret >= buflen) { + return -ENOMEM; + } + + ret = lwm2m_engine_get_bool(buf, &bootstrap_inst); + if (ret < 0) { + return ret; + } + + /* Bootstrap Security object instance does not have associated + * Server object instance, so do not report ssid for it. + */ + if (bootstrap_inst) { + return 0; + } + + ret = snprintk(buf, buflen, "0/%d/10", + path->obj_inst_id); + if (ret < 0 || ret >= buflen) { + return -ENOMEM; + } + + ret = lwm2m_engine_get_u16(buf, &server_id); + if (ret < 0) { + return ret; + } + + break; + } + + case LWM2M_OBJECT_SERVER_ID: + ret = snprintk(buf, buflen, "1/%d/0", path->obj_inst_id); + if (ret < 0 || ret >= buflen) { + return -ENOMEM; + } + + ret = lwm2m_engine_get_u16(buf, &server_id); + if (ret < 0) { + return ret; + } + + break; + + default: + LOG_ERR("Invalid object ID for ssid attribute: %d", + path->obj_id); + return -EINVAL; + } + + len = snprintk(buf, buflen, ";ssid=%d", server_id); + if (len < 0 || len >= buflen) { + return -ENOMEM; + } + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), buf, len); + if (ret < 0) { + return ret; + } + + return len; +} + +static int put_obj_corelink(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path, + struct link_format_out_formatter_data *fd) +{ + char obj_buf[CORELINK_BUF_SIZE]; + int len = 0; + int ret; + + ret = snprintk(obj_buf, sizeof(obj_buf), "", path->obj_id); + if (ret < 0 || ret >= sizeof(obj_buf)) { + return -ENOMEM; + } + + len += ret; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), obj_buf, len); + if (ret < 0) { + return ret; + } + + if (!fd->is_bootstrap) { + /* Report object attributes only in device management mode + * (5.4.2). + */ + struct lwm2m_engine_obj *obj = lwm2m_engine_get_obj(path); + + if (obj == NULL) { + return -EINVAL; + } + + ret = put_corelink_attributes(out, obj, obj_buf, + sizeof(obj_buf)); + if (ret < 0) { + return ret; + } + + len += ret; + } + + return len; +} + +static int put_obj_inst_corelink(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path, + struct link_format_out_formatter_data *fd) +{ + char obj_buf[CORELINK_BUF_SIZE]; + int len = 0; + int ret; + + ret = snprintk(obj_buf, sizeof(obj_buf), "", + path->obj_id, path->obj_inst_id); + if (ret < 0 || ret >= sizeof(obj_buf)) { + return -ENOMEM; + } + + len += ret; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), obj_buf, len); + if (ret < 0) { + return ret; + } + + /* Bootstrap object instance corelink shall only contain ssid + * parameter for Security and Server objects (5.2.7.3). + */ + if (fd->is_bootstrap) { + if (path->obj_id == LWM2M_OBJECT_SECURITY_ID || + path->obj_id == LWM2M_OBJECT_SERVER_ID) { + ret = put_corelink_ssid(out, path, obj_buf, + sizeof(obj_buf)); + if (ret < 0) { + return ret; + } + + len += ret; + } + + return len; + } + + /* Report object instance attributes only when Instance + * ID was specified (5.4.2). + */ + if (fd->request_level == LWM2M_PATH_LEVEL_OBJECT_INST) { + struct lwm2m_engine_obj_inst *obj_inst = + lwm2m_engine_get_obj_inst(path); + + if (obj_inst == NULL) { + return -EINVAL; + } + + ret = put_corelink_attributes(out, obj_inst, obj_buf, + sizeof(obj_buf)); + if (ret < 0) { + return ret; + } + + len += ret; + } + + return len; +} + +static int put_res_corelink(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path, + struct link_format_out_formatter_data *fd) +{ + char obj_buf[CORELINK_BUF_SIZE]; + int len = 0; + int ret; + + if (fd->is_bootstrap) { + /* No resource reporting in bootstrap mode. */ + return 0; + } + + ret = snprintk(obj_buf, sizeof(obj_buf), "", path->obj_id, + path->obj_inst_id, path->res_id); + if (ret < 0 || ret >= sizeof(obj_buf)) { + return -ENOMEM; + } + + len += ret; + + ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), obj_buf, len); + if (ret < 0) { + return ret; + } + + /* Report resource attrs when at least object instance was specified + * (5.4.2). + */ + if (fd->request_level >= LWM2M_PATH_LEVEL_OBJECT_INST) { + struct lwm2m_engine_res *res = lwm2m_engine_get_res(path); + + if (res == NULL) { + return -EINVAL; + } + + ret = put_corelink_dimension(out, res, obj_buf, + sizeof(obj_buf)); + if (ret < 0) { + return ret; + } + + len += ret; + + ret = put_corelink_attributes(out, res, obj_buf, + sizeof(obj_buf)); + if (ret < 0) { + return ret; + } + + len += ret; + } + + return len; +} + +static ssize_t put_corelink(struct lwm2m_output_context *out, + const struct lwm2m_obj_path *path) +{ + struct link_format_out_formatter_data *fd; + ssize_t len = 0; + int ret; + + fd = engine_get_out_user_data(out); + if (fd == NULL) { + return -EINVAL; + } + + if (fd->is_first) { + fd->is_first = false; + } else { + ret = put_corelink_separator(out); + if (ret < 0) { + return ret; + } + + len += ret; + } + + switch (path->level) { + case LWM2M_PATH_LEVEL_OBJECT: + ret = put_obj_corelink(out, path, fd); + if (ret < 0) { + return ret; + } + + len += ret; + break; + + case LWM2M_PATH_LEVEL_OBJECT_INST: + ret = put_obj_inst_corelink(out, path, fd); + if (ret < 0) { + return ret; + } + + len += ret; + break; + + case LWM2M_PATH_LEVEL_RESOURCE: + ret = put_res_corelink(out, path, fd); + if (ret < 0) { + return ret; + } + + len += ret; + break; + + default: + LOG_ERR("Invalid corelink path level: %d", path->level); + return -EINVAL; + } + + return len; +} + +const struct lwm2m_writer link_format_writer = { + .put_begin = put_begin, + .put_corelink = put_corelink, +}; + +int do_discover_op_link_format(struct lwm2m_message *msg, bool is_bootstrap) +{ + struct link_format_out_formatter_data fd; + int ret; + + fd.is_first = true; + fd.is_bootstrap = is_bootstrap; + fd.request_level = msg->path.level; + + engine_set_out_user_data(&msg->out, &fd); + ret = lwm2m_discover_handler(msg, is_bootstrap); + engine_clear_out_user_data(&msg->out); + + return ret; +} diff --git a/subsys/net/lib/lwm2m/lwm2m_rw_link_format.h b/subsys/net/lib/lwm2m/lwm2m_rw_link_format.h new file mode 100644 index 00000000000..8300ecbcef1 --- /dev/null +++ b/subsys/net/lib/lwm2m/lwm2m_rw_link_format.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef LWM2M_RW_LINK_FORMAT_H_ +#define LWM2M_RW_LINK_FORMAT_H_ + +#include "lwm2m_object.h" + +extern const struct lwm2m_writer link_format_writer; + +int do_discover_op_link_format(struct lwm2m_message *msg, bool is_bootstrap); + +#endif /* LWM2M_RW_LINK_FORMAT_H_ */