net: lwm2m: Add application/link-format content writer
This commit adds a new content writer, application/link-format, which can be used during the Discovery procedure, to fill the content of the response payload. Introducing this new content writer, which encapsulates some of the details like attribute handling which is different for bootstrap/regular discovery, allows to unify the discovery handler in the lwm2m_engine, thus it's no longer needed to have spearate handler functions for bootstrap/regular discovery. Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
parent
6ba362b11b
commit
b17555eb9e
|
@ -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
|
||||
|
|
|
@ -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</%u>",
|
||||
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</%u/%u>",
|
||||
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</%u/%u/%u>",
|
||||
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(",</xxxxx>")];
|
||||
|
||||
snprintk(buf, sizeof(buf), ",</%u>", 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(",</xxxxx/xxxxx>")];
|
||||
bool bootstrap_inst;
|
||||
uint16_t server_id;
|
||||
int ret;
|
||||
|
||||
snprintk(buf, sizeof(buf), ",</%u/%u>", 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 </OBJ_ID> 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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -52,6 +52,7 @@
|
|||
#include <net/net_ip.h>
|
||||
#include <sys/printk.h>
|
||||
#include <sys/util.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <net/coap.h>
|
||||
#include <net/lwm2m.h>
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
456
subsys/net/lib/lwm2m/lwm2m_rw_link_format.c
Normal file
456
subsys/net/lib/lwm2m/lwm2m_rw_link_format.c
Normal file
|
@ -0,0 +1,456 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Nordic Semiconductor ASA
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#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), "</%u>", 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), "</%u/%u>",
|
||||
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), "</%u/%u/%u>", 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;
|
||||
}
|
16
subsys/net/lib/lwm2m/lwm2m_rw_link_format.h
Normal file
16
subsys/net/lib/lwm2m/lwm2m_rw_link_format.h
Normal file
|
@ -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_ */
|
Loading…
Reference in a new issue