2017-07-07 11:04:03 -07:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Linaro Limited
|
2019-01-24 12:22:42 -08:00
|
|
|
* Copyright (c) 2018-2019 Foundries.io
|
2017-07-07 11:04:03 -07:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Uses some original concepts by:
|
|
|
|
* Joakim Eriksson <joakime@sics.se>
|
|
|
|
* Niclas Finne <nfi@sics.se>
|
|
|
|
* Joel Hoglund <joel@sics.se>
|
|
|
|
*/
|
|
|
|
|
2018-09-19 11:22:19 +03:00
|
|
|
#define LOG_MODULE_NAME net_lwm2m_engine
|
|
|
|
#define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
|
|
|
|
|
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
#include <fcntl.h>
|
2017-07-07 11:04:03 -07:00
|
|
|
#include <zephyr/types.h>
|
|
|
|
#include <stddef.h>
|
2017-11-24 18:47:00 +08:00
|
|
|
#include <stdlib.h>
|
2017-07-07 11:04:03 -07:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <init.h>
|
2019-06-26 10:33:49 -04:00
|
|
|
#include <sys/printk.h>
|
2017-07-07 11:04:03 -07:00
|
|
|
#include <net/net_ip.h>
|
2019-01-28 11:55:23 -08:00
|
|
|
#include <net/http_parser_url.h>
|
|
|
|
#include <net/socket.h>
|
2022-02-28 07:42:53 -08:00
|
|
|
#include <net/lwm2m.h>
|
2019-01-28 11:55:23 -08:00
|
|
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
|
|
|
#include <net/tls_credentials.h>
|
|
|
|
#endif
|
2019-01-31 09:21:51 -08:00
|
|
|
#if defined(CONFIG_DNS_RESOLVER)
|
|
|
|
#include <net/dns_resolve.h>
|
|
|
|
#endif
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
#include "lwm2m_object.h"
|
|
|
|
#include "lwm2m_engine.h"
|
2021-01-25 13:49:33 +01:00
|
|
|
#include "lwm2m_rw_link_format.h"
|
2017-07-07 11:04:03 -07:00
|
|
|
#include "lwm2m_rw_plain_text.h"
|
|
|
|
#include "lwm2m_rw_oma_tlv.h"
|
2021-07-21 13:46:05 +02:00
|
|
|
#include "lwm2m_util.h"
|
2022-01-12 23:28:16 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
#include "lwm2m_rw_senml_json.h"
|
|
|
|
#endif
|
2017-07-07 11:04:03 -07:00
|
|
|
#ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
|
|
|
|
#include "lwm2m_rw_json.h"
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
|
|
|
|
#include "lwm2m_rd_client.h"
|
|
|
|
#endif
|
|
|
|
|
2020-10-07 14:18:56 +03:00
|
|
|
#if IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)
|
|
|
|
/* Lowest priority cooperative thread */
|
|
|
|
#define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
|
|
|
|
#else
|
|
|
|
#define THREAD_PRIORITY K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1)
|
|
|
|
#endif
|
|
|
|
|
2020-04-24 15:32:39 +02:00
|
|
|
#define ENGINE_UPDATE_INTERVAL_MS 500
|
2020-10-26 09:22:44 +01:00
|
|
|
#define OBSERVE_COUNTER_START 0U
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-11-24 18:47:00 +08:00
|
|
|
#if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
|
|
|
|
#define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
|
|
|
|
#else
|
|
|
|
#define COAP_OPTION_BUF_LEN 13
|
|
|
|
#endif
|
|
|
|
|
2021-11-09 12:48:12 +02:00
|
|
|
#define BINDING_OPT_MAX_LEN 3 /* "UQ" */
|
|
|
|
#define QUEUE_OPT_MAX_LEN 2 /* "Q" */
|
2017-10-03 09:34:59 -07:00
|
|
|
#define MAX_TOKEN_LEN 8
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
struct observe_node {
|
|
|
|
sys_snode_t node;
|
2022-02-28 07:42:53 -08:00
|
|
|
sys_slist_t path_list; /* List of Observation path */
|
2022-02-23 23:43:52 -08:00
|
|
|
uint8_t token[MAX_TOKEN_LEN]; /* Observation Token */
|
|
|
|
int64_t event_timestamp; /* Timestamp for trig next Notify */
|
2022-02-28 07:42:53 -08:00
|
|
|
int64_t last_timestamp; /* Timestamp from last Notify */
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t counter;
|
|
|
|
uint16_t format;
|
|
|
|
uint8_t tkl;
|
2022-02-23 23:43:52 -08:00
|
|
|
bool resource_update : 1; /* Resource is updated */
|
2022-02-28 07:42:53 -08:00
|
|
|
bool composite : 1; /* Composite Observation */
|
2017-07-07 11:04:03 -07:00
|
|
|
};
|
|
|
|
|
2018-05-07 14:06:46 -07:00
|
|
|
struct notification_attrs {
|
|
|
|
/* use to determine which value is set */
|
2021-07-23 16:38:23 +02:00
|
|
|
double gt;
|
|
|
|
double lt;
|
|
|
|
double st;
|
2020-05-27 11:26:57 -05:00
|
|
|
int32_t pmin;
|
|
|
|
int32_t pmax;
|
|
|
|
uint8_t flags;
|
2018-05-07 14:06:46 -07:00
|
|
|
};
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
#ifdef CONFIG_LWM2M_VERSION_1_1
|
|
|
|
#define LWM2M_ENGINE_MAX_OBSERVER_PATH CONFIG_LWM2M_ENGINE_MAX_OBSERVER * 3
|
|
|
|
#else
|
|
|
|
#define LWM2M_ENGINE_MAX_OBSERVER_PATH CONFIG_LWM2M_ENGINE_MAX_OBSERVER
|
|
|
|
#endif
|
|
|
|
static struct lwm2m_obj_path_list observe_paths[LWM2M_ENGINE_MAX_OBSERVER_PATH];
|
|
|
|
static sys_slist_t obs_obj_path_list;
|
2017-07-07 11:04:03 -07:00
|
|
|
static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER];
|
|
|
|
|
2017-10-10 20:55:05 -07:00
|
|
|
#define MAX_PERIODIC_SERVICE 10
|
|
|
|
|
|
|
|
struct service_node {
|
|
|
|
sys_snode_t node;
|
2019-04-05 16:14:50 -07:00
|
|
|
k_work_handler_t service_work;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t min_call_period; /* ms */
|
|
|
|
uint64_t last_timestamp; /* ms */
|
2017-10-10 20:55:05 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct service_node service_node_data[MAX_PERIODIC_SERVICE];
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
static sys_slist_t engine_obj_list;
|
|
|
|
static sys_slist_t engine_obj_inst_list;
|
2017-10-10 20:55:05 -07:00
|
|
|
static sys_slist_t engine_service_list;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
#define LWM2M_DP_CLIENT_URI "dp"
|
|
|
|
|
2020-07-31 12:45:25 -07:00
|
|
|
static K_KERNEL_STACK_DEFINE(engine_thread_stack,
|
2019-01-28 11:55:23 -08:00
|
|
|
CONFIG_LWM2M_ENGINE_STACK_SIZE);
|
|
|
|
static struct k_thread engine_thread_data;
|
|
|
|
|
|
|
|
#define MAX_POLL_FD CONFIG_NET_SOCKETS_POLL_MAX
|
|
|
|
|
|
|
|
static struct lwm2m_ctx *sock_ctx[MAX_POLL_FD];
|
|
|
|
static struct pollfd sock_fds[MAX_POLL_FD];
|
|
|
|
static int sock_nfds;
|
|
|
|
|
2017-07-25 16:54:25 +08:00
|
|
|
#define NUM_BLOCK1_CONTEXT CONFIG_LWM2M_NUM_BLOCK1_CONTEXT
|
|
|
|
|
|
|
|
/* TODO: figure out what's correct value */
|
2020-04-24 15:32:39 +02:00
|
|
|
#define TIMEOUT_BLOCKWISE_TRANSFER_MS (MSEC_PER_SEC * 30)
|
2017-07-25 16:54:25 +08:00
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
static struct lwm2m_block_context block1_contexts[NUM_BLOCK1_CONTEXT];
|
2017-07-25 16:54:25 +08:00
|
|
|
|
2022-03-16 21:07:43 +00:00
|
|
|
/* write-attribute related definitions */
|
2017-11-24 18:47:00 +08:00
|
|
|
static const char * const LWM2M_ATTR_STR[] = { "pmin", "pmax",
|
|
|
|
"gt", "lt", "st" };
|
2020-05-27 11:26:57 -05:00
|
|
|
static const uint8_t LWM2M_ATTR_LEN[] = { 4, 4, 2, 2, 2 };
|
2017-11-24 18:47:00 +08:00
|
|
|
|
|
|
|
static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR];
|
|
|
|
|
2017-09-05 14:29:41 +08:00
|
|
|
static struct lwm2m_engine_obj *get_engine_obj(int obj_id);
|
|
|
|
static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
|
|
|
|
int obj_inst_id);
|
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
/* Shared set of in-flight LwM2M messages */
|
|
|
|
static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES];
|
|
|
|
|
2022-02-02 16:29:17 +01:00
|
|
|
/* Forward declarations. */
|
|
|
|
static int path_to_objs(const struct lwm2m_obj_path *path,
|
|
|
|
struct lwm2m_engine_obj_inst **obj_inst,
|
|
|
|
struct lwm2m_engine_obj_field **obj_field,
|
|
|
|
struct lwm2m_engine_res **res,
|
|
|
|
struct lwm2m_engine_res_inst **res_inst);
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static struct lwm2m_obj_path *lwm2m_read_first_path_ptr(sys_slist_t *lwm2m_path_list);
|
|
|
|
static struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list);
|
|
|
|
static int do_send_op(struct lwm2m_message *msg, uint16_t content_format,
|
|
|
|
sys_slist_t *lwm2m_path_list);
|
|
|
|
static int do_composite_observe_read_path_op(struct lwm2m_message *msg, uint16_t content_format,
|
|
|
|
sys_slist_t *lwm2m_path_list,
|
|
|
|
sys_slist_t *lwm2m_path_free_list);
|
|
|
|
static void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list);
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* for debugging: to print IP addresses */
|
|
|
|
char *lwm2m_sprint_ip_addr(const struct sockaddr *addr)
|
|
|
|
{
|
|
|
|
static char buf[NET_IPV6_ADDR_LEN];
|
|
|
|
|
2017-08-18 10:03:46 +03:00
|
|
|
if (addr->sa_family == AF_INET6) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr,
|
|
|
|
buf, sizeof(buf));
|
|
|
|
}
|
2019-07-08 10:18:23 -07:00
|
|
|
|
2017-08-18 10:03:46 +03:00
|
|
|
if (addr->sa_family == AF_INET) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr,
|
|
|
|
buf, sizeof(buf));
|
|
|
|
}
|
|
|
|
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unknown IP address family:%d", addr->sa_family);
|
2019-07-08 10:18:23 -07:00
|
|
|
strcpy(buf, "unk");
|
|
|
|
return buf;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static uint8_t to_hex_digit(uint8_t digit)
|
2018-05-09 14:01:29 -07:00
|
|
|
{
|
2019-03-26 19:57:45 -06:00
|
|
|
if (digit >= 10U) {
|
|
|
|
return digit - 10U + 'a';
|
2018-05-09 14:01:29 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return digit + '0';
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static char *sprint_token(const uint8_t *token, uint8_t tkl)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
static char buf[32];
|
2018-05-09 14:01:29 -07:00
|
|
|
char *ptr = buf;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-10-08 13:45:08 +02:00
|
|
|
if (token && tkl != 0) {
|
2017-10-03 09:34:59 -07:00
|
|
|
int i;
|
|
|
|
|
2019-02-11 17:14:19 +00:00
|
|
|
tkl = MIN(tkl, sizeof(buf) / 2 - 1);
|
2018-05-09 14:01:29 -07:00
|
|
|
|
2017-10-03 09:34:59 -07:00
|
|
|
for (i = 0; i < tkl; i++) {
|
2018-05-09 14:01:29 -07:00
|
|
|
*ptr++ = to_hex_digit(token[i] >> 4);
|
|
|
|
*ptr++ = to_hex_digit(token[i] & 0x0F);
|
2017-10-03 09:34:59 -07:00
|
|
|
}
|
|
|
|
|
2018-05-09 14:01:29 -07:00
|
|
|
*ptr = '\0';
|
2017-10-03 09:34:59 -07:00
|
|
|
} else {
|
|
|
|
strcpy(buf, "[no-token]");
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2017-10-03 09:34:59 -07:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2017-07-25 16:54:25 +08:00
|
|
|
/* block-wise transfer functions */
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
enum coap_block_size lwm2m_default_block_size(void)
|
2017-07-25 16:54:25 +08:00
|
|
|
{
|
|
|
|
switch (CONFIG_LWM2M_COAP_BLOCK_SIZE) {
|
|
|
|
case 16:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_16;
|
2017-07-25 16:54:25 +08:00
|
|
|
case 32:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_32;
|
2017-07-25 16:54:25 +08:00
|
|
|
case 64:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_64;
|
2017-07-25 16:54:25 +08:00
|
|
|
case 128:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_128;
|
2017-07-25 16:54:25 +08:00
|
|
|
case 256:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_256;
|
2017-07-25 16:54:25 +08:00
|
|
|
case 512:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_512;
|
2017-07-25 16:54:25 +08:00
|
|
|
case 1024:
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_1024;
|
2017-07-25 16:54:25 +08:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
return COAP_BLOCK_256;
|
2017-07-25 16:54:25 +08:00
|
|
|
}
|
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
static int init_block_ctx(const uint8_t *token, uint8_t tkl,
|
|
|
|
struct lwm2m_block_context **ctx)
|
2017-07-25 16:54:25 +08:00
|
|
|
{
|
|
|
|
int i;
|
2020-05-27 11:26:57 -05:00
|
|
|
int64_t timestamp;
|
2017-07-25 16:54:25 +08:00
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
timestamp = k_uptime_get();
|
|
|
|
for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
|
2019-03-26 19:57:45 -06:00
|
|
|
if (block1_contexts[i].tkl == 0U) {
|
2017-07-25 16:54:25 +08:00
|
|
|
*ctx = &block1_contexts[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (timestamp - block1_contexts[i].timestamp >
|
2020-04-24 15:32:39 +02:00
|
|
|
TIMEOUT_BLOCKWISE_TRANSFER_MS) {
|
2017-07-25 16:54:25 +08:00
|
|
|
*ctx = &block1_contexts[i];
|
|
|
|
/* TODO: notify application for block
|
|
|
|
* transfer timeout
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*ctx == NULL) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Cannot find free block context");
|
2017-07-25 16:54:25 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
(*ctx)->tkl = tkl;
|
|
|
|
memcpy((*ctx)->token, token, tkl);
|
2017-09-19 16:04:32 -07:00
|
|
|
coap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0);
|
2017-07-25 16:54:25 +08:00
|
|
|
(*ctx)->timestamp = timestamp;
|
2020-06-18 11:32:34 +02:00
|
|
|
(*ctx)->expected = 0;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
(*ctx)->last_block = false;
|
2022-01-12 23:28:16 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
lwm2m_senml_json_context_init(&(*ctx)->senml_json_ctx);
|
|
|
|
#endif
|
2020-09-16 15:09:30 +02:00
|
|
|
memset(&(*ctx)->opaque, 0, sizeof((*ctx)->opaque));
|
2017-07-25 16:54:25 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
static int get_block_ctx(const uint8_t *token, uint8_t tkl,
|
|
|
|
struct lwm2m_block_context **ctx)
|
2017-07-25 16:54:25 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
*ctx = NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
|
|
|
|
if (block1_contexts[i].tkl == tkl &&
|
|
|
|
memcmp(token, block1_contexts[i].token, tkl) == 0) {
|
|
|
|
*ctx = &block1_contexts[i];
|
2019-06-18 14:45:40 -04:00
|
|
|
/* refresh timestamp */
|
2017-07-25 16:54:25 +08:00
|
|
|
(*ctx)->timestamp = k_uptime_get();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*ctx == NULL) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
static void free_block_ctx(struct lwm2m_block_context *ctx)
|
2017-07-25 16:54:25 +08:00
|
|
|
{
|
|
|
|
if (ctx == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-11-29 11:23:03 -08:00
|
|
|
ctx->tkl = 0U;
|
2017-07-25 16:54:25 +08:00
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* observer functions */
|
|
|
|
|
2018-05-07 15:05:04 -07:00
|
|
|
static int update_attrs(void *ref, struct notification_attrs *out)
|
2017-11-24 18:47:00 +08:00
|
|
|
{
|
2018-05-07 15:05:04 -07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
|
2018-06-06 12:35:13 -07:00
|
|
|
if (ref != write_attr_pool[i].ref) {
|
2018-05-07 15:05:04 -07:00
|
|
|
continue;
|
|
|
|
}
|
2017-11-24 18:47:00 +08:00
|
|
|
|
2018-05-07 15:05:04 -07:00
|
|
|
switch (write_attr_pool[i].type) {
|
2017-11-24 18:47:00 +08:00
|
|
|
case LWM2M_ATTR_PMIN:
|
2018-05-07 15:05:04 -07:00
|
|
|
out->pmin = write_attr_pool[i].int_val;
|
2017-11-24 18:47:00 +08:00
|
|
|
break;
|
|
|
|
case LWM2M_ATTR_PMAX:
|
2018-05-07 15:05:04 -07:00
|
|
|
out->pmax = write_attr_pool[i].int_val;
|
2017-11-24 18:47:00 +08:00
|
|
|
break;
|
|
|
|
case LWM2M_ATTR_LT:
|
2018-05-07 15:05:04 -07:00
|
|
|
out->lt = write_attr_pool[i].float_val;
|
2017-11-24 18:47:00 +08:00
|
|
|
break;
|
|
|
|
case LWM2M_ATTR_GT:
|
2018-05-07 15:05:04 -07:00
|
|
|
out->gt = write_attr_pool[i].float_val;
|
2017-11-24 18:47:00 +08:00
|
|
|
break;
|
|
|
|
case LWM2M_ATTR_STEP:
|
2018-05-07 15:05:04 -07:00
|
|
|
out->st = write_attr_pool[i].float_val;
|
2017-11-24 18:47:00 +08:00
|
|
|
break;
|
|
|
|
default:
|
2022-03-16 21:07:43 +00:00
|
|
|
LOG_ERR("Unrecognized attr: %d",
|
2018-09-19 11:22:19 +03:00
|
|
|
write_attr_pool[i].type);
|
2017-11-24 18:47:00 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* mark as set */
|
2018-05-07 15:05:04 -07:00
|
|
|
out->flags |= BIT(write_attr_pool[i].type);
|
2017-11-24 18:47:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-07 15:05:04 -07:00
|
|
|
static void clear_attrs(void *ref)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
|
|
|
|
if (ref == write_attr_pool[i].ref) {
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(&write_attr_pool[i], 0,
|
|
|
|
sizeof(write_attr_pool[i]));
|
2018-05-07 15:05:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
static bool lwm2m_observer_path_compare(struct lwm2m_obj_path *o_p, struct lwm2m_obj_path *p)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2022-02-23 23:43:52 -08:00
|
|
|
/* updated path is deeper than obs node, skip */
|
|
|
|
if (p->level > o_p->level) {
|
|
|
|
return false;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
/* check obj id matched or not */
|
|
|
|
if (p->obj_id != o_p->obj_id) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-05-25 22:55:59 +02:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (o_p->level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
|
|
|
|
if (p->level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
|
|
|
|
p->obj_inst_id != o_p->obj_inst_id) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2021-05-25 22:55:59 +02:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (o_p->level >= LWM2M_PATH_LEVEL_RESOURCE) {
|
|
|
|
if (p->level >= LWM2M_PATH_LEVEL_RESOURCE &&
|
|
|
|
p->res_id != o_p->res_id) {
|
|
|
|
return false;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
|
|
|
|
o_p->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
|
|
|
|
if (p->level == LWM2M_PATH_LEVEL_RESOURCE_INST &&
|
|
|
|
p->res_inst_id != o_p->res_inst_id) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static bool lwm2m_notify_observer_list(sys_slist_t *path_list, struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *o_p;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) {
|
|
|
|
if (lwm2m_observer_path_compare(&o_p->path, path)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
int lwm2m_notify_observer(uint16_t obj_id, uint16_t obj_inst_id, uint16_t res_id)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2022-02-23 23:43:52 -08:00
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
|
|
|
|
path.level = LWM2M_PATH_LEVEL_RESOURCE;
|
|
|
|
path.obj_id = obj_id;
|
|
|
|
path.obj_inst_id = obj_inst_id;
|
|
|
|
path.res_id = res_id;
|
|
|
|
|
|
|
|
return lwm2m_notify_observer_path(&path);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
static int engine_observe_get_attributes(struct lwm2m_obj_path *path,
|
|
|
|
struct notification_attrs *attrs, uint16_t srv_obj_inst)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2022-02-23 23:43:52 -08:00
|
|
|
struct lwm2m_engine_obj *obj;
|
2018-08-27 13:57:23 -07:00
|
|
|
struct lwm2m_engine_obj_field *obj_field = NULL;
|
2017-09-05 14:29:41 +08:00
|
|
|
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
2022-02-02 16:29:17 +01:00
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
2022-02-23 23:43:52 -08:00
|
|
|
int ret, i;
|
2017-10-03 09:34:59 -07:00
|
|
|
|
2019-07-29 10:00:00 -07:00
|
|
|
/* defaults from server object */
|
2022-02-23 23:43:52 -08:00
|
|
|
attrs->pmin = lwm2m_server_get_pmin(srv_obj_inst);
|
|
|
|
attrs->pmax = lwm2m_server_get_pmax(srv_obj_inst);
|
|
|
|
attrs->flags = BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX);
|
2017-11-28 16:29:21 +08:00
|
|
|
|
2017-09-05 14:29:41 +08:00
|
|
|
/* check if object exists */
|
2022-02-23 23:43:52 -08:00
|
|
|
obj = get_engine_obj(path->obj_id);
|
2017-11-28 16:29:21 +08:00
|
|
|
if (!obj) {
|
2022-02-23 23:43:52 -08:00
|
|
|
LOG_ERR("unable to find obj: %u", path->obj_id);
|
2017-09-05 14:29:41 +08:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
ret = update_attrs(obj, attrs);
|
2018-05-07 14:18:37 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-11-28 16:29:21 +08:00
|
|
|
}
|
|
|
|
|
2017-09-05 14:29:41 +08:00
|
|
|
/* check if object instance exists */
|
2022-02-23 23:43:52 -08:00
|
|
|
if (path->level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
|
|
|
|
obj_inst = get_engine_obj_inst(path->obj_id,
|
|
|
|
path->obj_inst_id);
|
2017-09-05 14:29:41 +08:00
|
|
|
if (!obj_inst) {
|
2022-02-28 07:42:53 -08:00
|
|
|
attrs->pmax = 0;
|
|
|
|
attrs->pmin = 0;
|
|
|
|
return 0;
|
2017-09-05 14:29:41 +08:00
|
|
|
}
|
2017-11-28 16:29:21 +08:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
ret = update_attrs(obj_inst, attrs);
|
2018-05-07 14:18:37 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-11-28 16:29:21 +08:00
|
|
|
}
|
2017-09-05 14:29:41 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check if resource exists */
|
2022-02-23 23:43:52 -08:00
|
|
|
if (path->level >= LWM2M_PATH_LEVEL_RESOURCE) {
|
2017-09-05 14:29:41 +08:00
|
|
|
for (i = 0; i < obj_inst->resource_count; i++) {
|
2022-02-23 23:43:52 -08:00
|
|
|
if (obj_inst->resources[i].res_id == path->res_id) {
|
2017-09-05 14:29:41 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == obj_inst->resource_count) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unable to find res_id: %u/%u/%u",
|
2022-02-23 23:43:52 -08:00
|
|
|
path->obj_id, path->obj_inst_id,
|
|
|
|
path->res_id);
|
2017-09-05 14:29:41 +08:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2018-08-27 13:57:23 -07:00
|
|
|
/* load object field data */
|
|
|
|
obj_field = lwm2m_get_engine_obj_field(obj,
|
|
|
|
obj_inst->resources[i].res_id);
|
|
|
|
if (!obj_field) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unable to find obj_field: %u/%u/%u",
|
2022-02-23 23:43:52 -08:00
|
|
|
path->obj_id, path->obj_inst_id,
|
|
|
|
path->res_id);
|
2018-08-27 13:57:23 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check for READ permission on matching resource */
|
|
|
|
if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
ret = update_attrs(&obj_inst->resources[i], attrs);
|
2018-05-07 14:18:37 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-02 16:29:17 +01:00
|
|
|
/* check if resource instance exists */
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
|
2022-02-23 23:43:52 -08:00
|
|
|
path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
|
|
|
|
ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
|
2022-02-02 16:29:17 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res_inst == NULL) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
ret = update_attrs(res_inst, attrs);
|
2022-02-02 16:29:17 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
attrs->pmax = (attrs->pmax >= attrs->pmin) ? (uint32_t)attrs->pmax : 0UL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
int engine_observe_attribute_list_get(sys_slist_t *path_list, struct notification_attrs *nattrs,
|
|
|
|
uint16_t server_obj_inst)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *o_p;
|
|
|
|
/* Temporary compare values */
|
|
|
|
int32_t pmin = 0;
|
|
|
|
int32_t pmax = 0;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(path_list, o_p, node) {
|
|
|
|
nattrs->pmin = 0;
|
|
|
|
nattrs->pmax = 0;
|
|
|
|
|
|
|
|
ret = engine_observe_get_attributes(&o_p->path, nattrs, server_obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nattrs->pmin) {
|
|
|
|
if (pmin == 0) {
|
|
|
|
pmin = nattrs->pmin;
|
|
|
|
} else {
|
|
|
|
pmin = MIN(pmin, nattrs->pmin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nattrs->pmax) {
|
|
|
|
if (pmax == 0) {
|
|
|
|
pmax = nattrs->pmax;
|
|
|
|
} else {
|
|
|
|
pmax = MIN(pmax, nattrs->pmax);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nattrs->pmin = pmin;
|
|
|
|
nattrs->pmax = pmax;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
int lwm2m_notify_observer_path(struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
struct notification_attrs nattrs = { 0 };
|
|
|
|
int64_t timestamp;
|
|
|
|
int ret = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look for observers which match our resource */
|
|
|
|
for (i = 0; i < sock_nfds; ++i) {
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
|
2022-02-28 07:42:53 -08:00
|
|
|
if (lwm2m_notify_observer_list(&obs->path_list, path)) {
|
2022-02-23 23:43:52 -08:00
|
|
|
/* update the event time for this observer */
|
2022-02-28 07:42:53 -08:00
|
|
|
ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs,
|
2022-02-23 23:43:52 -08:00
|
|
|
sock_ctx[i]->srv_obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2022-02-28 07:42:53 -08:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (nattrs.pmin) {
|
|
|
|
timestamp =
|
|
|
|
obs->last_timestamp + MSEC_PER_SEC * nattrs.pmin;
|
|
|
|
} else {
|
|
|
|
/* Trig immediately */
|
|
|
|
timestamp = k_uptime_get();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obs->event_timestamp || obs->event_timestamp > timestamp) {
|
|
|
|
obs->resource_update = true;
|
|
|
|
obs->event_timestamp = timestamp;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_DBG("NOTIFY EVENT %u/%u/%u", path->obj_id, path->obj_inst_id,
|
|
|
|
path->res_id);
|
|
|
|
ret++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static struct observe_node *engine_allocate_observer(sys_slist_t *path_list, bool composite)
|
2022-02-23 23:43:52 -08:00
|
|
|
{
|
|
|
|
int i;
|
2022-02-28 07:42:53 -08:00
|
|
|
struct lwm2m_obj_path_list *entry, *tmp;
|
|
|
|
struct observe_node *obs = NULL;
|
2022-02-23 23:43:52 -08:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* find an unused observer index node */
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
|
2021-05-25 22:55:59 +02:00
|
|
|
if (!observe_node_data[i].tkl) {
|
2022-02-28 07:42:53 -08:00
|
|
|
|
|
|
|
obs = &observe_node_data[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obs) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
sys_slist_init(&obs->path_list);
|
|
|
|
obs->composite = composite;
|
|
|
|
|
|
|
|
/* Allocate and copy path */
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(path_list, tmp, node) {
|
|
|
|
/* Allocate path entry */
|
|
|
|
entry = lwm2m_engine_get_from_list(&obs_obj_path_list);
|
|
|
|
if (!entry) {
|
|
|
|
/* Free list */
|
|
|
|
lwm2m_engine_free_list(&obs->path_list, &obs_obj_path_list);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* copy the values and add it to the list */
|
|
|
|
memcpy(&entry->path, &tmp->path, sizeof(tmp->path));
|
|
|
|
/* Add to last by keeping already sorted order */
|
|
|
|
sys_slist_append(&obs->path_list, &entry->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
return obs;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void engine_observe_node_init(struct observe_node *obs, const uint8_t *token,
|
|
|
|
struct lwm2m_ctx *ctx, uint8_t tkl, uint16_t format,
|
|
|
|
int32_t att_pmax)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *tmp;
|
|
|
|
|
|
|
|
memcpy(obs->token, token, tkl);
|
|
|
|
obs->tkl = tkl;
|
|
|
|
|
|
|
|
obs->last_timestamp = k_uptime_get();
|
|
|
|
if (att_pmax) {
|
|
|
|
obs->event_timestamp = obs->last_timestamp + MSEC_PER_SEC * att_pmax;
|
|
|
|
} else {
|
|
|
|
obs->event_timestamp = 0;
|
|
|
|
}
|
|
|
|
obs->resource_update = false;
|
|
|
|
obs->format = format;
|
|
|
|
obs->counter = OBSERVE_COUNTER_START;
|
|
|
|
sys_slist_append(&ctx->observer,
|
|
|
|
&obs->node);
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, tmp, node) {
|
|
|
|
LOG_DBG("OBSERVER ADDED %u/%u/%u/%u(%u)", tmp->path.obj_id, tmp->path.obj_inst_id,
|
|
|
|
tmp->path.res_id, tmp->path.res_inst_id, tmp->path.level);
|
|
|
|
|
|
|
|
if (ctx->observe_cb) {
|
|
|
|
ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_ADDED, &tmp->path, NULL);
|
2022-02-23 23:43:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
LOG_DBG("token:'%s' addr:%s", log_strdup(sprint_token(token, tkl)),
|
|
|
|
log_strdup(lwm2m_sprint_ip_addr(&ctx->remote_addr)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void remove_observer_path_from_list(struct lwm2m_ctx *ctx, struct observe_node *obs,
|
|
|
|
struct lwm2m_obj_path_list *o_p, sys_snode_t *prev_node)
|
|
|
|
{
|
|
|
|
char buf[LWM2M_MAX_PATH_STR_LEN];
|
|
|
|
|
|
|
|
LOG_DBG("Removing observer %p for path %s", obs, lwm2m_path_log_strdup(buf, &o_p->path));
|
|
|
|
if (ctx->observe_cb) {
|
|
|
|
ctx->observe_cb(LWM2M_OBSERVE_EVENT_OBSERVER_REMOVED, &o_p->path, NULL);
|
|
|
|
}
|
|
|
|
/* Remove from the list and add to free list */
|
|
|
|
sys_slist_remove(&obs->path_list, prev_node, &o_p->node);
|
|
|
|
sys_slist_append(&obs_obj_path_list, &o_p->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void engine_observe_single_path_id_remove(struct lwm2m_ctx *ctx, struct observe_node *obs,
|
|
|
|
uint16_t obj_id, int32_t obj_inst_id)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *o_p, *tmp;
|
|
|
|
sys_snode_t *prev_node = NULL;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) {
|
|
|
|
if (o_p->path.obj_id != obj_id && o_p->path.obj_inst_id) {
|
|
|
|
prev_node = &o_p->node;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj_inst_id == -1 || o_p->path.obj_inst_id == obj_inst_id) {
|
|
|
|
remove_observer_path_from_list(ctx, obs, o_p, prev_node);
|
|
|
|
} else {
|
|
|
|
prev_node = &o_p->node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool engine_compare_obs_path_list(sys_slist_t *obs_path_list, sys_slist_t *path_list,
|
|
|
|
int list_length)
|
|
|
|
{
|
|
|
|
sys_snode_t *obs_ptr, *comp_ptr;
|
|
|
|
struct lwm2m_obj_path_list *obs_path, *comp_path;
|
|
|
|
|
|
|
|
obs_ptr = sys_slist_peek_head(obs_path_list);
|
|
|
|
comp_ptr = sys_slist_peek_head(path_list);
|
|
|
|
while (list_length--) {
|
|
|
|
obs_path = (struct lwm2m_obj_path_list *) obs_ptr;
|
|
|
|
comp_path = (struct lwm2m_obj_path_list *) comp_ptr;
|
|
|
|
if (memcmp(&obs_path->path, &comp_path->path, sizeof(struct lwm2m_obj_path))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/* Read Next Info from list entry*/
|
|
|
|
obs_ptr = sys_slist_peek_next_no_check(obs_ptr);
|
|
|
|
comp_ptr = sys_slist_peek_next_no_check(comp_ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int engine_path_list_size(sys_slist_t *lwm2m_path_list)
|
|
|
|
{
|
|
|
|
int list_size = 0;
|
|
|
|
struct lwm2m_obj_path_list *entry;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
|
|
|
|
list_size++;
|
|
|
|
}
|
|
|
|
return list_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct observe_node *engine_observe_node_discover(sys_slist_t *observe_node_list,
|
|
|
|
sys_snode_t **prev_node,
|
|
|
|
sys_slist_t *lwm2m_path_list,
|
|
|
|
const uint8_t *token, uint8_t tkl)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
int obs_list_size, path_list_size;
|
|
|
|
|
|
|
|
if (lwm2m_path_list) {
|
|
|
|
path_list_size = engine_path_list_size(lwm2m_path_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
*prev_node = NULL;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(observe_node_list, obs, node) {
|
|
|
|
|
|
|
|
if (lwm2m_path_list) {
|
|
|
|
/* Validate Path for discovery */
|
|
|
|
obs_list_size = engine_path_list_size(&obs->path_list);
|
|
|
|
|
|
|
|
if (obs_list_size != path_list_size) {
|
|
|
|
*prev_node = &obs->node;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!engine_compare_obs_path_list(&obs->path_list, lwm2m_path_list,
|
|
|
|
obs_list_size)) {
|
|
|
|
*prev_node = &obs->node;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token && memcmp(obs->token, token, tkl)) {
|
|
|
|
/* Token not match */
|
|
|
|
*prev_node = &obs->node;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
return obs;
|
|
|
|
}
|
2022-02-23 23:43:52 -08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int engine_add_observer(struct lwm2m_message *msg,
|
|
|
|
const uint8_t *token, uint8_t tkl,
|
|
|
|
uint16_t format)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
struct notification_attrs attrs;
|
2022-02-28 07:42:53 -08:00
|
|
|
struct lwm2m_obj_path_list obs_path_list_buf;
|
|
|
|
sys_slist_t lwm2m_path_list;
|
|
|
|
sys_snode_t *prev_node = NULL;
|
2022-02-23 23:43:52 -08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!msg || !msg->ctx) {
|
|
|
|
LOG_ERR("valid lwm2m message is required");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
|
|
|
|
LOG_ERR("token(%p) and token length(%u) must be valid.",
|
|
|
|
token, tkl);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
/* Create 1 entry linked list for message path */
|
|
|
|
memcpy(&obs_path_list_buf.path, &msg->path, sizeof(struct lwm2m_obj_path));
|
|
|
|
sys_slist_init(&lwm2m_path_list);
|
|
|
|
sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node);
|
2022-02-23 23:43:52 -08:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL,
|
|
|
|
0);
|
|
|
|
if (obs) {
|
|
|
|
memcpy(obs->token, token, tkl);
|
|
|
|
obs->tkl = tkl;
|
2022-02-23 23:43:52 -08:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]", msg->path.obj_id,
|
|
|
|
msg->path.obj_inst_id, msg->path.res_id, msg->path.level,
|
|
|
|
log_strdup(lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)));
|
|
|
|
|
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
/* Read attributes and allocate new entry */
|
2022-02-23 23:43:52 -08:00
|
|
|
ret = engine_observe_get_attributes(&msg->path, &attrs, msg->ctx->srv_obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
obs = engine_allocate_observer(&lwm2m_path_list, false);
|
2022-02-23 23:43:52 -08:00
|
|
|
if (!obs) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax);
|
|
|
|
return 0;
|
|
|
|
}
|
2022-02-23 23:43:52 -08:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static int engine_add_composite_observer(struct lwm2m_message *msg,
|
|
|
|
const uint8_t *token, uint8_t tkl,
|
|
|
|
uint16_t format)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
struct notification_attrs attrs;
|
|
|
|
struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
|
|
|
|
sys_slist_t lwm2m_path_list;
|
|
|
|
sys_slist_t lwm2m_path_free_list;
|
|
|
|
sys_snode_t *prev_node = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!msg || !msg->ctx) {
|
|
|
|
LOG_ERR("valid lwm2m message is required");
|
|
|
|
return -EINVAL;
|
2022-02-23 23:43:52 -08:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
|
|
|
|
LOG_ERR("token(%p) and token length(%u) must be valid.",
|
|
|
|
token, tkl);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
/* Init list */
|
|
|
|
lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
|
|
|
|
CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
|
|
|
|
|
|
|
|
/* Read attributes and allocate new entry */
|
|
|
|
ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list,
|
|
|
|
&lwm2m_path_free_list);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, NULL,
|
|
|
|
0);
|
|
|
|
if (obs) {
|
|
|
|
memcpy(obs->token, token, tkl);
|
|
|
|
obs->tkl = tkl;
|
|
|
|
|
|
|
|
LOG_DBG("OBSERVER Composite DUPLICATE [%s]",
|
|
|
|
log_strdup(lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)));
|
|
|
|
|
|
|
|
return 0;
|
2021-09-28 10:11:49 +02:00
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
ret = engine_observe_attribute_list_get(&lwm2m_path_list, &attrs, msg->ctx->srv_obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
obs = engine_allocate_observer(&lwm2m_path_list, true);
|
|
|
|
if (!obs) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
engine_observe_node_init(obs, token, msg->ctx, tkl, format, attrs.pmax);
|
2017-07-07 11:04:03 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-09-28 10:11:49 +02:00
|
|
|
static void remove_observer_from_list(struct lwm2m_ctx *ctx, sys_snode_t *prev_node,
|
|
|
|
struct observe_node *obs)
|
|
|
|
{
|
2022-02-28 07:42:53 -08:00
|
|
|
struct lwm2m_obj_path_list *o_p, *tmp;
|
2021-09-28 10:11:49 +02:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&obs->path_list, o_p, tmp, node) {
|
|
|
|
remove_observer_path_from_list(ctx, obs, o_p, NULL);
|
2021-09-28 10:11:49 +02:00
|
|
|
}
|
|
|
|
sys_slist_remove(&ctx->observer, prev_node, &obs->node);
|
|
|
|
(void)memset(obs, 0, sizeof(*obs));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int engine_remove_observer_by_token(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2022-02-28 07:42:53 -08:00
|
|
|
struct observe_node *obs;
|
2017-09-05 11:03:47 +08:00
|
|
|
sys_snode_t *prev_node = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("token(%p) and token length(%u) must be valid.",
|
|
|
|
token, tkl);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
obs = engine_observe_node_discover(&ctx->observer, &prev_node, NULL, token, tkl);
|
|
|
|
if (!obs) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
remove_observer_from_list(ctx, prev_node, obs);
|
|
|
|
|
|
|
|
LOG_DBG("observer '%s' removed", log_strdup(sprint_token(token, tkl)));
|
2017-09-05 11:03:47 +08:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int engine_remove_composite_observer(struct lwm2m_message *msg, const uint8_t *token,
|
|
|
|
uint8_t tkl, uint16_t format)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
sys_snode_t *prev_node = NULL;
|
|
|
|
struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
|
|
|
|
sys_slist_t lwm2m_path_list;
|
|
|
|
sys_slist_t lwm2m_path_free_list;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
|
|
|
|
LOG_ERR("token(%p) and token length(%u) must be valid.",
|
|
|
|
token, tkl);
|
|
|
|
return -EINVAL;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
/* Init list */
|
|
|
|
lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
|
|
|
|
CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
|
|
|
|
|
|
|
|
ret = do_composite_observe_read_path_op(msg, format, &lwm2m_path_list,
|
|
|
|
&lwm2m_path_free_list);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, &lwm2m_path_list, token,
|
|
|
|
tkl);
|
|
|
|
if (!obs) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
remove_observer_from_list(msg->ctx, prev_node, obs);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_DBG("observer '%s' removed", log_strdup(sprint_token(token, tkl)));
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-03-03 11:43:05 +01:00
|
|
|
#if defined(CONFIG_LOG)
|
2021-04-07 13:15:44 +02:00
|
|
|
char *lwm2m_path_log_strdup(char *buf, struct lwm2m_obj_path *path)
|
2021-03-03 11:43:05 +01:00
|
|
|
{
|
2022-02-28 07:42:53 -08:00
|
|
|
size_t cur;
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
sprintf(buf, "/");
|
|
|
|
return log_strdup(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
cur = sprintf(buf, "%u", path->obj_id);
|
2021-03-03 11:43:05 +01:00
|
|
|
|
|
|
|
if (path->level > 1) {
|
|
|
|
cur += sprintf(buf + cur, "/%u", path->obj_inst_id);
|
|
|
|
}
|
|
|
|
if (path->level > 2) {
|
|
|
|
cur += sprintf(buf + cur, "/%u", path->res_id);
|
|
|
|
}
|
|
|
|
if (path->level > 3) {
|
|
|
|
cur += sprintf(buf + cur, "/%u", path->res_inst_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
return log_strdup(buf);
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_LOG */
|
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
|
2021-11-02 11:32:26 +01:00
|
|
|
static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx,
|
|
|
|
struct lwm2m_obj_path *path)
|
2021-03-03 11:43:05 +01:00
|
|
|
{
|
2021-04-07 13:15:44 +02:00
|
|
|
char buf[LWM2M_MAX_PATH_STR_LEN];
|
2022-02-28 07:42:53 -08:00
|
|
|
struct observe_node *obs;
|
|
|
|
struct lwm2m_obj_path_list obs_path_list_buf;
|
|
|
|
sys_slist_t lwm2m_path_list;
|
2021-03-03 11:43:05 +01:00
|
|
|
sys_snode_t *prev_node = NULL;
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
/* Create 1 entry linked list for message path */
|
|
|
|
memcpy(&obs_path_list_buf.path, path, sizeof(struct lwm2m_obj_path));
|
|
|
|
sys_slist_init(&lwm2m_path_list);
|
|
|
|
sys_slist_append(&lwm2m_path_list, &obs_path_list_buf.node);
|
2021-03-03 11:43:05 +01:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
obs = engine_observe_node_discover(&ctx->observer, &prev_node, &lwm2m_path_list, NULL, 0);
|
|
|
|
if (!obs) {
|
2021-03-03 11:43:05 +01:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2021-04-07 13:15:44 +02:00
|
|
|
LOG_INF("Removing observer for path %s",
|
|
|
|
lwm2m_path_log_strdup(buf, path));
|
2021-09-28 10:11:49 +02:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
remove_observer_from_list(ctx, prev_node, obs);
|
2021-03-03 11:43:05 +01:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id)
|
2017-09-05 13:49:21 +08:00
|
|
|
{
|
|
|
|
struct observe_node *obs, *tmp;
|
|
|
|
sys_snode_t *prev_node = NULL;
|
2021-05-25 22:55:59 +02:00
|
|
|
int i;
|
2017-09-05 13:49:21 +08:00
|
|
|
|
|
|
|
/* remove observer instances accordingly */
|
2021-05-25 22:55:59 +02:00
|
|
|
for (i = 0; i < sock_nfds; ++i) {
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(
|
|
|
|
&sock_ctx[i]->observer, obs, tmp, node) {
|
2022-02-28 07:42:53 -08:00
|
|
|
engine_observe_single_path_id_remove(sock_ctx[i], obs, obj_id,
|
|
|
|
obj_inst_id);
|
|
|
|
|
|
|
|
if (sys_slist_is_empty(&obs->path_list)) {
|
|
|
|
remove_observer_from_list(sock_ctx[i], prev_node, obs);
|
|
|
|
} else {
|
2021-05-25 22:55:59 +02:00
|
|
|
prev_node = &obs->node;
|
|
|
|
}
|
|
|
|
}
|
2017-09-05 13:49:21 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* engine object */
|
|
|
|
|
|
|
|
void lwm2m_register_obj(struct lwm2m_engine_obj *obj)
|
|
|
|
{
|
|
|
|
sys_slist_append(&engine_obj_list, &obj->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwm2m_unregister_obj(struct lwm2m_engine_obj *obj)
|
|
|
|
{
|
2017-09-05 13:49:21 +08:00
|
|
|
engine_remove_observer_by_id(obj->obj_id, -1);
|
2017-09-05 11:03:47 +08:00
|
|
|
sys_slist_find_and_remove(&engine_obj_list, &obj->node);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct lwm2m_engine_obj *get_engine_obj(int obj_id)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj *obj;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
|
|
|
|
if (obj->obj_id == obj_id) {
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lwm2m_engine_obj_field *
|
|
|
|
lwm2m_get_engine_obj_field(struct lwm2m_engine_obj *obj, int res_id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (obj && obj->fields && obj->field_count > 0) {
|
|
|
|
for (i = 0; i < obj->field_count; i++) {
|
|
|
|
if (obj->fields[i].res_id == res_id) {
|
|
|
|
return &obj->fields[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* engine object instance */
|
|
|
|
|
|
|
|
static void engine_register_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
|
|
|
|
{
|
|
|
|
sys_slist_append(&engine_obj_inst_list, &obj_inst->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void engine_unregister_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
|
|
|
|
{
|
2017-09-05 13:49:21 +08:00
|
|
|
engine_remove_observer_by_id(
|
|
|
|
obj_inst->obj->obj_id, obj_inst->obj_inst_id);
|
2017-09-05 11:03:47 +08:00
|
|
|
sys_slist_find_and_remove(&engine_obj_inst_list, &obj_inst->node);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
|
|
|
|
int obj_inst_id)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst,
|
|
|
|
node) {
|
|
|
|
if (obj_inst->obj->obj_id == obj_id &&
|
|
|
|
obj_inst->obj_inst_id == obj_inst_id) {
|
|
|
|
return obj_inst;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct lwm2m_engine_obj_inst *
|
2018-08-27 17:00:17 -07:00
|
|
|
next_engine_obj_inst(int obj_id, int obj_inst_id)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-08-27 17:00:17 -07:00
|
|
|
struct lwm2m_engine_obj_inst *obj_inst, *next = NULL;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst,
|
|
|
|
node) {
|
|
|
|
if (obj_inst->obj->obj_id == obj_id &&
|
|
|
|
obj_inst->obj_inst_id > obj_inst_id &&
|
|
|
|
(!next || next->obj_inst_id > obj_inst->obj_inst_id)) {
|
|
|
|
next = obj_inst;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-27 17:00:17 -07:00
|
|
|
return next;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int lwm2m_create_obj_inst(uint16_t obj_id, uint16_t obj_inst_id,
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_inst **obj_inst)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj *obj;
|
2018-07-12 10:00:01 -07:00
|
|
|
int ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
*obj_inst = NULL;
|
|
|
|
obj = get_engine_obj(obj_id);
|
|
|
|
if (!obj) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unable to find obj: %u", obj_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obj->create_cb) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("obj %u has no create_cb", obj_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj->instance_count + 1 > obj->max_instance_count) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("no more instances available for obj %u", obj_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
*obj_inst = obj->create_cb(obj_inst_id);
|
|
|
|
if (!*obj_inst) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unable to create obj %u instance %u",
|
|
|
|
obj_id, obj_inst_id);
|
2017-08-17 13:10:59 -07:00
|
|
|
/*
|
|
|
|
* Already checked for instance count total.
|
|
|
|
* This can only be an error if the object instance exists.
|
|
|
|
*/
|
|
|
|
return -EEXIST;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
obj->instance_count++;
|
|
|
|
(*obj_inst)->obj = obj;
|
|
|
|
(*obj_inst)->obj_inst_id = obj_inst_id;
|
|
|
|
engine_register_obj_inst(*obj_inst);
|
2018-07-12 10:00:01 -07:00
|
|
|
|
|
|
|
if (obj->user_create_cb) {
|
|
|
|
ret = obj->user_create_cb(obj_inst_id);
|
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Error in user obj create %u/%u: %d",
|
|
|
|
obj_id, obj_inst_id, ret);
|
2018-07-12 10:00:01 -07:00
|
|
|
lwm2m_delete_obj_inst(obj_id, obj_inst_id);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int lwm2m_delete_obj_inst(uint16_t obj_id, uint16_t obj_inst_id)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-09-04 16:59:37 +08:00
|
|
|
int i, ret = 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj *obj;
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
|
|
|
|
obj = get_engine_obj(obj_id);
|
|
|
|
if (!obj) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj_inst = get_engine_obj_inst(obj_id, obj_inst_id);
|
|
|
|
if (!obj_inst) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2018-07-12 10:00:01 -07:00
|
|
|
if (obj->user_delete_cb) {
|
|
|
|
ret = obj->user_delete_cb(obj_inst_id);
|
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Error in user obj delete %u/%u: %d",
|
|
|
|
obj_id, obj_inst_id, ret);
|
2018-07-12 10:00:01 -07:00
|
|
|
/* don't return error */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
engine_unregister_obj_inst(obj_inst);
|
|
|
|
obj->instance_count--;
|
2017-09-04 16:59:37 +08:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
if (obj->delete_cb) {
|
2017-09-04 16:59:37 +08:00
|
|
|
ret = obj->delete_cb(obj_inst_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2017-09-04 16:59:37 +08:00
|
|
|
/* reset obj_inst and res_inst data structure */
|
|
|
|
for (i = 0; i < obj_inst->resource_count; i++) {
|
2018-05-07 15:05:04 -07:00
|
|
|
clear_attrs(&obj_inst->resources[i]);
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(obj_inst->resources + i, 0,
|
2019-07-29 10:09:00 -07:00
|
|
|
sizeof(struct lwm2m_engine_res));
|
2017-09-04 16:59:37 +08:00
|
|
|
}
|
|
|
|
|
2018-05-07 15:05:04 -07:00
|
|
|
clear_attrs(obj_inst);
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(obj_inst, 0, sizeof(struct lwm2m_engine_obj_inst));
|
2017-09-04 16:59:37 +08:00
|
|
|
return ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* utility functions */
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
static int coap_options_to_path(struct coap_option *opt, int options_count,
|
|
|
|
struct lwm2m_obj_path *path)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t len, *id[4] = { &path->obj_id, &path->obj_inst_id,
|
2017-11-21 18:00:10 +08:00
|
|
|
&path->res_id, &path->res_inst_id };
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
path->level = options_count;
|
|
|
|
|
2017-11-21 18:00:10 +08:00
|
|
|
for (int i = 0; i < options_count; i++) {
|
2022-03-07 04:42:06 -08:00
|
|
|
*id[i] = lwm2m_atou16(opt[i].value, opt[i].len, &len);
|
2019-03-26 19:57:45 -06:00
|
|
|
if (len == 0U || opt[i].len != len) {
|
2017-11-21 18:00:10 +08:00
|
|
|
path->level = i;
|
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
2017-08-28 17:06:48 +08:00
|
|
|
|
|
|
|
return options_count == path->level ? 0 : -EINVAL;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
static struct lwm2m_message *find_msg(struct coap_pending *pending,
|
|
|
|
struct coap_reply *reply)
|
2017-09-01 01:11:43 -07:00
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
2017-09-05 21:03:49 -07:00
|
|
|
if (!pending && !reply) {
|
2017-09-01 01:11:43 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
|
2020-12-01 16:44:30 +01:00
|
|
|
if (pending != NULL && messages[i].ctx &&
|
|
|
|
messages[i].pending == pending) {
|
2017-09-01 01:11:43 -07:00
|
|
|
return &messages[i];
|
|
|
|
}
|
2017-09-05 21:03:49 -07:00
|
|
|
|
2020-12-01 16:44:30 +01:00
|
|
|
if (reply != NULL && messages[i].ctx &&
|
|
|
|
messages[i].reply == reply) {
|
2017-09-05 21:03:49 -07:00
|
|
|
return &messages[i];
|
|
|
|
}
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
|
|
|
|
if (!messages[i].ctx) {
|
|
|
|
messages[i].ctx = client_ctx;
|
|
|
|
return &messages[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-11 11:29:54 -07:00
|
|
|
void lwm2m_reset_message(struct lwm2m_message *msg, bool release)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-09-01 01:11:43 -07:00
|
|
|
if (!msg) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->pending) {
|
2017-09-19 16:04:32 -07:00
|
|
|
coap_pending_clear(msg->pending);
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->reply) {
|
|
|
|
/* make sure we want to clear the reply */
|
2017-09-19 16:04:32 -07:00
|
|
|
coap_reply_clear(msg->reply);
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
2022-02-09 19:04:39 +01:00
|
|
|
sys_slist_find_and_remove(&msg->ctx->pending_sends, &msg->node);
|
|
|
|
|
2017-10-11 11:29:54 -07:00
|
|
|
if (release) {
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(msg, 0, sizeof(*msg));
|
2017-10-11 11:29:54 -07:00
|
|
|
} else {
|
|
|
|
msg->message_timeout_cb = NULL;
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(&msg->cpkt, 0, sizeof(msg->cpkt));
|
2017-10-11 11:29:54 -07:00
|
|
|
}
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_init_message(struct lwm2m_message *msg)
|
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t tokenlen = 0U;
|
|
|
|
uint8_t *token = NULL;
|
2017-09-19 16:04:32 -07:00
|
|
|
int r = 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
if (!msg || !msg->ctx) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("LwM2M message is invalid.");
|
2017-09-01 01:11:43 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-09-10 15:41:31 +02:00
|
|
|
if (msg->tkl == LWM2M_MSG_TOKEN_GENERATE_NEW) {
|
2020-10-08 13:45:08 +02:00
|
|
|
tokenlen = 8U;
|
2017-09-19 16:04:32 -07:00
|
|
|
token = coap_next_token();
|
2020-10-08 13:45:08 +02:00
|
|
|
} else if (msg->token && msg->tkl != 0) {
|
2017-09-19 16:04:32 -07:00
|
|
|
tokenlen = msg->tkl;
|
|
|
|
token = msg->token;
|
|
|
|
}
|
|
|
|
|
2019-01-25 14:13:07 -08:00
|
|
|
r = coap_packet_init(&msg->cpkt, msg->msg_data, sizeof(msg->msg_data),
|
2021-01-27 19:24:18 +01:00
|
|
|
COAP_VERSION_1, msg->type, tokenlen, token,
|
|
|
|
msg->code, msg->mid);
|
2017-09-19 16:04:32 -07:00
|
|
|
if (r < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("coap packet init error (err:%d)", r);
|
2017-09-19 16:04:32 -07:00
|
|
|
goto cleanup;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
/* only TYPE_CON messages need pending tracking / reply handling */
|
2017-09-19 16:04:32 -07:00
|
|
|
if (msg->type != COAP_TYPE_CON) {
|
2017-09-01 01:11:43 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->pending = coap_pending_next_unused(
|
2017-09-01 01:11:43 -07:00
|
|
|
msg->ctx->pendings,
|
|
|
|
CONFIG_LWM2M_ENGINE_MAX_PENDING);
|
|
|
|
if (!msg->pending) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unable to find a free pending to track "
|
|
|
|
"retransmissions.");
|
2017-09-01 01:11:43 -07:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto cleanup;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-01-18 13:13:26 +01:00
|
|
|
r = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr,
|
|
|
|
COAP_DEFAULT_MAX_RETRANSMIT);
|
2017-09-01 01:11:43 -07:00
|
|
|
if (r < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unable to initialize a pending "
|
|
|
|
"retransmission (err:%d).", r);
|
2017-09-01 01:11:43 -07:00
|
|
|
goto cleanup;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
if (msg->reply_cb) {
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->reply = coap_reply_next_unused(
|
2017-09-01 01:11:43 -07:00
|
|
|
msg->ctx->replies,
|
|
|
|
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
|
|
|
|
if (!msg->reply) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("No resources for waiting for replies.");
|
2017-09-01 01:11:43 -07:00
|
|
|
r = -ENOMEM;
|
|
|
|
goto cleanup;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2018-03-23 10:49:44 -07:00
|
|
|
coap_reply_clear(msg->reply);
|
2017-09-19 16:04:32 -07:00
|
|
|
coap_reply_init(msg->reply, &msg->cpkt);
|
2017-09-01 01:11:43 -07:00
|
|
|
msg->reply->reply = msg->reply_cb;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
2017-10-11 11:29:54 -07:00
|
|
|
lwm2m_reset_message(msg, true);
|
2017-09-01 01:11:43 -07:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
int lwm2m_send_message_async(struct lwm2m_message *msg)
|
2017-09-01 01:11:43 -07:00
|
|
|
{
|
2021-05-17 16:23:51 +02:00
|
|
|
sys_slist_append(&msg->ctx->pending_sends, &msg->node);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lwm2m_send_message(struct lwm2m_message *msg)
|
|
|
|
{
|
|
|
|
int rc;
|
2020-06-30 12:16:55 +02:00
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
if (!msg || !msg->ctx) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("LwM2M message is invalid.");
|
2017-09-01 01:11:43 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-01-30 13:39:34 -08:00
|
|
|
if (msg->type == COAP_TYPE_CON) {
|
|
|
|
coap_pending_cycle(msg->pending);
|
|
|
|
}
|
2017-12-08 11:55:01 +08:00
|
|
|
|
2020-06-30 12:16:55 +02:00
|
|
|
rc = send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
|
2021-05-17 16:23:51 +02:00
|
|
|
|
2020-06-30 12:16:55 +02:00
|
|
|
if (rc < 0) {
|
|
|
|
LOG_ERR("Failed to send packet, err %d", errno);
|
2021-05-17 16:23:51 +02:00
|
|
|
if (msg->type != COAP_TYPE_CON) {
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
}
|
2020-06-30 12:16:55 +02:00
|
|
|
|
2019-02-12 17:10:41 -08:00
|
|
|
return -errno;
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
if (msg->type != COAP_TYPE_CON) {
|
2017-10-11 11:29:54 -07:00
|
|
|
lwm2m_reset_message(msg, true);
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
2020-02-24 16:44:52 +01:00
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT) &&
|
|
|
|
IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
|
|
|
|
engine_update_tx_time();
|
|
|
|
}
|
|
|
|
|
2019-02-12 17:10:41 -08:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2020-10-13 14:08:17 +02:00
|
|
|
int lwm2m_send_empty_ack(struct lwm2m_ctx *client_ctx, uint16_t mid)
|
|
|
|
{
|
|
|
|
struct lwm2m_message *msg;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
msg = lwm2m_get_message(client_ctx);
|
|
|
|
if (!msg) {
|
|
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg->type = COAP_TYPE_ACK;
|
|
|
|
msg->code = COAP_CODE_EMPTY;
|
|
|
|
msg->mid = mid;
|
|
|
|
|
|
|
|
ret = lwm2m_init_message(msg);
|
|
|
|
if (ret) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
lwm2m_send_message_async(msg);
|
2020-10-13 14:08:17 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-12-01 13:56:34 +01:00
|
|
|
void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx)
|
|
|
|
{
|
|
|
|
struct lwm2m_message *request;
|
|
|
|
|
|
|
|
if (client_ctx == NULL || client_ctx->processed_req == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
request = (struct lwm2m_message *)client_ctx->processed_req;
|
|
|
|
|
|
|
|
if (request->acknowledged) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
request->acknowledged = true;
|
|
|
|
}
|
|
|
|
|
2021-01-29 11:35:25 +01:00
|
|
|
int lwm2m_register_payload_handler(struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj *obj;
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
2021-01-29 11:35:25 +01:00
|
|
|
int ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_begin(&msg->out, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-08-14 17:06:03 +08:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
|
2017-07-10 18:19:35 +08:00
|
|
|
/* Security obj MUST NOT be part of registration message */
|
|
|
|
if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:14:21 +01:00
|
|
|
/* Only report <OBJ_ID> when no instance available or it's
|
|
|
|
* needed to report object version.
|
|
|
|
*/
|
|
|
|
if (obj->instance_count == 0U ||
|
|
|
|
lwm2m_engine_shall_report_obj_version(obj)) {
|
2021-01-29 11:35:25 +01:00
|
|
|
struct lwm2m_obj_path path = {
|
|
|
|
.obj_id = obj->obj_id,
|
|
|
|
.level = LWM2M_PATH_LEVEL_OBJECT,
|
|
|
|
};
|
|
|
|
|
|
|
|
ret = engine_put_corelink(&msg->out, &path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-08-14 16:16:11 +08:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2021-02-02 13:14:21 +01:00
|
|
|
if (obj->instance_count == 0U) {
|
|
|
|
continue;
|
|
|
|
}
|
2017-08-14 16:16:11 +08:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list,
|
|
|
|
obj_inst, node) {
|
|
|
|
if (obj_inst->obj->obj_id == obj->obj_id) {
|
2021-01-29 11:35:25 +01:00
|
|
|
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;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-29 11:35:25 +01:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* input / output selection */
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static int select_writer(struct lwm2m_output_context *out, uint16_t accept)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
switch (accept) {
|
|
|
|
|
2017-10-15 13:42:45 -07:00
|
|
|
case LWM2M_FORMAT_APP_LINK_FORMAT:
|
2021-01-25 13:49:33 +01:00
|
|
|
out->writer = &link_format_writer;
|
2017-10-15 13:42:45 -07:00
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
case LWM2M_FORMAT_PLAIN_TEXT:
|
|
|
|
case LWM2M_FORMAT_OMA_PLAIN_TEXT:
|
|
|
|
out->writer = &plain_text_writer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_FORMAT_OMA_TLV:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_TLV:
|
|
|
|
out->writer = &oma_tlv_writer;
|
|
|
|
break;
|
|
|
|
|
|
|
|
#ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
|
|
|
|
case LWM2M_FORMAT_OMA_JSON:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_JSON:
|
|
|
|
out->writer = &json_writer;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
out->writer = &senml_json_writer;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_WRN("Unknown content type %u", accept);
|
2018-02-05 16:05:51 +08:00
|
|
|
return -ENOMSG;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2018-02-05 16:05:51 +08:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static int select_reader(struct lwm2m_input_context *in, uint16_t format)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
|
2017-10-30 15:18:35 +08:00
|
|
|
case LWM2M_FORMAT_APP_OCTET_STREAM:
|
2017-07-05 10:54:31 +08:00
|
|
|
case LWM2M_FORMAT_PLAIN_TEXT:
|
2017-07-07 11:04:03 -07:00
|
|
|
case LWM2M_FORMAT_OMA_PLAIN_TEXT:
|
|
|
|
in->reader = &plain_text_reader;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_FORMAT_OMA_TLV:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_TLV:
|
|
|
|
in->reader = &oma_tlv_reader;
|
|
|
|
break;
|
|
|
|
|
2019-01-25 15:56:23 -08:00
|
|
|
#ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
|
|
|
|
case LWM2M_FORMAT_OMA_JSON:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_JSON:
|
|
|
|
in->reader = &json_reader;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
in->reader = &senml_json_reader;
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_WRN("Unknown content type %u", format);
|
2018-01-30 15:18:57 +08:00
|
|
|
return -ENOMSG;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2018-02-05 16:05:51 +08:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* user data setter functions */
|
|
|
|
|
2018-04-30 15:02:04 -07:00
|
|
|
static int path_to_objs(const struct lwm2m_obj_path *path,
|
|
|
|
struct lwm2m_engine_obj_inst **obj_inst,
|
|
|
|
struct lwm2m_engine_obj_field **obj_field,
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res **res,
|
|
|
|
struct lwm2m_engine_res_inst **res_inst)
|
2018-04-30 15:02:04 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *oi;
|
|
|
|
struct lwm2m_engine_obj_field *of;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *r = NULL;
|
|
|
|
struct lwm2m_engine_res_inst *ri = NULL;
|
2018-04-30 15:02:04 -07:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
oi = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
|
|
|
|
if (!oi) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("obj instance %d/%d not found",
|
|
|
|
path->obj_id, path->obj_inst_id);
|
2018-04-30 15:02:04 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
if (!oi->resources || oi->resource_count == 0U) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("obj instance has no resources");
|
2018-04-30 15:02:04 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
of = lwm2m_get_engine_obj_field(oi->obj, path->res_id);
|
|
|
|
if (!of) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("obj field %d not found", path->res_id);
|
2018-04-30 15:02:04 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < oi->resource_count; i++) {
|
|
|
|
if (oi->resources[i].res_id == path->res_id) {
|
|
|
|
r = &oi->resources[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r) {
|
2019-07-29 10:09:00 -07:00
|
|
|
LOG_ERR("resource %d not found", path->res_id);
|
2018-04-30 15:02:04 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
for (i = 0; i < r->res_inst_count; i++) {
|
|
|
|
if (r->res_instances[i].res_inst_id == path->res_inst_id) {
|
|
|
|
ri = &r->res_instances[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* specifically don't complain about missing resource instance */
|
|
|
|
|
2018-04-30 15:02:04 -07:00
|
|
|
if (obj_inst) {
|
|
|
|
*obj_inst = oi;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj_field) {
|
|
|
|
*obj_field = of;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
*res = r;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (ri && res_inst) {
|
|
|
|
*res_inst = ri;
|
|
|
|
}
|
|
|
|
|
2018-04-30 15:02:04 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-27 14:31:26 +01:00
|
|
|
struct lwm2m_attr *lwm2m_engine_get_next_attr(const void *ref,
|
|
|
|
struct lwm2m_attr *prev)
|
|
|
|
{
|
|
|
|
struct lwm2m_attr *iter = (prev == NULL) ? write_attr_pool : prev + 1;
|
|
|
|
struct lwm2m_attr *result = NULL;
|
|
|
|
|
|
|
|
if (!PART_OF_ARRAY(write_attr_pool, iter)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (iter < &write_attr_pool[ARRAY_SIZE(write_attr_pool)]) {
|
|
|
|
if (ref == iter->ref) {
|
|
|
|
result = iter;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
++iter;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr)
|
|
|
|
{
|
|
|
|
if (attr->type >= NR_LWM2M_ATTR) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return LWM2M_ATTR_STR[attr->type];
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_create_obj_inst(const char *pathstr)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
int ret = 0;
|
|
|
|
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_DBG("path:%s", log_strdup(pathstr));
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* translate path -> path_obj */
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-03-26 19:57:45 -06:00
|
|
|
if (path.level != 2U) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("path must have 2 parts");
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-04-15 12:46:15 +02:00
|
|
|
ret = lwm2m_create_obj_inst(path.obj_id, path.obj_inst_id, &obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
|
|
|
|
engine_trigger_update(true);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_delete_obj_inst(const char *pathstr)
|
2021-04-15 12:43:40 +02:00
|
|
|
{
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
LOG_DBG("path: %s", log_strdup(pathstr));
|
|
|
|
|
|
|
|
/* translate path -> path_obj */
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2021-04-15 12:43:40 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level != 2U) {
|
|
|
|
LOG_ERR("path must have 2 parts");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lwm2m_delete_obj_inst(path.obj_id, path.obj_inst_id);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
|
|
|
|
engine_trigger_update(true);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_res_data(const char *pathstr, void *data_ptr, uint16_t data_len,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t data_flags)
|
2018-04-30 16:43:25 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_obj_path path;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
2018-04-30 16:43:25 -07:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* translate path -> path_obj */
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2018-04-30 16:43:25 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 3) {
|
2019-07-29 10:09:00 -07:00
|
|
|
LOG_ERR("path must have at least 3 parts");
|
2018-04-30 16:43:25 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look up resource obj */
|
2019-07-29 10:09:00 -07:00
|
|
|
ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
|
2018-04-30 16:43:25 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (!res_inst) {
|
|
|
|
LOG_ERR("res instance %d not found", path.res_inst_id);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2018-04-30 16:43:25 -07:00
|
|
|
/* assign data elements */
|
2019-07-29 10:09:00 -07:00
|
|
|
res_inst->data_ptr = data_ptr;
|
|
|
|
res_inst->data_len = data_len;
|
2020-09-24 15:28:10 +02:00
|
|
|
res_inst->max_data_len = data_len;
|
2019-07-29 10:09:00 -07:00
|
|
|
res_inst->data_flags = data_flags;
|
2018-04-30 16:43:25 -07:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
static int lwm2m_engine_set(const char *pathstr, void *value, uint16_t len)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
struct lwm2m_engine_obj_field *obj_field;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
void *data_ptr = NULL;
|
2020-09-24 15:28:10 +02:00
|
|
|
size_t max_data_len = 0;
|
2018-04-30 15:02:04 -07:00
|
|
|
int ret = 0;
|
|
|
|
bool changed = false;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_DBG("path:%s, value:%p, len:%d", log_strdup(pathstr), value, len);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* translate path -> path_obj */
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 3) {
|
2019-07-29 10:09:00 -07:00
|
|
|
LOG_ERR("path must have at least 3 parts");
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-30 15:02:04 -07:00
|
|
|
/* look up resource obj */
|
2019-07-29 10:09:00 -07:00
|
|
|
ret = path_to_objs(&path, &obj_inst, &obj_field, &res, &res_inst);
|
2018-04-30 15:02:04 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (!res_inst) {
|
|
|
|
LOG_ERR("res instance %d not found", path.res_inst_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
|
2019-07-29 12:05:00 -07:00
|
|
|
LOG_ERR("res instance data pointer is read-only "
|
|
|
|
"[%u/%u/%u/%u:%u]", path.obj_id, path.obj_inst_id,
|
|
|
|
path.res_id, path.res_inst_id, path.level);
|
2018-04-30 16:43:25 -07:00
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* setup initial data elements */
|
2019-07-29 10:09:00 -07:00
|
|
|
data_ptr = res_inst->data_ptr;
|
2020-09-24 15:28:10 +02:00
|
|
|
max_data_len = res_inst->max_data_len;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* allow user to override data elements via callback */
|
|
|
|
if (res->pre_write_cb) {
|
2019-07-29 10:09:00 -07:00
|
|
|
data_ptr = res->pre_write_cb(obj_inst->obj_inst_id,
|
|
|
|
res->res_id, res_inst->res_inst_id,
|
2020-09-24 15:28:10 +02:00
|
|
|
&max_data_len);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!data_ptr) {
|
2019-07-29 12:05:00 -07:00
|
|
|
LOG_ERR("res instance data pointer is NULL [%u/%u/%u/%u:%u]",
|
|
|
|
path.obj_id, path.obj_inst_id, path.res_id,
|
|
|
|
path.res_inst_id, path.level);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check length (note: we add 1 to string length for NULL pad) */
|
2020-12-09 02:12:51 +01:00
|
|
|
if (len > max_data_len -
|
2017-07-07 11:04:03 -07:00
|
|
|
(obj_field->data_type == LWM2M_RES_TYPE_STRING ? 1 : 0)) {
|
2019-07-29 10:09:00 -07:00
|
|
|
LOG_ERR("length %u is too long for res instance %d data",
|
2018-09-19 11:22:19 +03:00
|
|
|
len, path.res_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (memcmp(data_ptr, value, len) != 0) {
|
|
|
|
changed = true;
|
|
|
|
}
|
|
|
|
|
2021-03-10 13:14:31 +01:00
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
2021-02-17 15:47:28 +01:00
|
|
|
if (res->validate_cb) {
|
|
|
|
ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
|
|
|
|
res_inst->res_inst_id, value,
|
|
|
|
len, false, 0);
|
|
|
|
if (ret < 0) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 13:14:31 +01:00
|
|
|
#endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
|
2021-02-17 15:47:28 +01:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
switch (obj_field->data_type) {
|
|
|
|
|
2017-10-26 23:45:59 -07:00
|
|
|
case LWM2M_RES_TYPE_OPAQUE:
|
2020-05-27 11:26:57 -05:00
|
|
|
memcpy((uint8_t *)data_ptr, value, len);
|
2017-10-26 23:45:59 -07:00
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
case LWM2M_RES_TYPE_STRING:
|
2020-05-27 11:26:57 -05:00
|
|
|
memcpy((uint8_t *)data_ptr, value, len);
|
|
|
|
((uint8_t *)data_ptr)[len] = '\0';
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U32:
|
|
|
|
case LWM2M_RES_TYPE_TIME:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((uint32_t *)data_ptr) = *(uint32_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U16:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((uint16_t *)data_ptr) = *(uint16_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U8:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((uint8_t *)data_ptr) = *(uint8_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S64:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((int64_t *)data_ptr) = *(int64_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S32:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((int32_t *)data_ptr) = *(int32_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S16:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((int16_t *)data_ptr) = *(int16_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S8:
|
2020-05-27 11:26:57 -05:00
|
|
|
*((int8_t *)data_ptr) = *(int8_t *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_BOOL:
|
|
|
|
*((bool *)data_ptr) = *(bool *)value;
|
|
|
|
break;
|
|
|
|
|
2021-07-20 16:48:33 +02:00
|
|
|
case LWM2M_RES_TYPE_FLOAT:
|
2021-07-23 16:38:23 +02:00
|
|
|
*(double *)data_ptr = *(double *)value;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2020-05-19 12:37:02 +02:00
|
|
|
case LWM2M_RES_TYPE_OBJLNK:
|
|
|
|
*((struct lwm2m_objlnk *)data_ptr) =
|
|
|
|
*(struct lwm2m_objlnk *)value;
|
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unknown obj data_type %d", obj_field->data_type);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-09-24 15:28:10 +02:00
|
|
|
res_inst->data_len = len;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
if (res->post_write_cb) {
|
2019-07-29 10:09:00 -07:00
|
|
|
ret = res->post_write_cb(obj_inst->obj_inst_id,
|
|
|
|
res->res_id, res_inst->res_inst_id,
|
|
|
|
data_ptr, len, false, 0);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-07-15 15:25:21 +02:00
|
|
|
if (changed && LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
|
2017-07-07 11:04:03 -07:00
|
|
|
NOTIFY_OBSERVER_PATH(&path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_opaque(const char *pathstr, char *data_ptr, uint16_t data_len)
|
2017-10-26 23:45:59 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, data_ptr, data_len);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_string(const char *pathstr, char *data_ptr)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, data_ptr, strlen(data_ptr));
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_u8(const char *pathstr, uint8_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 1);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_u16(const char *pathstr, uint16_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 2);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_u32(const char *pathstr, uint32_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 4);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_u64(const char *pathstr, uint64_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 8);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_s8(const char *pathstr, int8_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 1);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_s16(const char *pathstr, int16_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 2);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_s32(const char *pathstr, int32_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 4);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_s64(const char *pathstr, int64_t value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, &value, 8);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_bool(const char *pathstr, bool value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t temp = (value != 0 ? 1 : 0);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
return lwm2m_engine_set(pathstr, &temp, 1);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_float(const char *pathstr, double *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2021-07-23 16:38:23 +02:00
|
|
|
return lwm2m_engine_set(pathstr, value, sizeof(double));
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_set_objlnk(const char *pathstr, struct lwm2m_objlnk *value)
|
2020-05-19 12:37:02 +02:00
|
|
|
{
|
|
|
|
return lwm2m_engine_set(pathstr, value, sizeof(struct lwm2m_objlnk));
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* user data getter functions */
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_res_data(const char *pathstr, void **data_ptr, uint16_t *data_len,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *data_flags)
|
2018-04-30 16:43:25 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_obj_path path;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
2018-04-30 16:43:25 -07:00
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* translate path -> path_obj */
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2018-04-30 16:43:25 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 3) {
|
2019-07-29 10:09:00 -07:00
|
|
|
LOG_ERR("path must have at least 3 parts");
|
2018-04-30 16:43:25 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* look up resource obj */
|
2019-07-29 10:09:00 -07:00
|
|
|
ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
|
2018-04-30 16:43:25 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (!res_inst) {
|
|
|
|
LOG_ERR("res instance %d not found", path.res_inst_id);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data_ptr = res_inst->data_ptr;
|
|
|
|
*data_len = res_inst->data_len;
|
|
|
|
*data_flags = res_inst->data_flags;
|
2018-04-30 16:43:25 -07:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
static int lwm2m_engine_get(const char *pathstr, void *buf, uint16_t buflen)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 15:02:04 -07:00
|
|
|
int ret = 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
struct lwm2m_engine_obj_field *obj_field;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
void *data_ptr = NULL;
|
|
|
|
size_t data_len = 0;
|
|
|
|
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_DBG("path:%s, buf:%p, buflen:%d", log_strdup(pathstr), buf, buflen);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* translate path -> path_obj */
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 3) {
|
2019-07-29 10:09:00 -07:00
|
|
|
LOG_ERR("path must have at least 3 parts");
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-04-30 15:02:04 -07:00
|
|
|
/* look up resource obj */
|
2019-07-29 10:09:00 -07:00
|
|
|
ret = path_to_objs(&path, &obj_inst, &obj_field, &res, &res_inst);
|
2018-04-30 15:02:04 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (!res_inst) {
|
|
|
|
LOG_ERR("res instance %d not found", path.res_inst_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup initial data elements */
|
2019-07-29 10:09:00 -07:00
|
|
|
data_ptr = res_inst->data_ptr;
|
|
|
|
data_len = res_inst->data_len;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* allow user to override data elements via callback */
|
|
|
|
if (res->read_cb) {
|
2019-07-29 10:09:00 -07:00
|
|
|
data_ptr = res->read_cb(obj_inst->obj_inst_id,
|
|
|
|
res->res_id, res_inst->res_inst_id,
|
|
|
|
&data_len);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: handle data_len > buflen case */
|
|
|
|
|
|
|
|
if (data_ptr && data_len > 0) {
|
|
|
|
switch (obj_field->data_type) {
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_OPAQUE:
|
2017-10-26 23:45:59 -07:00
|
|
|
if (data_len > buflen) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(buf, data_ptr, data_len);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_STRING:
|
2020-05-27 11:26:57 -05:00
|
|
|
strncpy((uint8_t *)buf, (uint8_t *)data_ptr, buflen);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U32:
|
|
|
|
case LWM2M_RES_TYPE_TIME:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(uint32_t *)buf = *(uint32_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U16:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(uint16_t *)buf = *(uint16_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U8:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(uint8_t *)buf = *(uint8_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S64:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(int64_t *)buf = *(int64_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S32:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(int32_t *)buf = *(int32_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S16:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(int16_t *)buf = *(int16_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S8:
|
2020-05-27 11:26:57 -05:00
|
|
|
*(int8_t *)buf = *(int8_t *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_BOOL:
|
|
|
|
*(bool *)buf = *(bool *)data_ptr;
|
|
|
|
break;
|
|
|
|
|
2021-07-20 16:48:33 +02:00
|
|
|
case LWM2M_RES_TYPE_FLOAT:
|
2021-07-23 16:38:23 +02:00
|
|
|
*(double *)buf = *(double *)data_ptr;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2020-05-19 12:37:02 +02:00
|
|
|
case LWM2M_RES_TYPE_OBJLNK:
|
|
|
|
*(struct lwm2m_objlnk *)buf =
|
|
|
|
*(struct lwm2m_objlnk *)data_ptr;
|
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unknown obj data_type %d",
|
|
|
|
obj_field->data_type);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_opaque(const char *pathstr, void *buf, uint16_t buflen)
|
2017-10-26 23:45:59 -07:00
|
|
|
{
|
|
|
|
return lwm2m_engine_get(pathstr, buf, buflen);
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_string(const char *pathstr, void *buf, uint16_t buflen)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-10-11 12:48:06 -07:00
|
|
|
return lwm2m_engine_get(pathstr, buf, buflen);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_u8(const char *pathstr, uint8_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 1);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_u16(const char *pathstr, uint16_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 2);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_u32(const char *pathstr, uint32_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 4);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_u64(const char *pathstr, uint64_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 8);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_s8(const char *pathstr, int8_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 1);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_s16(const char *pathstr, int16_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 2);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_s32(const char *pathstr, int32_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 4);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_s64(const char *pathstr, int64_t *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
return lwm2m_engine_get(pathstr, value, 8);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_bool(const char *pathstr, bool *value)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2018-04-30 16:38:36 -07:00
|
|
|
int ret = 0;
|
2020-05-27 11:26:57 -05:00
|
|
|
int8_t temp = 0;
|
2018-04-30 16:38:36 -07:00
|
|
|
|
|
|
|
ret = lwm2m_engine_get_s8(pathstr, &temp);
|
|
|
|
if (!ret) {
|
|
|
|
*value = temp != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_float(const char *pathstr, double *buf)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2021-07-23 16:38:23 +02:00
|
|
|
return lwm2m_engine_get(pathstr, buf, sizeof(double));
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_objlnk(const char *pathstr, struct lwm2m_objlnk *buf)
|
2020-05-19 12:37:02 +02:00
|
|
|
{
|
|
|
|
return lwm2m_engine_get(pathstr, buf, sizeof(struct lwm2m_objlnk));
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_get_resource(const char *pathstr, struct lwm2m_engine_res **res)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 3) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("path must have 3 parts");
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
return path_to_objs(&path, NULL, NULL, res, NULL);
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
static int lwm2m_update_or_allocate_attribute(void *ref, uint8_t type, void *data)
|
|
|
|
{
|
|
|
|
struct lwm2m_attr *attr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* find matching attributes */
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
|
|
|
|
if (ref != write_attr_pool[i].ref || write_attr_pool[i].type != type) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = write_attr_pool + i;
|
|
|
|
type = attr->type;
|
|
|
|
|
|
|
|
if (type <= LWM2M_ATTR_PMAX) {
|
|
|
|
attr->int_val = *(int32_t *)data;
|
|
|
|
LOG_DBG("Update %s to %d", log_strdup(LWM2M_ATTR_STR[type]),
|
|
|
|
attr->int_val);
|
|
|
|
} else {
|
|
|
|
attr->float_val = *(double *)data;
|
|
|
|
LOG_DBG("Update %s to %f", log_strdup(LWM2M_ATTR_STR[type]),
|
|
|
|
attr->float_val);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* add attribute to obj/obj_inst/res/res_inst */
|
|
|
|
/* grab an entry for newly added attribute */
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
|
|
|
|
if (!write_attr_pool[i].ref) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == CONFIG_LWM2M_NUM_ATTR) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = write_attr_pool + i;
|
|
|
|
attr->type = type;
|
|
|
|
attr->ref = ref;
|
|
|
|
|
|
|
|
if (type <= LWM2M_ATTR_PMAX) {
|
|
|
|
attr->int_val = *(int32_t *)data;
|
|
|
|
LOG_DBG("Add %s to %d", log_strdup(LWM2M_ATTR_STR[type]), attr->int_val);
|
|
|
|
} else {
|
|
|
|
attr->float_val = *(double *)data;
|
|
|
|
LOG_DBG("Add %s to %f", log_strdup(LWM2M_ATTR_STR[type]), attr->float_val);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lwm2m_engine_observer_timestamp_update(sys_slist_t *observer,
|
|
|
|
struct lwm2m_obj_path *path,
|
|
|
|
uint16_t srv_obj_inst)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
struct notification_attrs nattrs = { 0 };
|
|
|
|
int ret;
|
|
|
|
int64_t timestamp;
|
|
|
|
|
|
|
|
/* update observe_node accordingly */
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(observer, obs, node) {
|
|
|
|
if (!obs->resource_update) {
|
|
|
|
/* Resource Update on going skip this*/
|
|
|
|
continue;
|
|
|
|
}
|
2022-03-16 21:07:43 +00:00
|
|
|
/* Compare Observation node path to updated one */
|
2022-02-28 07:42:53 -08:00
|
|
|
if (!lwm2m_notify_observer_list(&obs->path_list, path)) {
|
2022-02-23 23:43:52 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-03-16 21:07:43 +00:00
|
|
|
/* Read Attributes after validation Path */
|
2022-02-28 07:42:53 -08:00
|
|
|
ret = engine_observe_attribute_list_get(&obs->path_list, &nattrs, srv_obj_inst);
|
2022-02-23 23:43:52 -08:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update based on by PMax */
|
|
|
|
if (nattrs.pmax) {
|
|
|
|
/* Update Current */
|
|
|
|
timestamp = obs->last_timestamp + MSEC_PER_SEC * nattrs.pmax;
|
|
|
|
} else {
|
|
|
|
/* Disable Automatic Notify */
|
|
|
|
timestamp = 0;
|
|
|
|
}
|
|
|
|
obs->event_timestamp = timestamp;
|
|
|
|
|
|
|
|
(void)memset(&nattrs, 0, sizeof(nattrs));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lwm2m_get_path_reference_ptr(struct lwm2m_engine_obj *obj, struct lwm2m_obj_path *path,
|
|
|
|
void **ref)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
struct lwm2m_engine_res *res;
|
|
|
|
struct lwm2m_engine_res_inst *res_inst;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (!obj) {
|
|
|
|
/* Discover Object */
|
|
|
|
obj = get_engine_obj(path->obj_id);
|
|
|
|
if (!obj) {
|
|
|
|
/* No matching object found - ignore request */
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path->level == LWM2M_PATH_LEVEL_OBJECT) {
|
|
|
|
*ref = obj;
|
|
|
|
} else if (path->level == LWM2M_PATH_LEVEL_OBJECT_INST) {
|
|
|
|
obj_inst = get_engine_obj_inst(path->obj_id,
|
|
|
|
path->obj_inst_id);
|
|
|
|
if (!obj_inst) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ref = obj_inst;
|
|
|
|
} else if (path->level == LWM2M_PATH_LEVEL_RESOURCE) {
|
|
|
|
ret = path_to_objs(path, NULL, NULL, &res, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ref = res;
|
|
|
|
} else if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
|
|
|
|
path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
|
|
|
|
|
|
|
|
ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ref = res_inst;
|
|
|
|
} else {
|
|
|
|
/* bad request */
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_engine_update_observer_min_period(struct lwm2m_ctx *client_ctx, const char *pathstr,
|
|
|
|
uint32_t period_s)
|
2021-02-18 09:32:32 +01:00
|
|
|
{
|
2022-02-23 23:43:52 -08:00
|
|
|
int ret;
|
2021-02-18 09:32:32 +01:00
|
|
|
struct lwm2m_obj_path path;
|
2022-02-23 23:43:52 -08:00
|
|
|
struct notification_attrs nattrs = { 0 };
|
|
|
|
struct notification_attrs attrs = { 0 };
|
|
|
|
void *ref;
|
2021-02-18 09:32:32 +01:00
|
|
|
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2021-02-18 09:32:32 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
/* Read Reference pointer to attribute */
|
|
|
|
ret = lwm2m_get_path_reference_ptr(NULL, &path, &ref);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/* retrieve existing attributes */
|
|
|
|
ret = update_attrs(ref, &nattrs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2021-02-18 09:32:32 +01:00
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (nattrs.flags & BIT(LWM2M_ATTR_PMIN) && nattrs.pmin == period_s) {
|
|
|
|
/* No need to change */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read Pmin & Pmax values for path */
|
|
|
|
ret = engine_observe_get_attributes(&path, &attrs, client_ctx->srv_obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (period_s && attrs.pmax && attrs.pmax < period_s) {
|
|
|
|
LOG_DBG("New pmin (%d) > pmax (%d)", period_s, attrs.pmax);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMIN, &period_s);
|
2021-02-18 09:32:32 +01:00
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
int lwm2m_engine_update_observer_max_period(struct lwm2m_ctx *client_ctx, const char *pathstr,
|
|
|
|
uint32_t period_s)
|
2021-02-18 09:32:32 +01:00
|
|
|
{
|
2022-02-23 23:43:52 -08:00
|
|
|
int ret;
|
2021-02-18 09:32:32 +01:00
|
|
|
struct lwm2m_obj_path path;
|
2022-02-23 23:43:52 -08:00
|
|
|
void *ref;
|
|
|
|
struct notification_attrs nattrs = { 0 };
|
|
|
|
struct notification_attrs attrs = { 0 };
|
2021-02-18 09:32:32 +01:00
|
|
|
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2021-02-18 09:32:32 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
/* Read Reference pointer to attribute */
|
|
|
|
ret = lwm2m_get_path_reference_ptr(NULL, &path, &ref);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/* retrieve existing attributes */
|
|
|
|
ret = update_attrs(ref, &nattrs);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2021-02-18 09:32:32 +01:00
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (nattrs.flags & BIT(LWM2M_ATTR_PMAX) && nattrs.pmax == period_s) {
|
|
|
|
/* No need to change */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read Pmin & Pmax values for path */
|
|
|
|
ret = engine_observe_get_attributes(&path, &attrs, client_ctx->srv_obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (period_s && attrs.pmin > period_s) {
|
|
|
|
LOG_DBG("pmin (%d) > new pmax (%d)", attrs.pmin, period_s);
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update or allocate new */
|
|
|
|
ret = lwm2m_update_or_allocate_attribute(ref, LWM2M_ATTR_PMAX, &period_s);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update Observer timestamp */
|
|
|
|
return lwm2m_engine_observer_timestamp_update(&client_ctx->observer, &path,
|
|
|
|
client_ctx->srv_obj_inst);
|
2021-02-18 09:32:32 +01:00
|
|
|
}
|
|
|
|
|
2020-02-20 16:49:14 +01:00
|
|
|
void lwm2m_engine_get_binding(char *binding)
|
2021-11-09 12:48:12 +02:00
|
|
|
{
|
|
|
|
/* Defaults to UDP. */
|
|
|
|
strncpy(binding, "U", BINDING_OPT_MAX_LEN);
|
|
|
|
#if CONFIG_LWM2M_VERSION_1_0
|
|
|
|
/* In LwM2M 1.0 binding and queue mode are in same parameter */
|
|
|
|
char queue[QUEUE_OPT_MAX_LEN];
|
|
|
|
|
|
|
|
lwm2m_engine_get_queue_mode(queue);
|
|
|
|
strncat(binding, queue, QUEUE_OPT_MAX_LEN);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwm2m_engine_get_queue_mode(char *queue)
|
2020-02-20 16:49:14 +01:00
|
|
|
{
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
|
2021-11-09 12:48:12 +02:00
|
|
|
strncpy(queue, "Q", QUEUE_OPT_MAX_LEN);
|
2020-02-20 16:49:14 +01:00
|
|
|
} else {
|
2021-11-09 12:48:12 +02:00
|
|
|
strncpy(queue, "", QUEUE_OPT_MAX_LEN);
|
2020-02-20 16:49:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-11 07:00:44 -08:00
|
|
|
static int lwm2m_engine_allocate_resource_instance(struct lwm2m_engine_res *res,
|
|
|
|
struct lwm2m_engine_res_inst **res_inst,
|
|
|
|
uint8_t resource_instance_id)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!res->res_instances || res->res_inst_count == 0) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < res->res_inst_count; i++) {
|
|
|
|
if (res->res_instances[i].res_inst_id ==
|
|
|
|
RES_INSTANCE_NOT_CREATED) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= res->res_inst_count) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
res->res_instances[i].res_inst_id = resource_instance_id;
|
|
|
|
*res_inst = &res->res_instances[i];
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_engine_get_create_res_inst(struct lwm2m_obj_path *path, struct lwm2m_engine_res **res,
|
|
|
|
struct lwm2m_engine_res_inst **res_inst)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct lwm2m_engine_res *r = NULL;
|
|
|
|
struct lwm2m_engine_res_inst *r_i = NULL;
|
|
|
|
|
|
|
|
ret = path_to_objs(path, NULL, NULL, &r, &r_i);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!r) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
/* Store resource pointer */
|
|
|
|
*res = r;
|
|
|
|
|
|
|
|
if (!r_i) {
|
|
|
|
if (path->level < LWM2M_PATH_LEVEL_RESOURCE_INST) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lwm2m_engine_allocate_resource_instance(r, &r_i, path->res_inst_id);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Store resource instance pointer */
|
|
|
|
*res_inst = r_i;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_create_res_inst(const char *pathstr)
|
2019-07-29 10:09:00 -07:00
|
|
|
{
|
2022-02-11 07:00:44 -08:00
|
|
|
int ret;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2019-07-29 10:09:00 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 4) {
|
|
|
|
LOG_ERR("path must have 4 parts");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = path_to_objs(&path, NULL, NULL, &res, &res_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!res) {
|
|
|
|
LOG_ERR("resource %u not found", path.res_id);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res_inst && res_inst->res_inst_id != RES_INSTANCE_NOT_CREATED) {
|
|
|
|
LOG_ERR("res instance %u already exists", path.res_inst_id);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2022-02-11 07:00:44 -08:00
|
|
|
return lwm2m_engine_allocate_resource_instance(res, &res_inst, path.res_inst_id);
|
2019-07-29 10:09:00 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_delete_res_inst(const char *pathstr)
|
2019-07-29 10:09:00 -07:00
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2019-07-29 10:09:00 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.level < 4) {
|
|
|
|
LOG_ERR("path must have 4 parts");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!res_inst) {
|
|
|
|
LOG_ERR("res instance %u not found", path.res_inst_id);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
res_inst->data_ptr = NULL;
|
2020-09-24 15:28:10 +02:00
|
|
|
res_inst->max_data_len = 0U;
|
2019-07-29 10:09:00 -07:00
|
|
|
res_inst->data_len = 0U;
|
|
|
|
res_inst->res_inst_id = RES_INSTANCE_NOT_CREATED;
|
|
|
|
|
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-02-08 01:48:32 +01:00
|
|
|
bool lwm2m_engine_path_is_observed(const char *pathstr)
|
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
struct lwm2m_obj_path path;
|
|
|
|
int ret;
|
|
|
|
int i;
|
|
|
|
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(pathstr, &path, '/');
|
2022-02-08 01:48:32 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sock_nfds; ++i) {
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
|
2022-02-28 07:42:53 -08:00
|
|
|
|
|
|
|
if (lwm2m_notify_observer_list(&obs->path_list, &path)) {
|
2022-02-08 01:48:32 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_register_read_callback(const char *pathstr,
|
2017-07-07 11:04:03 -07:00
|
|
|
lwm2m_engine_get_data_cb_t cb)
|
|
|
|
{
|
|
|
|
int ret;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-10-26 23:41:17 -07:00
|
|
|
ret = lwm2m_engine_get_resource(pathstr, &res);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
res->read_cb = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_register_pre_write_callback(const char *pathstr,
|
2017-07-07 11:04:03 -07:00
|
|
|
lwm2m_engine_get_data_cb_t cb)
|
|
|
|
{
|
|
|
|
int ret;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-10-26 23:41:17 -07:00
|
|
|
ret = lwm2m_engine_get_resource(pathstr, &res);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
res->pre_write_cb = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_register_validate_callback(const char *pathstr,
|
2021-02-17 15:47:28 +01:00
|
|
|
lwm2m_engine_set_data_cb_t cb)
|
|
|
|
{
|
2021-03-10 13:14:31 +01:00
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
2021-02-17 15:47:28 +01:00
|
|
|
int ret;
|
|
|
|
struct lwm2m_engine_res *res = NULL;
|
|
|
|
|
|
|
|
ret = lwm2m_engine_get_resource(pathstr, &res);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
res->validate_cb = cb;
|
|
|
|
return 0;
|
2021-03-10 13:14:31 +01:00
|
|
|
#else
|
|
|
|
ARG_UNUSED(pathstr);
|
|
|
|
ARG_UNUSED(cb);
|
|
|
|
|
|
|
|
LOG_ERR("Validation disabled. Set "
|
|
|
|
"CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 to "
|
|
|
|
"enable validation support.");
|
|
|
|
return -ENOTSUP;
|
|
|
|
#endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
|
2021-02-17 15:47:28 +01:00
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_register_post_write_callback(const char *pathstr,
|
2017-07-07 11:04:03 -07:00
|
|
|
lwm2m_engine_set_data_cb_t cb)
|
|
|
|
{
|
|
|
|
int ret;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-10-26 23:41:17 -07:00
|
|
|
ret = lwm2m_engine_get_resource(pathstr, &res);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
res->post_write_cb = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-08 02:17:51 +01:00
|
|
|
int lwm2m_engine_register_exec_callback(const char *pathstr,
|
2020-12-10 10:17:14 +01:00
|
|
|
lwm2m_engine_execute_cb_t cb)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
int ret;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-10-26 23:41:17 -07:00
|
|
|
ret = lwm2m_engine_get_resource(pathstr, &res);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
res->execute_cb = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int lwm2m_engine_register_create_callback(uint16_t obj_id,
|
2018-07-12 10:00:01 -07:00
|
|
|
lwm2m_engine_user_cb_t cb)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj *obj = NULL;
|
|
|
|
|
|
|
|
obj = get_engine_obj(obj_id);
|
|
|
|
if (!obj) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unable to find obj: %u", obj_id);
|
2018-07-12 10:00:01 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj->user_create_cb = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int lwm2m_engine_register_delete_callback(uint16_t obj_id,
|
2018-07-12 10:00:01 -07:00
|
|
|
lwm2m_engine_user_cb_t cb)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj *obj = NULL;
|
|
|
|
|
|
|
|
obj = get_engine_obj(obj_id);
|
|
|
|
if (!obj) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unable to find obj: %u", obj_id);
|
2018-07-12 10:00:01 -07:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj->user_delete_cb = cb;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* generic data handlers */
|
|
|
|
|
|
|
|
static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst,
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res,
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_field *obj_field,
|
2019-01-24 14:40:23 -08:00
|
|
|
struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2019-07-29 10:09:00 -07:00
|
|
|
int i, loop_max = 1, found_values = 0;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t res_inst_id_tmp = 0U;
|
2017-07-07 11:04:03 -07:00
|
|
|
void *data_ptr = NULL;
|
|
|
|
size_t data_len = 0;
|
2021-12-21 14:45:07 +01:00
|
|
|
int ret = 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
if (!obj_inst || !res || !obj_field || !msg) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
loop_max = res->res_inst_count;
|
2021-07-01 10:58:43 +02:00
|
|
|
if (res->multi_res_inst) {
|
2019-07-29 10:09:00 -07:00
|
|
|
/* search for valid resource instances */
|
|
|
|
for (i = 0; i < loop_max; i++) {
|
|
|
|
if (res->res_instances[i].res_inst_id !=
|
|
|
|
RES_INSTANCE_NOT_CREATED) {
|
|
|
|
found_values = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (!found_values) {
|
2018-02-05 15:29:24 -08:00
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_begin_ri(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
res_inst_id_tmp = msg->path.res_inst_id;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < loop_max; i++) {
|
2019-07-29 10:09:00 -07:00
|
|
|
if (res->res_instances[i].res_inst_id ==
|
|
|
|
RES_INSTANCE_NOT_CREATED) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-02-01 13:44:08 +02:00
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
|
|
|
|
msg->path.level == LWM2M_PATH_LEVEL_RESOURCE_INST &&
|
|
|
|
msg->path.res_inst_id != res->res_instances[i].res_inst_id) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (res->res_inst_count > 1) {
|
|
|
|
msg->path.res_inst_id =
|
|
|
|
res->res_instances[i].res_inst_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* setup initial data elements */
|
|
|
|
data_ptr = res->res_instances[i].data_ptr;
|
|
|
|
data_len = res->res_instances[i].data_len;
|
|
|
|
|
|
|
|
/* allow user to override data elements via callback */
|
|
|
|
if (res->read_cb) {
|
|
|
|
data_ptr = res->read_cb(obj_inst->obj_inst_id,
|
|
|
|
res->res_id,
|
|
|
|
res->res_instances[i].res_inst_id,
|
|
|
|
&data_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!data_ptr || data_len == 0) {
|
|
|
|
return -ENOENT;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (obj_field->data_type) {
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_OPAQUE:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_opaque(&msg->out, &msg->path,
|
|
|
|
(uint8_t *)data_ptr,
|
|
|
|
data_len);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_STRING:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_string(&msg->out, &msg->path,
|
|
|
|
(uint8_t *)data_ptr,
|
|
|
|
strlen((uint8_t *)data_ptr));
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U32:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s64(&msg->out, &msg->path,
|
|
|
|
(int64_t)*(uint32_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U16:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s32(&msg->out, &msg->path,
|
|
|
|
(int32_t)*(uint16_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U8:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s16(&msg->out, &msg->path,
|
|
|
|
(int16_t)*(uint8_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S64:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s64(&msg->out, &msg->path,
|
|
|
|
*(int64_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S32:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s32(&msg->out, &msg->path,
|
|
|
|
*(int32_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S16:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s16(&msg->out, &msg->path,
|
|
|
|
*(int16_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S8:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_s8(&msg->out, &msg->path,
|
|
|
|
*(int8_t *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2022-01-13 16:24:47 +02:00
|
|
|
case LWM2M_RES_TYPE_TIME:
|
|
|
|
ret = engine_put_time(&msg->out, &msg->path,
|
|
|
|
(int64_t)*(uint32_t *)data_ptr);
|
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
case LWM2M_RES_TYPE_BOOL:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_bool(&msg->out, &msg->path,
|
|
|
|
*(bool *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2021-07-20 16:48:33 +02:00
|
|
|
case LWM2M_RES_TYPE_FLOAT:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_float(&msg->out, &msg->path,
|
|
|
|
(double *)data_ptr);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2020-05-19 12:37:02 +02:00
|
|
|
case LWM2M_RES_TYPE_OBJLNK:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_objlnk(&msg->out, &msg->path,
|
|
|
|
(struct lwm2m_objlnk *)data_ptr);
|
2020-05-19 12:37:02 +02:00
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unknown obj data_type %d",
|
|
|
|
obj_field->data_type);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
/* Validate that we really read some data */
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Read operation fail");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2021-12-21 14:45:07 +01:00
|
|
|
}
|
|
|
|
|
2021-07-01 10:58:43 +02:00
|
|
|
if (res->multi_res_inst) {
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_end_ri(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->path.res_inst_id = res_inst_id_tmp;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-10-26 23:32:47 -07:00
|
|
|
size_t lwm2m_engine_get_opaque_more(struct lwm2m_input_context *in,
|
2020-09-16 15:09:30 +02:00
|
|
|
uint8_t *buf, size_t buflen,
|
|
|
|
struct lwm2m_opaque_context *opaque,
|
|
|
|
bool *last_block)
|
2017-10-26 23:32:47 -07:00
|
|
|
{
|
2020-09-16 15:09:30 +02:00
|
|
|
uint32_t in_len = opaque->remaining;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
uint16_t remaining = in->in_cpkt->max_len - in->offset;
|
2017-10-26 23:32:47 -07:00
|
|
|
|
|
|
|
if (in_len > buflen) {
|
|
|
|
in_len = buflen;
|
|
|
|
}
|
|
|
|
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
if (in_len > remaining) {
|
|
|
|
in_len = remaining;
|
|
|
|
}
|
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
opaque->remaining -= in_len;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
remaining -= in_len;
|
2020-09-16 15:09:30 +02:00
|
|
|
if (opaque->remaining == 0U || remaining == 0) {
|
2017-10-26 23:32:47 -07:00
|
|
|
*last_block = true;
|
|
|
|
}
|
|
|
|
|
2019-01-25 14:13:07 -08:00
|
|
|
if (buf_read(buf, in_len, CPKT_BUF_READ(in->in_cpkt),
|
|
|
|
&in->offset) < 0) {
|
2017-10-26 23:32:47 -07:00
|
|
|
*last_block = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (size_t)in_len;
|
|
|
|
}
|
|
|
|
|
2017-10-26 23:45:59 -07:00
|
|
|
static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res,
|
|
|
|
struct lwm2m_engine_res_inst *res_inst,
|
2021-02-17 15:47:28 +01:00
|
|
|
struct lwm2m_message *msg,
|
2020-09-16 15:09:30 +02:00
|
|
|
void *data_ptr, size_t data_len)
|
2017-10-26 23:45:59 -07:00
|
|
|
{
|
2021-12-21 14:45:07 +01:00
|
|
|
int len = 1;
|
2020-09-16 15:09:30 +02:00
|
|
|
bool last_pkt_block = false;
|
2017-10-27 15:14:42 -07:00
|
|
|
int ret = 0;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
bool last_block = true;
|
2020-09-16 15:09:30 +02:00
|
|
|
struct lwm2m_opaque_context opaque_ctx = { 0 };
|
2021-02-17 15:47:28 +01:00
|
|
|
void *write_buf;
|
|
|
|
size_t write_buf_len;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
if (msg->in.block_ctx != NULL) {
|
|
|
|
last_block = msg->in.block_ctx->last_block;
|
2020-09-16 15:09:30 +02:00
|
|
|
|
|
|
|
/* Restore the opaque context from the block context, if used.
|
|
|
|
*/
|
2021-02-17 15:47:28 +01:00
|
|
|
opaque_ctx = msg->in.block_ctx->opaque;
|
|
|
|
}
|
|
|
|
|
2021-03-10 13:14:31 +01:00
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
2021-02-17 15:47:28 +01:00
|
|
|
/* In case validation callback is present, write data to the temporary
|
|
|
|
* buffer first, for validation. Otherwise, write to the data buffer
|
|
|
|
* directly.
|
|
|
|
*/
|
|
|
|
if (res->validate_cb) {
|
|
|
|
write_buf = msg->ctx->validate_buf;
|
|
|
|
write_buf_len = sizeof(msg->ctx->validate_buf);
|
2021-03-10 13:14:31 +01:00
|
|
|
} else
|
|
|
|
#endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
|
|
|
|
{
|
2021-02-17 15:47:28 +01:00
|
|
|
write_buf = data_ptr;
|
|
|
|
write_buf_len = data_len;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
}
|
2017-10-26 23:45:59 -07:00
|
|
|
|
|
|
|
while (!last_pkt_block && len > 0) {
|
2021-02-17 15:47:28 +01:00
|
|
|
len = engine_get_opaque(&msg->in, write_buf,
|
|
|
|
MIN(data_len, write_buf_len),
|
|
|
|
&opaque_ctx, &last_pkt_block);
|
2021-12-21 14:45:07 +01:00
|
|
|
if (len <= 0) {
|
|
|
|
return len;
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
}
|
2021-02-17 15:47:28 +01:00
|
|
|
|
2021-03-10 13:14:31 +01:00
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
2021-02-17 15:47:28 +01:00
|
|
|
if (res->validate_cb) {
|
|
|
|
ret = res->validate_cb(
|
|
|
|
obj_inst->obj_inst_id, res->res_id,
|
|
|
|
res_inst->res_inst_id, write_buf, len,
|
|
|
|
last_pkt_block && last_block, opaque_ctx.len);
|
|
|
|
if (ret < 0) {
|
|
|
|
/* -EEXIST will generate Bad Request LWM2M response. */
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(data_ptr, write_buf, len);
|
|
|
|
}
|
2021-03-10 13:14:31 +01:00
|
|
|
#endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
|
2021-02-17 15:47:28 +01:00
|
|
|
|
2017-10-26 23:45:59 -07:00
|
|
|
if (res->post_write_cb) {
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
ret = res->post_write_cb(
|
|
|
|
obj_inst->obj_inst_id, res->res_id,
|
|
|
|
res_inst->res_inst_id, data_ptr, len,
|
2020-09-16 15:09:30 +02:00
|
|
|
last_pkt_block && last_block, opaque_ctx.len);
|
2017-11-01 14:36:50 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-10-26 23:45:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
if (msg->in.block_ctx != NULL) {
|
|
|
|
msg->in.block_ctx->opaque = opaque_ctx;
|
2020-09-16 15:09:30 +02:00
|
|
|
}
|
|
|
|
|
2020-09-24 15:28:10 +02:00
|
|
|
return opaque_ctx.len;
|
2017-10-26 23:45:59 -07:00
|
|
|
}
|
|
|
|
|
2022-02-01 02:45:52 -08:00
|
|
|
bool lwm2m_engine_bootstrap_override(struct lwm2m_ctx *client_ctx, struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
if (!client_ctx->bootstrap_mode) {
|
|
|
|
/* Bootstrap is not active override is not possible then */
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path->obj_id == LWM2M_OBJECT_SECURITY_ID || path->obj_id == LWM2M_OBJECT_SERVER_ID) {
|
|
|
|
/* Bootstrap server have a access to Security and Server object */
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* This function is exposed for the content format writers */
|
|
|
|
int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res,
|
|
|
|
struct lwm2m_engine_res_inst *res_inst,
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_field *obj_field,
|
2019-01-24 14:40:23 -08:00
|
|
|
struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
void *data_ptr = NULL;
|
|
|
|
size_t data_len = 0;
|
|
|
|
size_t len = 0;
|
2017-07-25 16:54:25 +08:00
|
|
|
size_t total_size = 0;
|
2020-05-27 11:26:57 -05:00
|
|
|
int64_t temp64 = 0;
|
|
|
|
int32_t temp32 = 0;
|
2017-07-25 16:54:25 +08:00
|
|
|
int ret = 0;
|
|
|
|
bool last_block = true;
|
2021-02-17 15:47:28 +01:00
|
|
|
void *write_buf;
|
|
|
|
size_t write_buf_len;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (!obj_inst || !res || !res_inst || !obj_field || !msg) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
|
2018-04-30 16:43:25 -07:00
|
|
|
return -EACCES;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* setup initial data elements */
|
2019-07-29 10:09:00 -07:00
|
|
|
data_ptr = res_inst->data_ptr;
|
2020-09-24 15:28:10 +02:00
|
|
|
data_len = res_inst->max_data_len;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* allow user to override data elements via callback */
|
|
|
|
if (res->pre_write_cb) {
|
2019-07-29 10:09:00 -07:00
|
|
|
data_ptr = res->pre_write_cb(obj_inst->obj_inst_id,
|
|
|
|
res->res_id, res_inst->res_inst_id,
|
|
|
|
&data_len);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-03-10 13:14:31 +01:00
|
|
|
if (res->post_write_cb
|
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
|
|
|
|| res->validate_cb
|
|
|
|
#endif
|
|
|
|
) {
|
2020-09-16 15:09:30 +02:00
|
|
|
if (msg->in.block_ctx != NULL) {
|
2017-10-26 23:45:59 -07:00
|
|
|
/* Get block_ctx for total_size (might be zero) */
|
2020-09-16 15:09:30 +02:00
|
|
|
total_size = msg->in.block_ctx->ctx.total_size;
|
|
|
|
LOG_DBG("BLOCK1: total:%zu current:%zu"
|
|
|
|
" last:%u",
|
|
|
|
msg->in.block_ctx->ctx.total_size,
|
|
|
|
msg->in.block_ctx->ctx.current,
|
|
|
|
msg->in.block_ctx->last_block);
|
2017-10-26 23:45:59 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-10 13:14:31 +01:00
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
2021-02-17 15:47:28 +01:00
|
|
|
/* In case validation callback is present, write data to the temporary
|
|
|
|
* buffer first, for validation. Otherwise, write to the data buffer
|
|
|
|
* directly.
|
|
|
|
*/
|
|
|
|
if (res->validate_cb) {
|
|
|
|
write_buf = msg->ctx->validate_buf;
|
|
|
|
write_buf_len = sizeof(msg->ctx->validate_buf);
|
2021-03-10 13:14:31 +01:00
|
|
|
} else
|
|
|
|
#endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
|
|
|
|
{
|
2021-02-17 15:47:28 +01:00
|
|
|
write_buf = data_ptr;
|
|
|
|
write_buf_len = data_len;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
if (data_ptr && data_len > 0) {
|
|
|
|
switch (obj_field->data_type) {
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_OPAQUE:
|
2019-01-24 14:40:23 -08:00
|
|
|
ret = lwm2m_write_handler_opaque(obj_inst, res,
|
2021-02-17 15:47:28 +01:00
|
|
|
res_inst, msg,
|
2020-09-16 15:09:30 +02:00
|
|
|
data_ptr, data_len);
|
2020-09-24 15:28:10 +02:00
|
|
|
len = ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_STRING:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_string(&msg->in, write_buf,
|
|
|
|
write_buf_len);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
len = strlen((char *)write_buf);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_TIME:
|
2022-01-13 16:24:47 +02:00
|
|
|
ret = engine_get_time(&msg->in, &temp64);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*(uint32_t *)write_buf = temp64;
|
|
|
|
len = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U32:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s64(&msg->in, &temp64);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-08-20 15:28:23 +02:00
|
|
|
*(uint32_t *)write_buf = temp64;
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U16:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s32(&msg->in, &temp32);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
*(uint16_t *)write_buf = temp32;
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_U8:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s32(&msg->in, &temp32);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
*(uint8_t *)write_buf = temp32;
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S64:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s64(&msg->in, (int64_t *)write_buf);
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 8;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S32:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s32(&msg->in, (int32_t *)write_buf);
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 4;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S16:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s32(&msg->in, &temp32);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
*(int16_t *)write_buf = temp32;
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 2;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_S8:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_s32(&msg->in, &temp32);
|
|
|
|
if (ret < 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
*(int8_t *)write_buf = temp32;
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LWM2M_RES_TYPE_BOOL:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_bool(&msg->in, (bool *)write_buf);
|
2017-07-07 11:04:03 -07:00
|
|
|
len = 1;
|
|
|
|
break;
|
|
|
|
|
2021-07-20 16:48:33 +02:00
|
|
|
case LWM2M_RES_TYPE_FLOAT:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_float(&msg->in, (double *)write_buf);
|
2021-07-23 16:38:23 +02:00
|
|
|
len = sizeof(double);
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2020-05-19 12:37:02 +02:00
|
|
|
case LWM2M_RES_TYPE_OBJLNK:
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_get_objlnk(&msg->in,
|
|
|
|
(struct lwm2m_objlnk *)write_buf);
|
2020-05-19 12:37:02 +02:00
|
|
|
len = sizeof(struct lwm2m_objlnk);
|
|
|
|
break;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("unknown obj data_type %d",
|
|
|
|
obj_field->data_type);
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
}
|
2021-12-21 14:45:07 +01:00
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2018-04-30 15:20:19 -07:00
|
|
|
} else {
|
|
|
|
return -ENOENT;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) {
|
2021-03-10 13:14:31 +01:00
|
|
|
#if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
|
2021-02-17 15:47:28 +01:00
|
|
|
if (res->validate_cb) {
|
|
|
|
ret = res->validate_cb(
|
|
|
|
obj_inst->obj_inst_id, res->res_id,
|
|
|
|
res_inst->res_inst_id, write_buf, len,
|
|
|
|
last_block, total_size);
|
|
|
|
if (ret < 0) {
|
|
|
|
/* -EEXIST will generate Bad Request LWM2M response. */
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
2020-09-24 15:28:10 +02:00
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
if (len > data_len) {
|
|
|
|
LOG_ERR("Received data won't fit into provided "
|
|
|
|
"bufffer");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
|
|
|
|
strncpy(data_ptr, write_buf, data_len);
|
|
|
|
} else {
|
|
|
|
memcpy(data_ptr, write_buf, len);
|
|
|
|
}
|
|
|
|
}
|
2021-03-10 13:14:31 +01:00
|
|
|
#endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
|
2021-02-17 15:47:28 +01:00
|
|
|
|
|
|
|
if (res->post_write_cb) {
|
|
|
|
ret = res->post_write_cb(
|
|
|
|
obj_inst->obj_inst_id, res->res_id,
|
|
|
|
res_inst->res_inst_id, data_ptr, len,
|
|
|
|
last_block, total_size);
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-02-17 15:47:28 +01:00
|
|
|
res_inst->data_len = len;
|
|
|
|
|
2021-07-15 15:25:21 +02:00
|
|
|
if (LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
|
|
|
|
NOTIFY_OBSERVER_PATH(&msg->path);
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-07-25 16:54:25 +08:00
|
|
|
return ret;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj,
|
2019-01-24 14:40:23 -08:00
|
|
|
struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-12-12 16:24:03 +08:00
|
|
|
bool update_observe_node = false;
|
2017-11-24 18:47:00 +08:00
|
|
|
char opt_buf[COAP_OPTION_BUF_LEN];
|
2018-05-07 15:05:04 -07:00
|
|
|
int nr_opt, i, ret = 0;
|
2017-11-24 18:47:00 +08:00
|
|
|
struct coap_option options[NR_LWM2M_ATTR];
|
2018-05-07 15:05:04 -07:00
|
|
|
struct lwm2m_attr *attr;
|
2017-11-24 18:47:00 +08:00
|
|
|
struct notification_attrs nattrs = { 0 };
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t type = 0U;
|
2017-11-24 18:47:00 +08:00
|
|
|
void *nattr_ptrs[NR_LWM2M_ATTR] = {
|
|
|
|
&nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt, &nattrs.st
|
|
|
|
};
|
2018-05-07 15:05:04 -07:00
|
|
|
void *ref;
|
2017-11-24 18:47:00 +08:00
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
if (!obj || !msg) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:47:00 +08:00
|
|
|
/* do not expose security obj */
|
|
|
|
if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
nr_opt = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_QUERY,
|
2017-11-24 18:47:00 +08:00
|
|
|
options, NR_LWM2M_ATTR);
|
|
|
|
if (nr_opt <= 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("No attribute found!");
|
2017-11-24 18:47:00 +08:00
|
|
|
/* translate as bad request */
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get lwm2m_attr slist */
|
2022-02-23 23:43:52 -08:00
|
|
|
ret = lwm2m_get_path_reference_ptr(obj, &msg->path, &ref);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2017-11-24 18:47:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* retrieve existing attributes */
|
2018-05-07 15:05:04 -07:00
|
|
|
ret = update_attrs(ref, &nattrs);
|
2018-05-07 14:18:37 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-11-24 18:47:00 +08:00
|
|
|
|
|
|
|
/* loop through options to parse attribute */
|
2018-05-07 15:05:04 -07:00
|
|
|
for (i = 0; i < nr_opt; i++) {
|
2019-02-11 17:14:19 +00:00
|
|
|
int limit = MIN(options[i].len, 5), plen = 0, vlen;
|
2021-07-23 16:38:23 +02:00
|
|
|
struct lwm2m_attr val = { 0 };
|
2018-11-29 11:23:03 -08:00
|
|
|
type = 0U;
|
2017-11-24 18:47:00 +08:00
|
|
|
|
|
|
|
/* search for '=' */
|
|
|
|
while (plen < limit && options[i].value[plen] != '=') {
|
|
|
|
plen += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* either length = 2(gt/lt/st) or = 4(pmin/pmax) */
|
|
|
|
if (plen != 2 && plen != 4) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* matching attribute name */
|
2018-11-29 11:23:03 -08:00
|
|
|
for (type = 0U; type < NR_LWM2M_ATTR; type++) {
|
2017-11-24 18:47:00 +08:00
|
|
|
if (LWM2M_ATTR_LEN[type] == plen &&
|
|
|
|
!memcmp(options[i].value, LWM2M_ATTR_STR[type],
|
|
|
|
LWM2M_ATTR_LEN[type])) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unrecognized attribute */
|
|
|
|
if (type == NR_LWM2M_ATTR) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* unset attribute when no value's given */
|
|
|
|
if (options[i].len == plen) {
|
|
|
|
nattrs.flags &= ~BIT(type);
|
|
|
|
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(nattr_ptrs[type], 0,
|
2020-05-27 11:26:57 -05:00
|
|
|
type <= LWM2M_ATTR_PMAX ? sizeof(int32_t) :
|
2021-07-23 16:38:23 +02:00
|
|
|
sizeof(double));
|
2017-11-24 18:47:00 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* gt/lt/st cannot be assigned to obj/obj_inst unless unset */
|
2019-03-26 19:57:45 -06:00
|
|
|
if (plen == 2 && msg->path.level <= 2U) {
|
2017-11-24 18:47:00 +08:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
vlen = options[i].len - plen - 1;
|
|
|
|
memcpy(opt_buf, options[i].value + plen + 1, vlen);
|
|
|
|
opt_buf[vlen] = '\0';
|
|
|
|
|
|
|
|
/* convert value to integer or float */
|
|
|
|
if (plen == 4) {
|
|
|
|
char *end;
|
|
|
|
long int v;
|
|
|
|
|
|
|
|
/* pmin/pmax: integer (sec 5.1.2)
|
|
|
|
* however, negative is non-sense
|
|
|
|
*/
|
|
|
|
errno = 0;
|
|
|
|
v = strtol(opt_buf, &end, 10);
|
2019-02-13 13:37:25 -08:00
|
|
|
if (errno || *end || v < 0) {
|
2017-11-24 18:47:00 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
|
2021-07-23 16:38:23 +02:00
|
|
|
val.int_val = v;
|
2017-11-24 18:47:00 +08:00
|
|
|
} else {
|
|
|
|
/* gt/lt/st: type float */
|
2021-07-23 16:38:23 +02:00
|
|
|
ret = lwm2m_atof(opt_buf, &val.float_val);
|
2017-11-24 18:47:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("invalid attr[%s] value",
|
2019-05-29 16:49:18 -07:00
|
|
|
log_strdup(LWM2M_ATTR_STR[type]));
|
2017-11-24 18:47:00 +08:00
|
|
|
/* bad request */
|
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type <= LWM2M_ATTR_PMAX) {
|
2021-07-23 16:38:23 +02:00
|
|
|
*(int32_t *)nattr_ptrs[type] = val.int_val;
|
2017-11-24 18:47:00 +08:00
|
|
|
} else {
|
2021-07-23 16:38:23 +02:00
|
|
|
memcpy(nattr_ptrs[type], &val.float_val,
|
|
|
|
sizeof(val.float_val));
|
2017-11-24 18:47:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nattrs.flags |= BIT(type);
|
|
|
|
}
|
|
|
|
|
2022-01-20 12:31:04 +01:00
|
|
|
if (((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) ==
|
|
|
|
(BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) &&
|
2017-11-24 18:47:00 +08:00
|
|
|
nattrs.pmin > nattrs.pmax) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax);
|
2017-11-24 18:47:00 +08:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
2022-01-20 12:31:04 +01:00
|
|
|
if ((nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) ==
|
|
|
|
(BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) {
|
2021-07-23 16:38:23 +02:00
|
|
|
if (nattrs.lt > nattrs.gt) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("lt > gt");
|
2017-11-24 18:47:00 +08:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) {
|
2021-07-23 16:38:23 +02:00
|
|
|
if (nattrs.lt + 2 * nattrs.st > nattrs.gt) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("lt + 2*st > gt");
|
2017-11-24 18:47:00 +08:00
|
|
|
return -EEXIST;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-07 15:05:04 -07:00
|
|
|
/* find matching attributes */
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
|
|
|
|
if (ref != write_attr_pool[i].ref) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = write_attr_pool + i;
|
2017-11-24 18:47:00 +08:00
|
|
|
type = attr->type;
|
|
|
|
|
|
|
|
if (!(BIT(type) & nattrs.flags)) {
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_DBG("Unset attr %s",
|
|
|
|
log_strdup(LWM2M_ATTR_STR[type]));
|
2018-09-11 19:09:03 -07:00
|
|
|
(void)memset(attr, 0, sizeof(*attr));
|
2017-12-12 16:24:03 +08:00
|
|
|
|
|
|
|
if (type <= LWM2M_ATTR_PMAX) {
|
|
|
|
update_observe_node = true;
|
|
|
|
}
|
|
|
|
|
2017-11-24 18:47:00 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
nattrs.flags &= ~BIT(type);
|
|
|
|
|
|
|
|
if (type <= LWM2M_ATTR_PMAX) {
|
2020-05-27 11:26:57 -05:00
|
|
|
if (attr->int_val == *(int32_t *)nattr_ptrs[type]) {
|
2017-12-12 16:24:03 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
attr->int_val = *(int32_t *)nattr_ptrs[type];
|
2017-12-12 16:24:03 +08:00
|
|
|
update_observe_node = true;
|
2021-07-23 16:38:23 +02:00
|
|
|
|
|
|
|
LOG_DBG("Update %s to %d", log_strdup(LWM2M_ATTR_STR[type]),
|
|
|
|
attr->int_val);
|
2017-11-24 18:47:00 +08:00
|
|
|
} else {
|
2021-07-23 16:38:23 +02:00
|
|
|
if (attr->float_val == *(double *)nattr_ptrs[type]) {
|
2017-12-12 16:24:03 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-07-23 16:38:23 +02:00
|
|
|
attr->float_val = *(double *)nattr_ptrs[type];
|
2017-11-24 18:47:00 +08:00
|
|
|
|
2021-07-23 16:38:23 +02:00
|
|
|
LOG_DBG("Update %s to %f", log_strdup(LWM2M_ATTR_STR[type]),
|
|
|
|
attr->float_val);
|
|
|
|
}
|
2017-11-24 18:47:00 +08:00
|
|
|
}
|
|
|
|
|
2022-02-02 16:29:17 +01:00
|
|
|
/* add attribute to obj/obj_inst/res/res_inst */
|
2018-11-29 11:23:03 -08:00
|
|
|
for (type = 0U; nattrs.flags && type < NR_LWM2M_ATTR; type++) {
|
2017-11-24 18:47:00 +08:00
|
|
|
if (!(BIT(type) & nattrs.flags)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* grab an entry for newly added attribute */
|
|
|
|
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
|
2018-05-07 15:05:04 -07:00
|
|
|
if (!write_attr_pool[i].ref) {
|
2017-11-24 18:47:00 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == CONFIG_LWM2M_NUM_ATTR) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = write_attr_pool + i;
|
|
|
|
attr->type = type;
|
2018-05-07 15:05:04 -07:00
|
|
|
attr->ref = ref;
|
|
|
|
|
2017-11-24 18:47:00 +08:00
|
|
|
if (type <= LWM2M_ATTR_PMAX) {
|
2020-05-27 11:26:57 -05:00
|
|
|
attr->int_val = *(int32_t *)nattr_ptrs[type];
|
2017-12-12 16:24:03 +08:00
|
|
|
update_observe_node = true;
|
2021-07-23 16:38:23 +02:00
|
|
|
|
|
|
|
LOG_DBG("Add %s to %d", log_strdup(LWM2M_ATTR_STR[type]),
|
|
|
|
attr->int_val);
|
2017-11-24 18:47:00 +08:00
|
|
|
} else {
|
2021-07-23 16:38:23 +02:00
|
|
|
attr->float_val = *(double *)nattr_ptrs[type];
|
|
|
|
|
|
|
|
LOG_DBG("Add %s to %f", log_strdup(LWM2M_ATTR_STR[type]),
|
|
|
|
attr->float_val);
|
2017-11-24 18:47:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
nattrs.flags &= ~BIT(type);
|
|
|
|
}
|
|
|
|
|
2017-12-12 16:24:03 +08:00
|
|
|
/* check only pmin/pmax */
|
|
|
|
if (!update_observe_node) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
lwm2m_engine_observer_timestamp_update(&msg->ctx->observer, &msg->path,
|
|
|
|
msg->ctx->srv_obj_inst);
|
2017-12-12 16:24:03 +08:00
|
|
|
|
2017-11-24 18:47:00 +08:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-07-29 10:08:00 -07:00
|
|
|
static int lwm2m_exec_handler(struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
int ret;
|
2020-12-10 10:17:14 +01:00
|
|
|
uint8_t *args;
|
|
|
|
uint16_t args_len;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-07-29 10:08:00 -07:00
|
|
|
if (!msg) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:09:00 -07:00
|
|
|
ret = path_to_objs(&msg->path, &obj_inst, NULL, &res, NULL);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-12-10 10:17:14 +01:00
|
|
|
args = (uint8_t *)coap_packet_get_payload(msg->in.in_cpkt, &args_len);
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
if (res->execute_cb) {
|
2020-12-10 10:17:14 +01:00
|
|
|
return res->execute_cb(obj_inst->obj_inst_id, args, args_len);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: something else to handle for execute? */
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:08:00 -07:00
|
|
|
static int lwm2m_delete_handler(struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2019-01-25 22:36:47 -08:00
|
|
|
int ret;
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
if (!msg) {
|
2017-07-07 11:04:03 -07:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2020-11-24 10:06:47 +01:00
|
|
|
/* Device management interface is not allowed to delete Security and
|
|
|
|
* Device objects instances.
|
|
|
|
*/
|
|
|
|
if (msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID ||
|
|
|
|
msg->path.obj_id == LWM2M_OBJECT_DEVICE_ID) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
2019-01-25 22:36:47 -08:00
|
|
|
ret = lwm2m_delete_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
|
2021-04-15 12:43:40 +02:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-01-25 22:36:47 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
|
2021-04-15 12:43:40 +02:00
|
|
|
if (!msg->ctx->bootstrap_mode) {
|
2020-10-28 16:15:27 +01:00
|
|
|
engine_trigger_update(true);
|
2019-01-25 22:36:47 -08:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-04-15 12:43:40 +02:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
static int do_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
2018-08-29 13:35:50 -07:00
|
|
|
{
|
|
|
|
switch (content_format) {
|
|
|
|
|
|
|
|
case LWM2M_FORMAT_APP_OCTET_STREAM:
|
|
|
|
case LWM2M_FORMAT_PLAIN_TEXT:
|
|
|
|
case LWM2M_FORMAT_OMA_PLAIN_TEXT:
|
2019-07-29 10:08:00 -07:00
|
|
|
return do_read_op_plain_text(msg, content_format);
|
2018-08-29 13:35:50 -07:00
|
|
|
|
|
|
|
case LWM2M_FORMAT_OMA_TLV:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_TLV:
|
2019-07-29 10:08:00 -07:00
|
|
|
return do_read_op_tlv(msg, content_format);
|
2018-08-29 13:35:50 -07:00
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_RW_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_OMA_JSON:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_JSON:
|
2019-07-29 10:08:00 -07:00
|
|
|
return do_read_op_json(msg, content_format);
|
2018-08-29 13:35:50 -07:00
|
|
|
#endif
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
return do_read_op_senml_json(msg);
|
|
|
|
#endif
|
|
|
|
|
2018-08-29 13:35:50 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unsupported content-format: %u", content_format);
|
2018-08-29 13:35:50 -07:00
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-17 07:34:34 -08:00
|
|
|
static int do_composite_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
|
|
|
{
|
|
|
|
switch (content_format) {
|
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
return do_composite_read_op_senml_json(msg);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported content-format: %u", content_format);
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static int do_composite_observe_read_path_op(struct lwm2m_message *msg, uint16_t content_format,
|
|
|
|
sys_slist_t *lwm2m_path_list,
|
|
|
|
sys_slist_t *lwm2m_path_free_list)
|
|
|
|
{
|
|
|
|
switch (content_format) {
|
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
return do_composite_observe_parse_path_senml_json(msg, lwm2m_path_list,
|
|
|
|
lwm2m_path_free_list);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported content-format: %u", content_format);
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
static int lwm2m_perform_read_object_instance(struct lwm2m_message *msg,
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst,
|
|
|
|
uint8_t *num_read)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2019-07-29 10:09:00 -07:00
|
|
|
struct lwm2m_engine_res *res = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_field *obj_field;
|
2022-01-13 00:02:46 -08:00
|
|
|
int ret = 0;
|
2017-10-19 16:27:32 -07:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
while (obj_inst) {
|
2019-03-26 19:57:45 -06:00
|
|
|
if (!obj_inst->resources || obj_inst->resource_count == 0U) {
|
2018-08-27 17:00:17 -07:00
|
|
|
goto move_forward;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2018-08-29 14:44:23 -07:00
|
|
|
/* update the obj_inst_id as we move through the instances */
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->path.obj_inst_id = obj_inst->obj_inst_id;
|
2018-08-29 14:44:23 -07:00
|
|
|
|
2022-03-02 15:58:36 +02:00
|
|
|
ret = engine_put_begin_oi(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2018-08-29 14:44:23 -07:00
|
|
|
}
|
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
for (int index = 0; index < obj_inst->resource_count; index++) {
|
|
|
|
if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
|
|
|
|
msg->path.res_id != obj_inst->resources[index].res_id) {
|
2018-08-29 14:22:09 -07:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
res = &obj_inst->resources[index];
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->path.res_id = res->res_id;
|
2022-01-13 00:02:46 -08:00
|
|
|
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj, res->res_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (!obj_field) {
|
|
|
|
ret = -ENOENT;
|
2018-04-30 15:30:12 -07:00
|
|
|
} else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
|
2017-07-07 11:04:03 -07:00
|
|
|
ret = -EPERM;
|
|
|
|
} else {
|
2018-08-29 14:44:23 -07:00
|
|
|
/* start resource formatting */
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_begin_r(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2018-08-29 14:44:23 -07:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* perform read operation on this resource */
|
|
|
|
ret = lwm2m_read_handler(obj_inst, res,
|
2019-01-24 14:40:23 -08:00
|
|
|
obj_field, msg);
|
2021-12-21 14:45:07 +01:00
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
/* No point continuing if there's no
|
|
|
|
* memory left in a message.
|
|
|
|
*/
|
|
|
|
return ret;
|
|
|
|
} else if (ret < 0) {
|
2018-08-27 16:56:44 -07:00
|
|
|
/* ignore errors unless single read */
|
2022-01-13 00:02:46 -08:00
|
|
|
if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
|
|
|
|
!LWM2M_HAS_PERM(obj_field, BIT(LWM2M_FLAG_OPTIONAL))) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("READ OP: %d", ret);
|
2018-04-30 16:43:25 -07:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
} else {
|
2022-01-13 00:02:46 -08:00
|
|
|
*num_read += 1U;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2018-08-29 14:44:23 -07:00
|
|
|
|
|
|
|
/* end resource formatting */
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_end_r(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* on single read break if errors */
|
2022-01-13 00:02:46 -08:00
|
|
|
if (ret < 0 && msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST) {
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* when reading multiple resources ignore return code */
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
|
2018-08-27 17:00:17 -07:00
|
|
|
move_forward:
|
2022-03-02 15:58:36 +02:00
|
|
|
ret = engine_put_end_oi(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2018-08-29 14:44:23 -07:00
|
|
|
}
|
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT) {
|
2018-08-27 17:00:17 -07:00
|
|
|
/* advance to the next object instance */
|
2022-01-13 00:02:46 -08:00
|
|
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, obj_inst->obj_inst_id);
|
2018-08-27 17:00:17 -07:00
|
|
|
} else {
|
|
|
|
obj_inst = NULL;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
|
|
|
struct lwm2m_obj_path temp_path;
|
|
|
|
int ret = 0;
|
|
|
|
uint8_t num_read = 0U;
|
|
|
|
|
|
|
|
if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
|
2022-03-21 03:52:11 -07:00
|
|
|
obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
|
|
|
|
if (!obj_inst) {
|
|
|
|
/* When Object instace is indicated error have to be reported */
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2022-01-13 00:02:46 -08:00
|
|
|
} else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
|
2022-03-21 03:52:11 -07:00
|
|
|
/* find first obj_inst with path's obj_id.
|
|
|
|
* Path level 1 can accept NULL. It define empty payload to response.
|
|
|
|
*/
|
2022-01-13 00:02:46 -08:00
|
|
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set output content-format */
|
|
|
|
ret = coap_append_option_int(msg->out.out_cpkt,
|
|
|
|
COAP_OPTION_CONTENT_FORMAT,
|
|
|
|
content_format);
|
2021-12-21 14:45:07 +01:00
|
|
|
if (ret < 0) {
|
2022-01-13 00:02:46 -08:00
|
|
|
LOG_ERR("Error setting response content-format: %d", ret);
|
2021-12-21 14:45:07 +01:00
|
|
|
return ret;
|
|
|
|
}
|
2018-08-29 14:15:43 -07:00
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Error appending payload marker: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* store original path values so we can change them during processing */
|
|
|
|
memcpy(&temp_path, &msg->path, sizeof(temp_path));
|
|
|
|
|
|
|
|
if (engine_put_begin(&msg->out, &msg->path) < 0) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (engine_put_end(&msg->out, &msg->path) < 0) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2018-08-29 14:32:51 -07:00
|
|
|
/* restore original path values */
|
2019-01-24 14:40:23 -08:00
|
|
|
memcpy(&msg->path, &temp_path, sizeof(temp_path));
|
2018-08-29 14:32:51 -07:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* did not read anything even if we should have - on single item */
|
2022-02-01 13:44:08 +02:00
|
|
|
if (ret == 0 && num_read == 0U) {
|
|
|
|
if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
|
|
|
|
msg->path.level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-02 15:39:59 +01:00
|
|
|
static int lwm2m_discover_add_res(struct lwm2m_message *msg,
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst,
|
|
|
|
struct lwm2m_engine_res *res)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct lwm2m_obj_path path = {
|
|
|
|
.obj_id = obj_inst->obj->obj_id,
|
|
|
|
.obj_inst_id = obj_inst->obj_inst_id,
|
|
|
|
.res_id = res->res_id,
|
|
|
|
.level = LWM2M_PATH_LEVEL_RESOURCE,
|
|
|
|
};
|
|
|
|
|
|
|
|
ret = engine_put_corelink(&msg->out, &path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Report resource instances, if applicable. */
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
|
|
|
|
msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
|
|
|
|
res->multi_res_inst) {
|
|
|
|
for (int i = 0; i < res->res_inst_count; i++) {
|
|
|
|
struct lwm2m_engine_res_inst *res_inst =
|
|
|
|
&res->res_instances[i];
|
|
|
|
|
|
|
|
if (res_inst->res_inst_id ==
|
|
|
|
RES_INSTANCE_NOT_CREATED) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
path = (struct lwm2m_obj_path){
|
|
|
|
.obj_id = obj_inst->obj->obj_id,
|
|
|
|
.obj_inst_id = obj_inst->obj_inst_id,
|
|
|
|
.res_id = res->res_id,
|
|
|
|
.res_inst_id = res_inst->res_inst_id,
|
|
|
|
.level = LWM2M_PATH_LEVEL_RESOURCE_INST,
|
|
|
|
};
|
|
|
|
|
|
|
|
ret = engine_put_corelink(&msg->out, &path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap)
|
2017-11-28 11:51:36 +08:00
|
|
|
{
|
2017-11-15 09:46:13 +08:00
|
|
|
struct lwm2m_engine_obj *obj;
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
2017-11-15 09:46:13 +08:00
|
|
|
int ret;
|
|
|
|
bool reported = false;
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
/* 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) {
|
2017-11-15 09:46:13 +08:00
|
|
|
return -EPERM;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* set output content-format */
|
2019-01-24 14:40:23 -08:00
|
|
|
ret = coap_append_option_int(msg->out.out_cpkt,
|
2017-11-15 09:46:13 +08:00
|
|
|
COAP_OPTION_CONTENT_FORMAT,
|
|
|
|
LWM2M_FORMAT_APP_LINK_FORMAT);
|
2017-09-19 16:04:32 -07:00
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Error setting response content-format: %d", ret);
|
2017-09-19 16:04:32 -07:00
|
|
|
return ret;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
|
2017-10-19 16:27:32 -07:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
/*
|
|
|
|
* Add required prefix for bootstrap discovery (5.2.7.3).
|
|
|
|
* For device management discovery, `engine_put_begin()` adds nothing.
|
2017-11-15 09:46:13 +08:00
|
|
|
*/
|
2021-12-21 14:45:07 +01:00
|
|
|
ret = engine_put_begin(&msg->out, &msg->path);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2019-01-25 14:13:07 -08:00
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
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;
|
|
|
|
}
|
2017-11-15 09:46:13 +08:00
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
/* For bootstrap discover, only report object ID when no
|
2021-02-02 13:14:21 +01:00
|
|
|
* instance is available or it's needed to report object
|
|
|
|
* version.
|
2021-01-25 13:49:33 +01:00
|
|
|
* For device management discovery, only report object ID with
|
|
|
|
* attributes if object ID (alone) was provided.
|
|
|
|
*/
|
2021-02-02 13:14:21 +01:00
|
|
|
if ((is_bootstrap && (obj->instance_count == 0U ||
|
|
|
|
lwm2m_engine_shall_report_obj_version(obj))) ||
|
2021-01-25 13:49:33 +01:00
|
|
|
(!is_bootstrap && msg->path.level == LWM2M_PATH_LEVEL_OBJECT)) {
|
|
|
|
struct lwm2m_obj_path path = {
|
|
|
|
.obj_id = obj->obj_id,
|
|
|
|
.level = LWM2M_PATH_LEVEL_OBJECT,
|
|
|
|
};
|
|
|
|
|
|
|
|
ret = engine_put_corelink(&msg->out, &path);
|
2017-11-28 11:51:36 +08:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-11-15 09:46:13 +08:00
|
|
|
reported = true;
|
2020-11-26 15:58:21 +01:00
|
|
|
|
2021-02-02 13:14:21 +01:00
|
|
|
if (obj->instance_count == 0U) {
|
2021-01-25 13:49:33 +01:00
|
|
|
continue;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list,
|
|
|
|
obj_inst, node) {
|
|
|
|
if (obj_inst->obj->obj_id != obj->obj_id) {
|
|
|
|
continue;
|
|
|
|
}
|
2019-01-25 14:13:07 -08:00
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
/* Skip unrelated object instance. */
|
|
|
|
if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT &&
|
|
|
|
msg->path.obj_inst_id != obj_inst->obj_inst_id) {
|
|
|
|
continue;
|
2017-11-15 09:46:13 +08:00
|
|
|
}
|
2017-10-19 16:27:32 -07:00
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
/* Report object instances only if no Resource ID is
|
|
|
|
* provided.
|
2020-11-26 15:58:21 +01:00
|
|
|
*/
|
2021-01-25 13:49:33 +01:00
|
|
|
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);
|
2020-11-26 15:58:21 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2017-11-28 11:51:36 +08:00
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
reported = true;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
/* Do not report resources in bootstrap discovery. */
|
|
|
|
if (is_bootstrap) {
|
2017-11-15 09:46:13 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
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;
|
|
|
|
}
|
2019-01-25 14:13:07 -08:00
|
|
|
|
2022-02-02 15:39:59 +01:00
|
|
|
ret = lwm2m_discover_add_res(msg, obj_inst,
|
|
|
|
&obj_inst->resources[i]);
|
2020-11-26 16:51:49 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
reported = true;
|
2017-11-28 11:51:36 +08:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-15 09:46:13 +08:00
|
|
|
return reported ? 0 : -ENOENT;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-01-25 13:49:33 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
int lwm2m_get_or_create_engine_obj(struct lwm2m_message *msg,
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_inst **obj_inst,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *created)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if (created) {
|
2018-11-29 11:23:03 -08:00
|
|
|
*created = 0U;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
*obj_inst = get_engine_obj_inst(msg->path.obj_id,
|
|
|
|
msg->path.obj_inst_id);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (!*obj_inst) {
|
2019-01-24 14:40:23 -08:00
|
|
|
ret = lwm2m_create_obj_inst(msg->path.obj_id,
|
|
|
|
msg->path.obj_inst_id,
|
2017-07-07 11:04:03 -07:00
|
|
|
obj_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set created flag to one */
|
|
|
|
if (created) {
|
2018-11-29 11:23:03 -08:00
|
|
|
*created = 1U;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2019-01-25 22:36:47 -08:00
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
|
|
|
|
if (!msg->ctx->bootstrap_mode) {
|
2020-10-28 16:15:27 +01:00
|
|
|
engine_trigger_update(true);
|
2019-01-25 22:36:47 -08:00
|
|
|
}
|
|
|
|
#endif
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2022-02-11 07:00:44 -08:00
|
|
|
int lwm2m_engine_validate_write_access(struct lwm2m_message *msg,
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst,
|
|
|
|
struct lwm2m_engine_obj_field **obj_field)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_field *o_f;
|
|
|
|
|
|
|
|
o_f = lwm2m_get_engine_obj_field(obj_inst->obj, msg->path.res_id);
|
|
|
|
if (!o_f) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
*obj_field = o_f;
|
|
|
|
|
|
|
|
if (!LWM2M_HAS_PERM(o_f, LWM2M_PERM_W) &&
|
|
|
|
!lwm2m_engine_bootstrap_override(msg->ctx, &msg->path)) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obj_inst->resources || obj_inst->resource_count == 0U) {
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-01-27 14:31:26 +01:00
|
|
|
struct lwm2m_engine_obj *lwm2m_engine_get_obj(
|
|
|
|
const struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
if (path->level < LWM2M_PATH_LEVEL_OBJECT) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return get_engine_obj(path->obj_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lwm2m_engine_obj_inst *lwm2m_engine_get_obj_inst(
|
|
|
|
const struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
if (path->level < LWM2M_PATH_LEVEL_OBJECT_INST) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return get_engine_obj_inst(path->obj_id, path->obj_inst_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct lwm2m_engine_res *lwm2m_engine_get_res(
|
|
|
|
const struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_res *res = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = path_to_objs(path, NULL, NULL, &res, NULL);
|
|
|
|
if (ret < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2022-02-02 15:39:59 +01:00
|
|
|
struct lwm2m_engine_res_inst *lwm2m_engine_get_res_inst(
|
|
|
|
const struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_res_inst *res_inst = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (path->level != LWM2M_PATH_LEVEL_RESOURCE_INST) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = path_to_objs(path, NULL, NULL, NULL, &res_inst);
|
|
|
|
if (ret < 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return res_inst;
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:14:21 +01:00
|
|
|
bool lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj *obj)
|
|
|
|
{
|
|
|
|
if (obj->is_core) {
|
|
|
|
return obj->version_major != LWM2M_PROTOCOL_VERSION_MAJOR ||
|
|
|
|
obj->version_minor != LWM2M_PROTOCOL_VERSION_MINOR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return obj->version_major != 1 || obj->version_minor != 0;
|
|
|
|
}
|
|
|
|
|
2019-07-29 10:08:00 -07:00
|
|
|
static int do_write_op(struct lwm2m_message *msg,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t format)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
|
2017-10-30 15:18:35 +08:00
|
|
|
case LWM2M_FORMAT_APP_OCTET_STREAM:
|
2017-07-07 11:04:03 -07:00
|
|
|
case LWM2M_FORMAT_PLAIN_TEXT:
|
|
|
|
case LWM2M_FORMAT_OMA_PLAIN_TEXT:
|
2019-07-29 10:08:00 -07:00
|
|
|
return do_write_op_plain_text(msg);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
case LWM2M_FORMAT_OMA_TLV:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_TLV:
|
2019-07-29 10:08:00 -07:00
|
|
|
return do_write_op_tlv(msg);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
#ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
|
|
|
|
case LWM2M_FORMAT_OMA_JSON:
|
|
|
|
case LWM2M_FORMAT_OMA_OLD_JSON:
|
2019-07-29 10:08:00 -07:00
|
|
|
return do_write_op_json(msg);
|
2017-07-07 11:04:03 -07:00
|
|
|
#endif
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
return do_write_op_senml_json(msg);
|
|
|
|
#endif
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
default:
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unsupported format: %u", format);
|
2018-01-30 15:18:57 +08:00
|
|
|
return -ENOMSG;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-21 03:23:56 -08:00
|
|
|
static int do_composite_write_op(struct lwm2m_message *msg,
|
|
|
|
uint16_t format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
|
|
|
return do_write_op_senml_json(msg);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported format: %u", format);
|
|
|
|
return -ENOMSG;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-25 22:36:47 -08:00
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
2020-11-24 10:06:47 +01:00
|
|
|
static bool bootstrap_delete_allowed(int obj_id, int obj_inst_id)
|
|
|
|
{
|
|
|
|
char pathstr[MAX_RESOURCE_LEN];
|
|
|
|
bool bootstrap_server;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (obj_id == LWM2M_OBJECT_SECURITY_ID) {
|
|
|
|
snprintk(pathstr, sizeof(pathstr), "%d/%d/1",
|
|
|
|
LWM2M_OBJECT_SECURITY_ID, obj_inst_id);
|
|
|
|
ret = lwm2m_engine_get_bool(pathstr, &bootstrap_server);
|
|
|
|
if (ret < 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bootstrap_server) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (obj_id == LWM2M_OBJECT_DEVICE_ID) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int bootstrap_delete(struct lwm2m_message *msg)
|
2019-01-25 22:36:47 -08:00
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst, *tmp;
|
|
|
|
int ret = 0;
|
|
|
|
|
2020-11-24 10:06:47 +01:00
|
|
|
if (msg->path.level > 2) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg->path.level == 2) {
|
|
|
|
if (!bootstrap_delete_allowed(msg->path.obj_id,
|
|
|
|
msg->path.obj_inst_id)) {
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return lwm2m_delete_obj_inst(msg->path.obj_id,
|
|
|
|
msg->path.obj_inst_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* DELETE all instances of a specific object or all object instances if
|
|
|
|
* not specified, excluding the following exceptions (according to the
|
|
|
|
* LwM2M specification v1.0.2, ch 5.2.7.5):
|
|
|
|
* - LwM2M Bootstrap-Server Account (Bootstrap Security object, ID 0)
|
|
|
|
* - Device object (ID 3)
|
|
|
|
*/
|
2019-01-25 22:36:47 -08:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&engine_obj_inst_list,
|
|
|
|
obj_inst, tmp, node) {
|
2020-11-24 10:06:47 +01:00
|
|
|
if (msg->path.level == 1 &&
|
|
|
|
obj_inst->obj->obj_id != msg->path.obj_id) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!bootstrap_delete_allowed(obj_inst->obj->obj_id,
|
|
|
|
obj_inst->obj_inst_id)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lwm2m_delete_obj_inst(obj_inst->obj->obj_id,
|
|
|
|
obj_inst->obj_inst_id);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
2019-01-25 22:36:47 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2022-01-21 03:23:56 -08:00
|
|
|
static bool lwm2m_engine_path_included(uint8_t code, bool bootstrap_mode)
|
|
|
|
{
|
|
|
|
switch (code & COAP_REQUEST_MASK) {
|
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
|
|
case COAP_METHOD_DELETE:
|
|
|
|
case COAP_METHOD_GET:
|
|
|
|
if (bootstrap_mode) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
case COAP_METHOD_FETCH:
|
|
|
|
/* Composite Read operation */
|
|
|
|
case COAP_METHOD_IPATCH:
|
|
|
|
/* Composite write operation */
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static int lwm2m_engine_observation_handler(struct lwm2m_message *msg, int observe, uint16_t accept,
|
|
|
|
bool composite)
|
2022-01-26 00:12:06 -08:00
|
|
|
{
|
|
|
|
int r;
|
|
|
|
|
|
|
|
if (observe == 0) {
|
|
|
|
/* add new observer */
|
|
|
|
r = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_OBSERVE,
|
|
|
|
OBSERVE_COUNTER_START);
|
|
|
|
if (r < 0) {
|
|
|
|
LOG_ERR("OBSERVE option error: %d", r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
if (composite) {
|
|
|
|
r = engine_add_composite_observer(msg, msg->token, msg->tkl, accept);
|
|
|
|
} else {
|
|
|
|
r = engine_add_observer(msg, msg->token, msg->tkl, accept);
|
|
|
|
}
|
|
|
|
|
2022-01-26 00:12:06 -08:00
|
|
|
if (r < 0) {
|
|
|
|
LOG_ERR("add OBSERVE error: %d", r);
|
|
|
|
}
|
|
|
|
} else if (observe == 1) {
|
|
|
|
/* remove observer */
|
2022-02-28 07:42:53 -08:00
|
|
|
if (composite) {
|
|
|
|
r = engine_remove_composite_observer(msg, msg->token, msg->tkl,
|
|
|
|
accept);
|
|
|
|
} else {
|
|
|
|
r = engine_remove_observer_by_token(msg->ctx, msg->token, msg->tkl);
|
|
|
|
if (r < 0) {
|
2022-01-26 00:12:06 -08:00
|
|
|
#if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
|
2022-02-28 07:42:53 -08:00
|
|
|
r = engine_remove_observer_by_path(msg->ctx, &msg->path);
|
|
|
|
if (r < 0)
|
2022-01-26 00:12:06 -08:00
|
|
|
#endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
|
2022-02-28 07:42:53 -08:00
|
|
|
{
|
|
|
|
LOG_ERR("remove observe error: %d", r);
|
|
|
|
r = 0;
|
|
|
|
}
|
2022-01-26 00:12:06 -08:00
|
|
|
}
|
|
|
|
}
|
2022-02-28 07:42:53 -08:00
|
|
|
|
2022-01-26 00:12:06 -08:00
|
|
|
} else {
|
|
|
|
r = -EINVAL;
|
|
|
|
}
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2022-02-01 01:09:51 -08:00
|
|
|
static int lwm2m_engine_default_content_format(uint16_t *accept_format)
|
|
|
|
{
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) {
|
|
|
|
/* Select content format use SenML CBOR when it possible */
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
|
|
|
|
LOG_DBG("No accept option given. Assume SenML Json.");
|
|
|
|
*accept_format = LWM2M_FORMAT_APP_SEML_JSON;
|
|
|
|
} else {
|
|
|
|
LOG_ERR("SenML CBOR or JSON is not supported");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG_DBG("No accept option given. Assume OMA TLV.");
|
|
|
|
*accept_format = LWM2M_FORMAT_OMA_TLV;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
static int handle_request(struct coap_packet *request,
|
2017-09-01 01:11:43 -07:00
|
|
|
struct lwm2m_message *msg)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
|
|
|
int r;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t code;
|
2017-09-19 16:04:32 -07:00
|
|
|
struct coap_option options[4];
|
2017-11-15 09:46:13 +08:00
|
|
|
struct lwm2m_engine_obj *obj = NULL;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t token[8];
|
|
|
|
uint8_t tkl = 0U;
|
|
|
|
uint16_t format = LWM2M_FORMAT_NONE, accept;
|
2017-07-07 11:04:03 -07:00
|
|
|
int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
|
2020-06-18 11:32:34 +02:00
|
|
|
int block_opt, block_num;
|
2020-09-16 15:09:30 +02:00
|
|
|
struct lwm2m_block_context *block_ctx = NULL;
|
2017-09-19 16:04:32 -07:00
|
|
|
enum coap_block_size block_size;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t payload_len = 0U;
|
2017-10-23 10:10:08 +08:00
|
|
|
bool last_block = false;
|
2020-06-18 11:32:34 +02:00
|
|
|
bool ignore = false;
|
2021-07-06 15:03:56 +02:00
|
|
|
const uint8_t *payload_start;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
/* set CoAP request / message */
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->in.in_cpkt = request;
|
|
|
|
msg->out.out_cpkt = &msg->cpkt;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
/* set default reader/writer */
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->in.reader = &plain_text_reader;
|
|
|
|
msg->out.writer = &plain_text_writer;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
code = coap_header_get_code(msg->in.in_cpkt);
|
2017-08-28 18:11:02 +08:00
|
|
|
|
2017-11-15 16:20:05 +08:00
|
|
|
/* setup response token */
|
2019-01-24 14:40:23 -08:00
|
|
|
tkl = coap_header_get_token(msg->in.in_cpkt, token);
|
2017-11-15 16:20:05 +08:00
|
|
|
if (tkl) {
|
|
|
|
msg->tkl = tkl;
|
|
|
|
msg->token = token;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* parse the URL path into components */
|
2019-01-24 14:40:23 -08:00
|
|
|
r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options,
|
2018-07-12 10:00:02 -07:00
|
|
|
ARRAY_SIZE(options));
|
2020-11-24 10:06:47 +01:00
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Treat empty URI path option as is there were no option - this will be
|
|
|
|
* represented as a level "zero" in the path structure.
|
|
|
|
*/
|
|
|
|
if (r == 1 && options[0].len == 0) {
|
|
|
|
r = 0;
|
|
|
|
}
|
|
|
|
|
2022-01-21 03:23:56 -08:00
|
|
|
if (r == 0 && lwm2m_engine_path_included(code, msg->ctx->bootstrap_mode)) {
|
2020-11-24 10:06:47 +01:00
|
|
|
/* No URI path or empty URI path option - allowed only during
|
2022-01-21 03:23:56 -08:00
|
|
|
* bootstrap or CoAP Fetch or iPATCH.
|
2020-11-24 10:06:47 +01:00
|
|
|
*/
|
2022-01-17 07:34:34 -08:00
|
|
|
|
2022-01-21 03:23:56 -08:00
|
|
|
r = -EPERM;
|
|
|
|
goto error;
|
2017-08-28 14:54:40 +08:00
|
|
|
}
|
|
|
|
|
2019-08-17 10:05:55 -07:00
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
|
|
/* check for bootstrap-finish */
|
2021-01-28 13:55:40 +01:00
|
|
|
if ((code & COAP_REQUEST_MASK) == COAP_METHOD_POST && r == 1 &&
|
|
|
|
strncmp(options[0].value, "bs", options[0].len) == 0) {
|
2019-08-17 10:05:55 -07:00
|
|
|
engine_bootstrap_finish();
|
2020-07-15 14:32:51 +02:00
|
|
|
|
2020-10-08 10:06:46 +02:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CHANGED;
|
|
|
|
|
2020-07-15 14:32:51 +02:00
|
|
|
r = lwm2m_init_message(msg);
|
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2019-08-17 10:05:55 -07:00
|
|
|
return 0;
|
2021-01-28 13:55:40 +01:00
|
|
|
} else
|
2019-08-17 10:05:55 -07:00
|
|
|
#endif
|
2021-01-28 13:55:40 +01:00
|
|
|
{
|
2019-01-24 14:40:23 -08:00
|
|
|
r = coap_options_to_path(options, r, &msg->path);
|
2017-08-28 17:06:48 +08:00
|
|
|
if (r < 0) {
|
|
|
|
r = -ENOENT;
|
|
|
|
goto error;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2018-02-05 16:05:51 +08:00
|
|
|
/* read Content Format / setup in.reader */
|
2019-01-24 14:40:23 -08:00
|
|
|
r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_CONTENT_FORMAT,
|
2017-07-07 11:04:03 -07:00
|
|
|
options, 1);
|
|
|
|
if (r > 0) {
|
2018-01-30 15:18:57 +08:00
|
|
|
format = coap_option_value_to_int(&options[0]);
|
2019-01-24 14:40:23 -08:00
|
|
|
r = select_reader(&msg->in, format);
|
2018-01-30 15:18:57 +08:00
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2018-02-05 16:05:51 +08:00
|
|
|
/* read Accept / setup out.writer */
|
2019-01-24 14:40:23 -08:00
|
|
|
r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_ACCEPT, options, 1);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (r > 0) {
|
2017-09-19 16:04:32 -07:00
|
|
|
accept = coap_option_value_to_int(&options[0]);
|
2017-07-07 11:04:03 -07:00
|
|
|
} else {
|
2022-02-01 01:09:51 -08:00
|
|
|
/* Select Default based LWM2M_VERSION */
|
|
|
|
r = lwm2m_engine_default_content_format(&accept);
|
|
|
|
if (r) {
|
|
|
|
goto error;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-01-24 14:40:23 -08:00
|
|
|
r = select_writer(&msg->out, accept);
|
2018-02-05 16:05:51 +08:00
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2022-01-21 03:23:56 -08:00
|
|
|
/* Do Only Object find if path have been parsed */
|
|
|
|
if (lwm2m_engine_path_included(code, msg->ctx->bootstrap_mode)) {
|
2022-01-17 07:34:34 -08:00
|
|
|
if (!(msg->ctx->bootstrap_mode && msg->path.level == LWM2M_PATH_LEVEL_NONE)) {
|
|
|
|
/* find registered obj */
|
|
|
|
obj = get_engine_obj(msg->path.obj_id);
|
|
|
|
if (!obj) {
|
|
|
|
/* No matching object found - ignore request */
|
|
|
|
r = -ENOENT;
|
|
|
|
goto error;
|
|
|
|
}
|
2017-11-15 09:46:13 +08:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set the operation */
|
2017-09-19 16:04:32 -07:00
|
|
|
switch (code & COAP_REQUEST_MASK) {
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
case COAP_METHOD_GET:
|
2017-10-15 13:42:45 -07:00
|
|
|
/*
|
2017-11-20 15:33:32 +08:00
|
|
|
* LwM2M V1_0_1-20170704-A, table 25,
|
|
|
|
* Discover: CoAP GET + accept=LWM2M_FORMAT_APP_LINK_FORMAT
|
2017-10-15 13:42:45 -07:00
|
|
|
*/
|
2021-01-28 13:55:40 +01:00
|
|
|
if (accept == LWM2M_FORMAT_APP_LINK_FORMAT) {
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_DISCOVER;
|
2017-07-07 11:04:03 -07:00
|
|
|
accept = LWM2M_FORMAT_APP_LINK_FORMAT;
|
|
|
|
} else {
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_READ;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2017-11-15 09:46:13 +08:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* check for observe */
|
2020-05-28 00:00:38 -03:00
|
|
|
observe = coap_get_option_int(msg->in.in_cpkt,
|
|
|
|
COAP_OPTION_OBSERVE);
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CONTENT;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2022-01-17 07:34:34 -08:00
|
|
|
case COAP_METHOD_FETCH:
|
|
|
|
msg->operation = LWM2M_OP_READ;
|
|
|
|
/* check for observe */
|
|
|
|
observe = coap_get_option_int(msg->in.in_cpkt,
|
|
|
|
COAP_OPTION_OBSERVE);
|
|
|
|
msg->code = COAP_RESPONSE_CODE_CONTENT;
|
|
|
|
break;
|
|
|
|
|
2022-01-21 03:23:56 -08:00
|
|
|
case COAP_METHOD_IPATCH:
|
|
|
|
msg->operation = LWM2M_OP_WRITE;
|
|
|
|
msg->code = COAP_RESPONSE_CODE_CHANGED;
|
|
|
|
break;
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
case COAP_METHOD_POST:
|
2019-03-26 19:57:45 -06:00
|
|
|
if (msg->path.level == 1U) {
|
2017-11-20 17:36:23 +08:00
|
|
|
/* create an object instance */
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_CREATE;
|
2017-10-11 13:44:35 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CREATED;
|
2019-03-26 19:57:45 -06:00
|
|
|
} else if (msg->path.level == 2U) {
|
2017-11-20 17:36:23 +08:00
|
|
|
/* write values to an object instance */
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_WRITE;
|
2017-11-20 17:36:23 +08:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CHANGED;
|
2017-07-07 11:04:03 -07:00
|
|
|
} else {
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_EXECUTE;
|
2017-10-11 13:44:35 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CHANGED;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2017-11-20 17:36:23 +08:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
case COAP_METHOD_PUT:
|
2017-11-21 18:16:52 +08:00
|
|
|
/* write attributes if content-format is absent */
|
|
|
|
if (format == LWM2M_FORMAT_NONE) {
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_WRITE_ATTR;
|
2017-11-21 18:16:52 +08:00
|
|
|
} else {
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_WRITE;
|
2017-11-21 18:16:52 +08:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CHANGED;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
case COAP_METHOD_DELETE:
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->operation = LWM2M_OP_DELETE;
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_DELETED;
|
2017-07-07 11:04:03 -07:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2017-10-19 16:27:32 -07:00
|
|
|
/* setup incoming data */
|
2021-07-06 15:03:56 +02:00
|
|
|
payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
|
|
|
|
if (payload_len > 0) {
|
|
|
|
msg->in.offset = payload_start - msg->in.in_cpkt->data;
|
|
|
|
} else {
|
|
|
|
msg->in.offset = msg->in.in_cpkt->offset;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-07-25 16:54:25 +08:00
|
|
|
/* Check for block transfer */
|
2020-06-18 11:32:34 +02:00
|
|
|
|
|
|
|
block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
|
|
|
|
if (block_opt > 0) {
|
|
|
|
last_block = !GET_MORE(block_opt);
|
2017-10-23 10:10:08 +08:00
|
|
|
|
2017-07-25 16:54:25 +08:00
|
|
|
/* RFC7252: 4.6. Message Size */
|
2020-06-18 11:32:34 +02:00
|
|
|
block_size = GET_BLOCK_SIZE(block_opt);
|
2017-10-23 10:10:08 +08:00
|
|
|
if (!last_block &&
|
2019-01-25 14:13:07 -08:00
|
|
|
coap_block_size_to_bytes(block_size) > payload_len) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("Trailing payload is discarded!");
|
2017-07-25 16:54:25 +08:00
|
|
|
r = -EFBIG;
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
block_num = GET_BLOCK_NUM(block_opt);
|
|
|
|
|
|
|
|
/* Try to retrieve existing block context. If one not exists,
|
|
|
|
* and we've received first block, allocate new context.
|
|
|
|
*/
|
|
|
|
r = get_block_ctx(token, tkl, &block_ctx);
|
|
|
|
if (r < 0 && block_num == 0) {
|
2017-07-25 16:54:25 +08:00
|
|
|
r = init_block_ctx(token, tkl, &block_ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (r < 0) {
|
2020-06-18 11:32:34 +02:00
|
|
|
LOG_ERR("Cannot find block context");
|
2017-07-25 16:54:25 +08:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
msg->in.block_ctx = block_ctx;
|
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
if (block_num < block_ctx->expected) {
|
|
|
|
LOG_WRN("Block already handled %d, expected %d",
|
|
|
|
block_num, block_ctx->expected);
|
|
|
|
ignore = true;
|
|
|
|
} else if (block_num > block_ctx->expected) {
|
|
|
|
LOG_WRN("Block out of order %d, expected %d",
|
|
|
|
block_num, block_ctx->expected);
|
|
|
|
r = -EFAULT;
|
2017-10-23 10:10:08 +08:00
|
|
|
goto error;
|
2020-06-18 11:32:34 +02:00
|
|
|
} else {
|
|
|
|
r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx);
|
|
|
|
if (r < 0) {
|
|
|
|
LOG_ERR("Error from block update: %d", r);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
block_ctx->last_block = last_block;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
/* Initial block sent by the server might be larger than
|
|
|
|
* our block size therefore it is needed to take this
|
|
|
|
* into account when calculating next expected block
|
|
|
|
* number.
|
|
|
|
*/
|
|
|
|
block_ctx->expected += GET_BLOCK_SIZE(block_opt) -
|
|
|
|
block_ctx->ctx.block_size + 1;
|
|
|
|
}
|
net: lwm2m: Fix opaque data transfer in block mode
When FW update in PUSH mode is used, the firmware is encapsulated in the
TLV as an opaque data, according to the LMWM2M satandard, and then
sliced into blocks and transferred block by block in several
transactions. Therefore, the TLV header is only present in the initial
message.
Current implementation did not handle this case well, reporting errors
on consecutive blocks, therefore making the FW update in PUSH mode
broken.
This commit fixes this issue with following changes:
* The TLV is only assumed to be present in the initial block, while
consecutive blocks will be processed directly into the appropriate
handler,
* 32-bit variables shall be used whenever dealing with the opaque data
length, since the firmware size can easily exceed the 16-bit range,
* Additional information, required for the FW block transfer to work
properly were added to the block context structure,
* The application shall only be notified of the actual data length, and
not the total block size (the total TLV size including header).
Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2020-06-03 10:48:44 +02:00
|
|
|
|
2017-10-23 10:10:08 +08:00
|
|
|
/* Handle blockwise 1 (Part 1): Set response code */
|
|
|
|
if (!last_block) {
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_CONTINUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* render CoAP packet header */
|
2017-10-11 14:04:39 -07:00
|
|
|
r = lwm2m_init_message(msg);
|
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
2017-09-19 16:04:32 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
if (!ignore) {
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
switch (msg->operation) {
|
|
|
|
|
|
|
|
case LWM2M_OP_READ:
|
2022-01-26 00:12:06 -08:00
|
|
|
if (observe >= 0) {
|
|
|
|
/* Validate That Token is valid for Observation */
|
|
|
|
if (!msg->token) {
|
|
|
|
LOG_ERR("OBSERVE request missing token");
|
|
|
|
r = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
2020-06-18 11:32:34 +02:00
|
|
|
|
2022-01-26 00:12:06 -08:00
|
|
|
if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) {
|
2022-03-16 21:07:43 +00:00
|
|
|
/* Normal Observation Request or Cancel */
|
2022-02-28 07:42:53 -08:00
|
|
|
r = lwm2m_engine_observation_handler(msg, observe, accept,
|
|
|
|
false);
|
2020-06-18 11:32:34 +02:00
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
2022-01-26 00:12:06 -08:00
|
|
|
|
|
|
|
r = do_read_op(msg, accept);
|
2020-06-18 11:32:34 +02:00
|
|
|
} else {
|
2022-01-26 00:12:06 -08:00
|
|
|
/* Composite Observation request & cancel handler */
|
2022-02-28 07:42:53 -08:00
|
|
|
r = lwm2m_engine_observation_handler(msg, observe, accept,
|
|
|
|
true);
|
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
r = do_composite_read_op(msg, accept);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2022-01-17 07:34:34 -08:00
|
|
|
} else {
|
2022-01-26 00:12:06 -08:00
|
|
|
if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) {
|
|
|
|
r = do_read_op(msg, accept);
|
|
|
|
} else {
|
|
|
|
r = do_composite_read_op(msg, accept);
|
|
|
|
}
|
2022-01-17 07:34:34 -08:00
|
|
|
}
|
2020-06-18 11:32:34 +02:00
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
case LWM2M_OP_DISCOVER:
|
2021-01-25 13:49:33 +01:00
|
|
|
r = do_discover_op(msg, accept);
|
2020-06-18 11:32:34 +02:00
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
case LWM2M_OP_WRITE:
|
|
|
|
case LWM2M_OP_CREATE:
|
2022-01-21 03:23:56 -08:00
|
|
|
if ((code & COAP_REQUEST_MASK) == COAP_METHOD_IPATCH) {
|
|
|
|
/* iPATCH is for Composite purpose */
|
|
|
|
r = do_composite_write_op(msg, format);
|
|
|
|
} else {
|
|
|
|
/* Single resource write Operation */
|
|
|
|
r = do_write_op(msg, format);
|
|
|
|
}
|
2020-06-18 11:32:34 +02:00
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
case LWM2M_OP_WRITE_ATTR:
|
|
|
|
r = lwm2m_write_attr_handler(obj, msg);
|
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
case LWM2M_OP_EXECUTE:
|
|
|
|
r = lwm2m_exec_handler(msg);
|
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
case LWM2M_OP_DELETE:
|
2020-11-24 10:06:47 +01:00
|
|
|
#if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
|
|
|
|
if (msg->ctx->bootstrap_mode) {
|
|
|
|
r = bootstrap_delete(msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2020-06-18 11:32:34 +02:00
|
|
|
r = lwm2m_delete_handler(msg);
|
|
|
|
break;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
default:
|
|
|
|
LOG_ERR("Unknown operation: %u", msg->operation);
|
|
|
|
r = -EINVAL;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2020-06-18 11:32:34 +02:00
|
|
|
if (r < 0) {
|
|
|
|
goto error;
|
|
|
|
}
|
2017-07-25 16:54:25 +08:00
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-10-23 10:10:08 +08:00
|
|
|
/* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */
|
2017-07-25 16:54:25 +08:00
|
|
|
if (block_ctx) {
|
2017-10-23 10:10:08 +08:00
|
|
|
if (!last_block) {
|
2017-07-25 16:54:25 +08:00
|
|
|
/* More to come, ack with correspond block # */
|
2019-01-24 14:40:23 -08:00
|
|
|
r = coap_append_block1_option(msg->out.out_cpkt,
|
2017-09-19 16:04:32 -07:00
|
|
|
&block_ctx->ctx);
|
2017-10-19 16:27:32 -07:00
|
|
|
if (r < 0) {
|
2017-07-25 16:54:25 +08:00
|
|
|
/* report as internal server error */
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Fail adding block1 option: %d", r);
|
2017-07-25 16:54:25 +08:00
|
|
|
r = -EINVAL;
|
|
|
|
goto error;
|
|
|
|
}
|
2017-10-26 23:45:59 -07:00
|
|
|
} else {
|
|
|
|
/* Free context when finished */
|
|
|
|
free_block_ctx(block_ctx);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2017-07-25 16:54:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
error:
|
2017-10-11 14:04:39 -07:00
|
|
|
lwm2m_reset_message(msg, false);
|
2017-07-25 16:54:25 +08:00
|
|
|
if (r == -ENOENT) {
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_NOT_FOUND;
|
2017-07-25 16:54:25 +08:00
|
|
|
} else if (r == -EPERM) {
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_NOT_ALLOWED;
|
2017-07-25 16:54:25 +08:00
|
|
|
} else if (r == -EEXIST) {
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_BAD_REQUEST;
|
2017-11-02 18:36:24 +08:00
|
|
|
} else if (r == -EFAULT) {
|
|
|
|
msg->code = COAP_RESPONSE_CODE_INCOMPLETE;
|
2017-07-25 16:54:25 +08:00
|
|
|
} else if (r == -EFBIG) {
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_REQUEST_TOO_LARGE;
|
2017-11-21 18:16:52 +08:00
|
|
|
} else if (r == -ENOTSUP) {
|
|
|
|
msg->code = COAP_RESPONSE_CODE_NOT_IMPLEMENTED;
|
2018-01-30 15:18:57 +08:00
|
|
|
} else if (r == -ENOMSG) {
|
|
|
|
msg->code = COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT;
|
2017-07-25 16:54:25 +08:00
|
|
|
} else {
|
|
|
|
/* Failed to handle the request */
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->code = COAP_RESPONSE_CODE_INTERNAL_ERROR;
|
2017-07-25 16:54:25 +08:00
|
|
|
}
|
|
|
|
|
2017-10-11 14:04:39 -07:00
|
|
|
r = lwm2m_init_message(msg);
|
|
|
|
if (r < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Error recreating message: %d", r);
|
2017-10-11 14:04:39 -07:00
|
|
|
}
|
|
|
|
|
2017-07-25 16:54:25 +08:00
|
|
|
/* Free block context when error happened */
|
|
|
|
free_block_ctx(block_ctx);
|
|
|
|
|
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2020-12-01 13:56:34 +01:00
|
|
|
static int lwm2m_response_promote_to_con(struct lwm2m_message *msg)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
msg->type = COAP_TYPE_CON;
|
|
|
|
msg->mid = coap_next_id();
|
|
|
|
|
|
|
|
/* Since the response CoAP packet is already generated at this point,
|
|
|
|
* tweak the specific fields manually:
|
|
|
|
* - CoAP message type (byte 0, bits 2 and 3)
|
|
|
|
* - CoAP message id (bytes 2 and 3)
|
|
|
|
*/
|
|
|
|
msg->cpkt.data[0] &= ~(0x3 << 4);
|
|
|
|
msg->cpkt.data[0] |= (msg->type & 0x3) << 4;
|
|
|
|
msg->cpkt.data[2] = msg->mid >> 8;
|
|
|
|
msg->cpkt.data[3] = (uint8_t) msg->mid;
|
|
|
|
|
|
|
|
/* Add the packet to the pending list. */
|
|
|
|
msg->pending = coap_pending_next_unused(
|
|
|
|
msg->ctx->pendings,
|
|
|
|
CONFIG_LWM2M_ENGINE_MAX_PENDING);
|
|
|
|
if (!msg->pending) {
|
|
|
|
LOG_ERR("Unable to find a free pending to track "
|
|
|
|
"retransmissions.");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2021-01-18 13:13:26 +01:00
|
|
|
ret = coap_pending_init(msg->pending, &msg->cpkt,
|
|
|
|
&msg->ctx->remote_addr,
|
|
|
|
COAP_DEFAULT_MAX_RETRANSMIT);
|
2020-12-01 13:56:34 +01:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Unable to initialize a pending "
|
|
|
|
"retransmission (err:%d).", ret);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx,
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t *buf, uint16_t buf_len,
|
2019-01-28 11:55:23 -08:00
|
|
|
struct sockaddr *from_addr,
|
2019-01-25 21:43:52 -08:00
|
|
|
udp_request_handler_cb_t udp_request_handler)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-09-01 01:11:43 -07:00
|
|
|
struct lwm2m_message *msg = NULL;
|
2017-09-19 16:04:32 -07:00
|
|
|
struct coap_pending *pending;
|
|
|
|
struct coap_reply *reply;
|
|
|
|
struct coap_packet response;
|
|
|
|
int r;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t token[8];
|
|
|
|
uint8_t tkl;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
r = coap_packet_parse(&response, buf, buf_len, NULL, 0);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (r < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Invalid data received (err:%d)", r);
|
2019-01-25 14:13:07 -08:00
|
|
|
return;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
tkl = coap_header_get_token(&response, token);
|
|
|
|
pending = coap_pending_received(&response, client_ctx->pendings,
|
2017-08-24 08:51:25 -07:00
|
|
|
CONFIG_LWM2M_ENGINE_MAX_PENDING);
|
2020-10-13 14:10:55 +02:00
|
|
|
if (pending && coap_header_get_type(&response) == COAP_TYPE_ACK) {
|
2017-09-05 21:03:49 -07:00
|
|
|
msg = find_msg(pending, NULL);
|
2020-10-13 14:10:55 +02:00
|
|
|
if (msg == NULL) {
|
|
|
|
LOG_DBG("Orphaned pending %p.", pending);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg->acknowledged = true;
|
|
|
|
|
|
|
|
if (msg->reply == NULL) {
|
|
|
|
/* No response expected, release the message. */
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the original message was a request and an empty
|
|
|
|
* ACK was received, expect separate response later.
|
|
|
|
*/
|
|
|
|
if ((msg->code >= COAP_METHOD_GET) &&
|
|
|
|
(msg->code <= COAP_METHOD_DELETE) &&
|
|
|
|
(coap_header_get_code(&response) == COAP_CODE_EMPTY)) {
|
|
|
|
LOG_DBG("Empty ACK, expect separate response.");
|
|
|
|
return;
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("checking for reply from [%s]",
|
2019-05-29 16:49:18 -07:00
|
|
|
log_strdup(lwm2m_sprint_ip_addr(from_addr)));
|
2019-01-28 11:55:23 -08:00
|
|
|
reply = coap_response_received(&response, from_addr,
|
2017-08-24 08:51:25 -07:00
|
|
|
client_ctx->replies,
|
|
|
|
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
|
2017-09-01 01:11:43 -07:00
|
|
|
if (reply) {
|
2020-10-13 14:10:55 +02:00
|
|
|
msg = find_msg(NULL, reply);
|
2017-09-05 21:03:49 -07:00
|
|
|
|
2020-10-13 14:10:55 +02:00
|
|
|
if (coap_header_get_type(&response) == COAP_TYPE_CON) {
|
|
|
|
r = lwm2m_send_empty_ack(client_ctx,
|
|
|
|
coap_header_get_id(&response));
|
|
|
|
if (r < 0) {
|
|
|
|
LOG_ERR("Error transmitting ACK");
|
|
|
|
}
|
2017-09-05 21:03:49 -07:00
|
|
|
}
|
2017-09-01 01:11:43 -07:00
|
|
|
|
2018-03-23 10:49:44 -07:00
|
|
|
/* skip release if reply->user_data has error condition */
|
2021-09-28 10:11:49 +02:00
|
|
|
if (reply && reply->user_data == (void *)COAP_REPLY_STATUS_ERROR) {
|
2018-03-23 10:49:44 -07:00
|
|
|
/* reset reply->user_data for next time */
|
|
|
|
reply->user_data = (void *)COAP_REPLY_STATUS_NONE;
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("reply %p NOT removed", reply);
|
2019-01-25 14:13:07 -08:00
|
|
|
return;
|
2018-03-23 10:49:44 -07:00
|
|
|
}
|
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
/* free up msg resources */
|
|
|
|
if (msg) {
|
2017-10-11 11:29:54 -07:00
|
|
|
lwm2m_reset_message(msg, true);
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("reply %p handled and removed", reply);
|
2019-01-25 14:13:07 -08:00
|
|
|
return;
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If no normal response handler is found, then this is
|
|
|
|
* a new request coming from the server. Let's look
|
|
|
|
* at registered objects to find a handler.
|
|
|
|
*/
|
|
|
|
if (udp_request_handler &&
|
2017-09-19 16:04:32 -07:00
|
|
|
coap_header_get_type(&response) == COAP_TYPE_CON) {
|
2017-09-01 01:11:43 -07:00
|
|
|
msg = lwm2m_get_message(client_ctx);
|
|
|
|
if (!msg) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
2019-01-25 14:13:07 -08:00
|
|
|
return;
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Create a response message if we reach this point */
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->type = COAP_TYPE_ACK;
|
|
|
|
msg->code = coap_header_get_code(&response);
|
|
|
|
msg->mid = coap_header_get_id(&response);
|
2017-09-01 01:11:43 -07:00
|
|
|
/* skip token generation by default */
|
2020-10-08 13:45:08 +02:00
|
|
|
msg->tkl = 0;
|
2017-09-01 01:11:43 -07:00
|
|
|
|
2020-12-01 13:56:34 +01:00
|
|
|
client_ctx->processed_req = msg;
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
/* process the response to this request */
|
|
|
|
r = udp_request_handler(&response, msg);
|
2017-09-01 01:11:43 -07:00
|
|
|
if (r < 0) {
|
2019-01-25 14:13:07 -08:00
|
|
|
return;
|
2017-09-01 01:11:43 -07:00
|
|
|
}
|
|
|
|
|
2020-12-01 13:56:34 +01:00
|
|
|
if (msg->acknowledged) {
|
|
|
|
r = lwm2m_response_promote_to_con(msg);
|
|
|
|
if (r < 0) {
|
2022-03-16 21:07:43 +00:00
|
|
|
LOG_ERR("Failed to promote response to CON: %d",
|
2020-12-01 13:56:34 +01:00
|
|
|
r);
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
client_ctx->processed_req = NULL;
|
2021-05-17 16:23:51 +02:00
|
|
|
lwm2m_send_message_async(msg);
|
2017-09-01 01:11:43 -07:00
|
|
|
} else {
|
2020-04-24 12:41:42 +02:00
|
|
|
LOG_DBG("No handler for response");
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
/* returns ms until the next retransmission is due, or INT32_MAX
|
|
|
|
* if no retransmissions are necessary
|
|
|
|
*/
|
|
|
|
static int32_t retransmit_request(struct lwm2m_ctx *client_ctx,
|
|
|
|
const uint32_t timestamp)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-09-01 01:11:43 -07:00
|
|
|
struct lwm2m_message *msg;
|
2021-05-17 16:23:51 +02:00
|
|
|
struct coap_pending *p;
|
|
|
|
int32_t remaining, next_retransmission = INT32_MAX;
|
|
|
|
int i;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
for (i = 0, p = client_ctx->pendings;
|
|
|
|
i < CONFIG_LWM2M_ENGINE_MAX_PENDING; i++, p++) {
|
|
|
|
if (!p->timeout) {
|
|
|
|
continue;
|
2018-09-13 08:52:42 -07:00
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
remaining = p->t0 + p->timeout - timestamp;
|
|
|
|
if (remaining < 0) {
|
|
|
|
msg = find_msg(p, NULL);
|
|
|
|
if (!msg) {
|
|
|
|
LOG_ERR("pending has no valid LwM2M message!");
|
|
|
|
coap_pending_clear(p);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!p->retries) {
|
|
|
|
/* pending request has expired */
|
|
|
|
if (msg->message_timeout_cb) {
|
|
|
|
msg->message_timeout_cb(msg);
|
|
|
|
}
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (msg->acknowledged) {
|
|
|
|
/* No need to retransmit, just keep the timer running to
|
|
|
|
* timeout in case no response arrives.
|
|
|
|
*/
|
|
|
|
coap_pending_cycle(p);
|
|
|
|
continue;
|
|
|
|
}
|
2020-06-30 12:16:55 +02:00
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
lwm2m_send_message_async(msg);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (remaining < next_retransmission) {
|
|
|
|
next_retransmission = remaining;
|
|
|
|
}
|
2018-01-30 13:50:59 -08:00
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
return next_retransmission;
|
|
|
|
}
|
2020-06-30 12:16:55 +02:00
|
|
|
|
2021-06-22 12:48:54 +01:00
|
|
|
static void notify_message_timeout_cb(struct lwm2m_message *msg)
|
|
|
|
{
|
|
|
|
if (msg->ctx != NULL) {
|
|
|
|
struct lwm2m_ctx *client_ctx = msg->ctx;
|
|
|
|
|
2021-09-28 10:11:49 +02:00
|
|
|
if (client_ctx->observe_cb) {
|
|
|
|
client_ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT,
|
|
|
|
&msg->path, msg->reply->user_data);
|
2021-06-22 12:48:54 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_ERR("Notify Message Timed Out : %p", msg);
|
|
|
|
}
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
static int notify_message_reply_cb(const struct coap_packet *response,
|
|
|
|
struct coap_reply *reply,
|
2017-07-07 11:04:03 -07:00
|
|
|
const struct sockaddr *from)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint8_t type, code;
|
2021-05-25 22:55:59 +02:00
|
|
|
struct lwm2m_message *msg;
|
2021-09-28 10:11:49 +02:00
|
|
|
struct observe_node *obs, *found_obj = NULL;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
type = coap_header_get_type(response);
|
|
|
|
code = coap_header_get_code(response);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'",
|
2017-07-07 11:04:03 -07:00
|
|
|
type,
|
2017-09-19 16:04:32 -07:00
|
|
|
COAP_RESPONSE_CODE_CLASS(code),
|
|
|
|
COAP_RESPONSE_CODE_DETAIL(code),
|
2019-05-29 16:49:18 -07:00
|
|
|
log_strdup(sprint_token(reply->token, reply->tkl)));
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2021-09-28 10:11:49 +02:00
|
|
|
msg = find_msg(NULL, reply);
|
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
/* remove observer on COAP_TYPE_RESET */
|
|
|
|
if (type == COAP_TYPE_RESET) {
|
2017-07-07 11:04:03 -07:00
|
|
|
if (reply->tkl > 0) {
|
2021-09-28 10:11:49 +02:00
|
|
|
ret = engine_remove_observer_by_token(msg->ctx, reply->token, reply->tkl);
|
2017-07-07 11:04:03 -07:00
|
|
|
if (ret) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("remove observe error: %d", ret);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
} else {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("notify reply missing token -- ignored.");
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
2021-09-28 10:11:49 +02:00
|
|
|
} else {
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&msg->ctx->observer, obs, node) {
|
|
|
|
if (memcmp(obs->token, reply->token, reply->tkl) == 0) {
|
|
|
|
found_obj = obs;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (found_obj) {
|
|
|
|
if (msg->ctx->observe_cb) {
|
|
|
|
msg->ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_ACK,
|
2022-02-28 07:42:53 -08:00
|
|
|
lwm2m_read_first_path_ptr(&obs->path_list),
|
|
|
|
reply->user_data);
|
2021-09-28 10:11:49 +02:00
|
|
|
}
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-05-25 22:55:59 +02:00
|
|
|
static int generate_notify_message(struct lwm2m_ctx *ctx,
|
|
|
|
struct observe_node *obs,
|
2021-09-28 10:11:49 +02:00
|
|
|
void *user_data)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-09-01 01:11:43 -07:00
|
|
|
struct lwm2m_message *msg;
|
2017-07-07 11:04:03 -07:00
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
2022-02-28 07:42:53 -08:00
|
|
|
struct lwm2m_obj_path *path;
|
2017-07-07 11:04:03 -07:00
|
|
|
int ret = 0;
|
|
|
|
|
2021-05-25 22:55:59 +02:00
|
|
|
msg = lwm2m_get_message(ctx);
|
2019-01-24 14:40:23 -08:00
|
|
|
if (!msg) {
|
|
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
if (!obs->composite) {
|
|
|
|
path = lwm2m_read_first_path_ptr(&obs->path_list);
|
|
|
|
if (!path) {
|
|
|
|
LOG_ERR("Observation node not include path");
|
|
|
|
ret = -EINVAL;
|
|
|
|
}
|
|
|
|
/* copy path */
|
|
|
|
memcpy(&msg->path, path, sizeof(struct lwm2m_obj_path));
|
|
|
|
LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
|
|
|
|
obs->resource_update ? "MANUAL" : "AUTO", path->obj_id, path->obj_inst_id,
|
|
|
|
path->res_id, path->level, log_strdup(sprint_token(obs->token, obs->tkl)),
|
|
|
|
log_strdup(lwm2m_sprint_ip_addr(&ctx->remote_addr)),
|
|
|
|
(long long)k_uptime_get());
|
|
|
|
|
|
|
|
obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
|
|
|
|
if (!obj_inst) {
|
|
|
|
LOG_ERR("unable to get engine obj for %u/%u", path->obj_id,
|
|
|
|
path->obj_inst_id);
|
|
|
|
ret = -EINVAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOG_DBG("[%s] NOTIFY MSG START: (Composite)) token:'%s' [%s] %lld",
|
2022-02-23 23:43:52 -08:00
|
|
|
obs->resource_update ? "MANUAL" : "AUTO",
|
2019-05-29 16:49:18 -07:00
|
|
|
log_strdup(sprint_token(obs->token, obs->tkl)),
|
2021-05-25 22:55:59 +02:00
|
|
|
log_strdup(lwm2m_sprint_ip_addr(&ctx->remote_addr)),
|
2022-01-13 13:30:58 +01:00
|
|
|
(long long)k_uptime_get());
|
2022-02-28 07:42:53 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
msg->operation = LWM2M_OP_READ;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
obs->resource_update = false;
|
2022-02-28 07:42:53 -08:00
|
|
|
|
2017-09-01 01:11:43 -07:00
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
msg->type = COAP_TYPE_CON;
|
|
|
|
msg->code = COAP_RESPONSE_CODE_CONTENT;
|
2020-09-10 16:23:48 +02:00
|
|
|
msg->mid = coap_next_id();
|
2017-09-01 01:11:43 -07:00
|
|
|
msg->token = obs->token;
|
|
|
|
msg->tkl = obs->tkl;
|
|
|
|
msg->reply_cb = notify_message_reply_cb;
|
2021-06-22 12:48:54 +01:00
|
|
|
msg->message_timeout_cb = notify_message_timeout_cb;
|
2019-01-24 14:40:23 -08:00
|
|
|
msg->out.out_cpkt = &msg->cpkt;
|
2017-09-01 01:11:43 -07:00
|
|
|
|
|
|
|
ret = lwm2m_init_message(msg);
|
2017-09-19 16:04:32 -07:00
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("Unable to init lwm2m message! (err: %d)", ret);
|
2017-09-19 16:04:32 -07:00
|
|
|
goto cleanup;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2021-09-28 10:11:49 +02:00
|
|
|
/* lwm2m_init_message() cleans the coap reply fields, so we assign our data here */
|
|
|
|
msg->reply->user_data = user_data;
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
/* each notification should increment the obs counter */
|
|
|
|
obs->counter++;
|
2017-09-19 16:04:32 -07:00
|
|
|
ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE,
|
|
|
|
obs->counter);
|
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("OBSERVE option error: %d", ret);
|
2017-07-07 11:04:03 -07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set the output writer */
|
2019-01-24 14:40:23 -08:00
|
|
|
select_writer(&msg->out, obs->format);
|
2022-02-28 07:42:53 -08:00
|
|
|
if (obs->composite) {
|
|
|
|
/* Use do send which actually do Composite read operation */
|
|
|
|
ret = do_send_op(msg, obs->format, &obs->path_list);
|
|
|
|
} else {
|
|
|
|
ret = do_read_op(msg, obs->format);
|
|
|
|
}
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2017-09-19 16:04:32 -07:00
|
|
|
if (ret < 0) {
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_ERR("error in multi-format read (err:%d)", ret);
|
2017-07-07 11:04:03 -07:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
lwm2m_send_message_async(msg);
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2018-09-19 11:22:19 +03:00
|
|
|
LOG_DBG("NOTIFY MSG: SENT");
|
2017-09-01 01:11:43 -07:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
|
|
|
cleanup:
|
2017-10-11 11:29:54 -07:00
|
|
|
lwm2m_reset_message(msg, true);
|
2017-07-07 11:04:03 -07:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
static int32_t engine_next_service_timeout_ms(uint32_t max_timeout,
|
|
|
|
const int64_t timestamp)
|
2017-10-10 20:55:05 -07:00
|
|
|
{
|
|
|
|
struct service_node *srv;
|
2021-05-17 16:23:51 +02:00
|
|
|
uint64_t time_left_ms;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint32_t timeout = max_timeout;
|
2017-10-10 20:55:05 -07:00
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
|
2020-04-24 15:32:39 +02:00
|
|
|
time_left_ms = srv->last_timestamp + srv->min_call_period;
|
2017-10-10 20:55:05 -07:00
|
|
|
|
|
|
|
/* service is due */
|
|
|
|
if (time_left_ms < timestamp) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* service timeout is less than the current timeout */
|
|
|
|
time_left_ms -= timestamp;
|
|
|
|
if (time_left_ms < timeout) {
|
|
|
|
timeout = time_left_ms;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return timeout;
|
|
|
|
}
|
|
|
|
|
2020-05-27 11:26:57 -05:00
|
|
|
int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
|
2017-10-10 20:55:05 -07:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* find an unused service index node */
|
|
|
|
for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
|
2019-04-05 16:14:50 -07:00
|
|
|
if (!service_node_data[i].service_work) {
|
2017-10-10 20:55:05 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == MAX_PERIODIC_SERVICE) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2019-04-05 16:14:50 -07:00
|
|
|
service_node_data[i].service_work = service;
|
2017-10-10 20:55:05 -07:00
|
|
|
service_node_data[i].min_call_period = period_ms;
|
2019-03-26 19:57:45 -06:00
|
|
|
service_node_data[i].last_timestamp = 0U;
|
2017-10-10 20:55:05 -07:00
|
|
|
|
|
|
|
sys_slist_append(&engine_service_list,
|
|
|
|
&service_node_data[i].node);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-02-18 09:32:32 +01:00
|
|
|
int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period_ms)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
|
|
|
|
if (service_node_data[i].service_work == service) {
|
|
|
|
service_node_data[i].min_call_period = period_ms;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
static int32_t lwm2m_engine_service(const int64_t timestamp)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2017-10-10 20:55:05 -07:00
|
|
|
struct service_node *srv;
|
2021-05-17 16:23:51 +02:00
|
|
|
int64_t service_due_timestamp;
|
2017-10-10 20:55:05 -07:00
|
|
|
|
2019-01-25 22:47:58 -08:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
|
|
|
|
service_due_timestamp = srv->last_timestamp +
|
2020-04-24 15:32:39 +02:00
|
|
|
srv->min_call_period;
|
2019-01-25 22:47:58 -08:00
|
|
|
/* service is due */
|
2019-04-08 11:31:46 -07:00
|
|
|
if (timestamp >= service_due_timestamp) {
|
2019-01-25 22:47:58 -08:00
|
|
|
srv->last_timestamp = k_uptime_get();
|
2019-04-05 16:14:50 -07:00
|
|
|
srv->service_work(NULL);
|
2017-10-10 20:55:05 -07:00
|
|
|
}
|
2019-01-25 22:47:58 -08:00
|
|
|
}
|
2017-10-10 20:55:05 -07:00
|
|
|
|
2019-01-25 22:47:58 -08:00
|
|
|
/* calculate how long to sleep till the next service */
|
2021-05-17 16:23:51 +02:00
|
|
|
return engine_next_service_timeout_ms(ENGINE_UPDATE_INTERVAL_MS,
|
|
|
|
timestamp);
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2019-01-25 22:36:47 -08:00
|
|
|
int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx)
|
|
|
|
{
|
2019-01-28 11:55:23 -08:00
|
|
|
int sock_fd = client_ctx->sock_fd;
|
2020-02-23 20:04:10 +02:00
|
|
|
struct lwm2m_message *msg;
|
2021-05-25 22:55:59 +02:00
|
|
|
sys_snode_t *obs_node;
|
|
|
|
struct observe_node *obs;
|
2020-02-23 20:04:10 +02:00
|
|
|
size_t i;
|
2019-01-28 11:55:23 -08:00
|
|
|
|
2019-02-18 21:51:01 -08:00
|
|
|
/* Remove observes for this context */
|
2021-05-25 22:55:59 +02:00
|
|
|
while (!sys_slist_is_empty(&client_ctx->observer)) {
|
|
|
|
obs_node = sys_slist_get_not_empty(&client_ctx->observer);
|
|
|
|
obs = SYS_SLIST_CONTAINER(obs_node, obs, node);
|
2021-09-28 10:11:49 +02:00
|
|
|
remove_observer_from_list(client_ctx, NULL, obs);
|
2019-02-18 21:51:01 -08:00
|
|
|
}
|
|
|
|
|
2020-07-01 14:28:32 +02:00
|
|
|
for (i = 0, msg = messages; i < ARRAY_SIZE(messages); i++, msg++) {
|
|
|
|
if (msg->ctx == client_ctx) {
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
}
|
2020-02-23 20:04:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
coap_pendings_clear(client_ctx->pendings,
|
|
|
|
CONFIG_LWM2M_ENGINE_MAX_PENDING);
|
|
|
|
coap_replies_clear(client_ctx->replies,
|
|
|
|
CONFIG_LWM2M_ENGINE_MAX_REPLIES);
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
lwm2m_socket_del(client_ctx);
|
|
|
|
client_ctx->sock_fd = -1;
|
|
|
|
if (sock_fd >= 0) {
|
|
|
|
return close(sock_fd);
|
|
|
|
} else {
|
|
|
|
return 0;
|
|
|
|
}
|
2019-01-25 22:36:47 -08:00
|
|
|
}
|
|
|
|
|
2017-09-08 08:22:59 -07:00
|
|
|
void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx)
|
|
|
|
{
|
2021-05-17 16:23:51 +02:00
|
|
|
sys_slist_init(&client_ctx->pending_sends);
|
2021-05-25 22:55:59 +02:00
|
|
|
sys_slist_init(&client_ctx->observer);
|
2017-09-08 08:22:59 -07:00
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/* LwM2M Socket Integration */
|
|
|
|
|
|
|
|
int lwm2m_socket_add(struct lwm2m_ctx *ctx)
|
2017-12-04 21:54:16 -08:00
|
|
|
{
|
2020-06-23 12:40:42 +02:00
|
|
|
if (sock_nfds >= MAX_POLL_FD) {
|
2019-01-28 11:55:23 -08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
2020-06-23 12:40:42 +02:00
|
|
|
sock_ctx[sock_nfds] = ctx;
|
|
|
|
sock_fds[sock_nfds].fd = ctx->sock_fd;
|
|
|
|
sock_fds[sock_nfds].events = POLLIN;
|
|
|
|
sock_nfds++;
|
|
|
|
|
2017-12-04 21:54:16 -08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
void lwm2m_socket_del(struct lwm2m_ctx *ctx)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2019-01-28 11:55:23 -08:00
|
|
|
for (int i = 0; i < sock_nfds; i++) {
|
2020-06-23 12:40:42 +02:00
|
|
|
if (sock_ctx[i] != ctx) {
|
|
|
|
continue;
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
2020-06-23 12:40:42 +02:00
|
|
|
|
|
|
|
sock_nfds--;
|
|
|
|
|
|
|
|
/* If not last, overwrite the entry with the last one. */
|
|
|
|
if (i < sock_nfds) {
|
|
|
|
sock_ctx[i] = sock_ctx[sock_nfds];
|
|
|
|
sock_fds[i].fd = sock_fds[sock_nfds].fd;
|
|
|
|
sock_fds[i].events = sock_fds[sock_nfds].events;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Remove the last entry. */
|
|
|
|
sock_ctx[sock_nfds] = NULL;
|
|
|
|
sock_fds[sock_nfds].fd = -1;
|
|
|
|
break;
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
|
|
|
}
|
2018-05-16 15:50:32 -07:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
static int64_t engine_observe_shedule_next_event(struct observe_node *obs, uint16_t srv_obj_inst,
|
|
|
|
const int64_t timestamp)
|
2021-05-17 16:23:51 +02:00
|
|
|
{
|
2022-02-23 23:43:52 -08:00
|
|
|
struct notification_attrs attrs;
|
|
|
|
int64_t t_s = 0;
|
|
|
|
int ret;
|
2021-05-17 16:23:51 +02:00
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
ret = engine_observe_attribute_list_get(&obs->path_list, &attrs, srv_obj_inst);
|
2022-02-23 23:43:52 -08:00
|
|
|
if (ret < 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
2021-05-17 16:23:51 +02:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
if (attrs.pmax) {
|
|
|
|
t_s = timestamp + MSEC_PER_SEC * attrs.pmax;
|
|
|
|
}
|
2021-05-17 16:23:51 +02:00
|
|
|
|
2022-02-23 23:43:52 -08:00
|
|
|
return t_s;
|
2021-05-17 16:23:51 +02:00
|
|
|
}
|
2018-05-16 15:50:32 -07:00
|
|
|
|
2021-05-25 22:55:59 +02:00
|
|
|
static void check_notifications(struct lwm2m_ctx *ctx,
|
|
|
|
const int64_t timestamp)
|
2021-05-17 16:23:51 +02:00
|
|
|
{
|
|
|
|
struct observe_node *obs;
|
|
|
|
int rc;
|
|
|
|
|
2021-05-25 22:55:59 +02:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
|
2022-02-23 23:43:52 -08:00
|
|
|
if (!obs->event_timestamp || timestamp < obs->event_timestamp) {
|
2021-05-17 16:23:51 +02:00
|
|
|
continue;
|
|
|
|
}
|
2022-02-23 23:43:52 -08:00
|
|
|
rc = generate_notify_message(ctx, obs, NULL);
|
2021-05-17 16:23:51 +02:00
|
|
|
if (rc == -ENOMEM) {
|
|
|
|
/* no memory/messages available, retry later */
|
|
|
|
return;
|
|
|
|
}
|
2022-02-23 23:43:52 -08:00
|
|
|
obs->event_timestamp =
|
|
|
|
engine_observe_shedule_next_event(obs, ctx->srv_obj_inst, timestamp);
|
2021-05-17 16:23:51 +02:00
|
|
|
obs->last_timestamp = timestamp;
|
|
|
|
if (!rc) {
|
|
|
|
/* create at most one notification */
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int socket_recv_message(struct lwm2m_ctx *client_ctx)
|
2019-01-28 11:55:23 -08:00
|
|
|
{
|
2020-05-27 11:26:57 -05:00
|
|
|
static uint8_t in_buf[NET_IPV6_MTU];
|
2019-01-28 11:55:23 -08:00
|
|
|
socklen_t from_addr_len;
|
|
|
|
ssize_t len;
|
2021-05-17 16:23:51 +02:00
|
|
|
static struct sockaddr from_addr;
|
|
|
|
|
|
|
|
from_addr_len = sizeof(from_addr);
|
|
|
|
len = recvfrom(client_ctx->sock_fd, in_buf, sizeof(in_buf) - 1,
|
|
|
|
0, &from_addr, &from_addr_len);
|
|
|
|
|
|
|
|
if (len < 0) {
|
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_ERR("Error reading response: %d", errno);
|
|
|
|
if (client_ctx->fault_cb != NULL) {
|
|
|
|
client_ctx->fault_cb(errno);
|
|
|
|
}
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (len == 0) {
|
|
|
|
LOG_ERR("Zero length recv");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
in_buf[len] = 0U;
|
|
|
|
lwm2m_udp_receive(client_ctx, in_buf, len, &from_addr, handle_request);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int socket_send_message(struct lwm2m_ctx *client_ctx)
|
|
|
|
{
|
|
|
|
sys_snode_t *msg_node = sys_slist_get(&client_ctx->pending_sends);
|
|
|
|
struct lwm2m_message *msg;
|
|
|
|
|
|
|
|
if (!msg_node) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
msg = SYS_SLIST_CONTAINER(msg_node, msg, node);
|
|
|
|
return lwm2m_send_message(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void socket_reset_pollfd_events(void)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < sock_nfds; ++i) {
|
|
|
|
sock_fds[i].events = POLLIN
|
|
|
|
| (sys_slist_is_empty(&sock_ctx[i]->pending_sends) ? 0 : POLLOUT);
|
|
|
|
sock_fds[i].revents = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* LwM2M main work loop */
|
|
|
|
static void socket_loop(void)
|
|
|
|
{
|
|
|
|
int i, rc;
|
|
|
|
int64_t timestamp;
|
|
|
|
int32_t timeout, next_retransmit;
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
while (1) {
|
2021-05-17 16:23:51 +02:00
|
|
|
timestamp = k_uptime_get();
|
|
|
|
timeout = lwm2m_engine_service(timestamp);
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/* wait for sockets */
|
|
|
|
if (sock_nfds < 1) {
|
2021-05-17 16:23:51 +02:00
|
|
|
k_msleep(timeout);
|
2019-01-28 11:55:23 -08:00
|
|
|
continue;
|
|
|
|
}
|
2017-08-28 14:39:50 -07:00
|
|
|
|
2021-05-25 22:55:59 +02:00
|
|
|
for (i = 0; i < sock_nfds; ++i) {
|
|
|
|
if (sys_slist_is_empty(&sock_ctx[i]->pending_sends)) {
|
|
|
|
next_retransmit = retransmit_request(sock_ctx[i], timestamp);
|
|
|
|
if (next_retransmit < timeout) {
|
|
|
|
timeout = next_retransmit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sys_slist_is_empty(&sock_ctx[i]->pending_sends)) {
|
|
|
|
check_notifications(sock_ctx[i], timestamp);
|
|
|
|
}
|
2021-05-17 16:23:51 +02:00
|
|
|
}
|
2021-05-25 22:55:59 +02:00
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
socket_reset_pollfd_events();
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/*
|
|
|
|
* FIXME: Currently we timeout and restart poll in case fds
|
|
|
|
* were modified.
|
|
|
|
*/
|
2021-05-17 16:23:51 +02:00
|
|
|
rc = poll(sock_fds, sock_nfds, timeout);
|
|
|
|
if (rc < 0) {
|
2019-01-28 11:55:23 -08:00
|
|
|
LOG_ERR("Error in poll:%d", errno);
|
|
|
|
errno = 0;
|
2020-04-24 15:32:39 +02:00
|
|
|
k_msleep(ENGINE_UPDATE_INTERVAL_MS);
|
2019-01-28 11:55:23 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sock_nfds; i++) {
|
2020-04-29 14:07:48 +02:00
|
|
|
if ((sock_fds[i].revents & POLLERR) ||
|
|
|
|
(sock_fds[i].revents & POLLNVAL) ||
|
|
|
|
(sock_fds[i].revents & POLLHUP)) {
|
|
|
|
LOG_ERR("Poll reported a socket error, %02x.",
|
|
|
|
sock_fds[i].revents);
|
2020-11-03 14:04:44 +01:00
|
|
|
if (sock_ctx[i] != NULL &&
|
|
|
|
sock_ctx[i]->fault_cb != NULL) {
|
|
|
|
sock_ctx[i]->fault_cb(EIO);
|
|
|
|
}
|
2019-01-28 11:55:23 -08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
if (sock_fds[i].revents & POLLIN) {
|
|
|
|
while (sock_ctx[i]) {
|
|
|
|
rc = socket_recv_message(sock_ctx[i]);
|
|
|
|
if (rc) {
|
|
|
|
break;
|
|
|
|
}
|
2020-11-03 14:04:44 +01:00
|
|
|
}
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
if (sock_fds[i].revents & POLLOUT) {
|
|
|
|
socket_send_message(sock_ctx[i]);
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
2019-01-25 21:43:52 -08:00
|
|
|
}
|
2017-12-04 21:54:16 -08:00
|
|
|
}
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
2017-12-04 21:54:16 -08:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
2020-05-27 11:26:57 -05:00
|
|
|
static int load_tls_credential(struct lwm2m_ctx *client_ctx, uint16_t res_id,
|
2019-01-28 11:55:23 -08:00
|
|
|
enum tls_credential_type type)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
void *cred = NULL;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t cred_len;
|
|
|
|
uint8_t cred_flags;
|
2019-01-28 11:55:23 -08:00
|
|
|
char pathstr[MAX_RESOURCE_LEN];
|
|
|
|
|
|
|
|
/* ignore error value */
|
|
|
|
tls_credential_delete(client_ctx->tls_tag, type);
|
|
|
|
|
|
|
|
snprintk(pathstr, sizeof(pathstr), "0/%d/%u", client_ctx->sec_obj_inst,
|
|
|
|
res_id);
|
|
|
|
|
|
|
|
ret = lwm2m_engine_get_res_data(pathstr, &cred, &cred_len, &cred_flags);
|
2017-08-28 14:39:50 -07:00
|
|
|
if (ret < 0) {
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_ERR("Unable to get resource data for '%s'",
|
|
|
|
log_strdup(pathstr));
|
2019-01-28 11:55:23 -08:00
|
|
|
return ret;
|
2017-08-28 14:39:50 -07:00
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
ret = tls_credential_add(client_ctx->tls_tag, type, cred, cred_len);
|
|
|
|
if (ret < 0) {
|
2019-07-08 10:49:30 -07:00
|
|
|
LOG_ERR("Error setting cred tag %d type %d: Error %d",
|
|
|
|
client_ctx->tls_tag, type, ret);
|
2019-01-25 14:39:41 -08:00
|
|
|
}
|
2017-08-28 14:39:50 -07:00
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
return ret;
|
|
|
|
}
|
2019-01-28 11:55:23 -08:00
|
|
|
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
|
2017-07-07 11:04:03 -07:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
int lwm2m_socket_start(struct lwm2m_ctx *client_ctx)
|
2019-01-25 21:43:52 -08:00
|
|
|
{
|
2021-05-17 16:23:51 +02:00
|
|
|
int flags;
|
2019-01-28 11:55:23 -08:00
|
|
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
|
|
|
int ret;
|
2021-11-04 16:07:54 -03:00
|
|
|
uint8_t tmp;
|
2019-01-25 21:43:52 -08:00
|
|
|
|
2019-07-08 11:04:15 -07:00
|
|
|
if (client_ctx->load_credentials) {
|
|
|
|
ret = client_ctx->load_credentials(client_ctx);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ret = load_tls_credential(client_ctx, 3, TLS_CREDENTIAL_PSK_ID);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2019-01-25 21:43:52 -08:00
|
|
|
|
2019-07-08 11:04:15 -07:00
|
|
|
ret = load_tls_credential(client_ctx, 5, TLS_CREDENTIAL_PSK);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
2019-01-25 21:43:52 -08:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
if (client_ctx->use_dtls) {
|
|
|
|
client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
|
|
|
|
SOCK_DGRAM, IPPROTO_DTLS_1_2);
|
|
|
|
} else
|
|
|
|
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
|
|
|
|
{
|
|
|
|
client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
|
|
|
|
SOCK_DGRAM, IPPROTO_UDP);
|
2019-01-25 21:43:52 -08:00
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
if (client_ctx->sock_fd < 0) {
|
|
|
|
LOG_ERR("Failed to create socket: %d", errno);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
|
|
|
if (client_ctx->use_dtls) {
|
|
|
|
sec_tag_t tls_tag_list[] = {
|
|
|
|
client_ctx->tls_tag,
|
|
|
|
};
|
|
|
|
|
|
|
|
ret = setsockopt(client_ctx->sock_fd, SOL_TLS, TLS_SEC_TAG_LIST,
|
|
|
|
tls_tag_list, sizeof(tls_tag_list));
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to set TLS_SEC_TAG_LIST option: %d",
|
|
|
|
errno);
|
|
|
|
lwm2m_engine_context_close(client_ctx);
|
|
|
|
return -errno;
|
|
|
|
}
|
2021-11-04 16:07:54 -03:00
|
|
|
|
|
|
|
if (client_ctx->desthostname != NULL) {
|
|
|
|
/** store character at len position */
|
|
|
|
tmp = client_ctx->desthostname[client_ctx->desthostnamelen];
|
|
|
|
|
|
|
|
/** change it to '\0' to pass to socket*/
|
|
|
|
client_ctx->desthostname[client_ctx->desthostnamelen] = '\0';
|
|
|
|
|
|
|
|
/** mbedtls ignores length */
|
|
|
|
ret = setsockopt(client_ctx->sock_fd, SOL_TLS, TLS_HOSTNAME,
|
|
|
|
client_ctx->desthostname, client_ctx->desthostnamelen);
|
|
|
|
|
|
|
|
/** restore character */
|
|
|
|
client_ctx->desthostname[client_ctx->desthostnamelen] = tmp;
|
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Failed to set TLS_HOSTNAME option: %d", errno);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
}
|
2019-01-25 21:43:52 -08:00
|
|
|
}
|
2019-01-28 11:55:23 -08:00
|
|
|
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
|
2019-01-25 21:43:52 -08:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
if (connect(client_ctx->sock_fd, &client_ctx->remote_addr,
|
|
|
|
NET_SOCKADDR_MAX_SIZE) < 0) {
|
|
|
|
LOG_ERR("Cannot connect UDP (-%d)", errno);
|
|
|
|
lwm2m_engine_context_close(client_ctx);
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
flags = fcntl(client_ctx->sock_fd, F_GETFL, 0);
|
|
|
|
if (flags == -1) {
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
fcntl(client_ctx->sock_fd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
2020-02-23 20:07:59 +02:00
|
|
|
return lwm2m_socket_add(client_ctx);
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
|
|
|
|
2021-11-04 16:07:54 -03:00
|
|
|
int lwm2m_parse_peerinfo(char *url, struct lwm2m_ctx *client_ctx, bool is_firmware_uri)
|
2019-01-28 11:55:23 -08:00
|
|
|
{
|
|
|
|
struct http_parser_url parser;
|
2019-10-15 09:56:03 +02:00
|
|
|
#if defined(CONFIG_LWM2M_DNS_SUPPORT)
|
2019-10-15 10:44:15 +02:00
|
|
|
struct addrinfo *res, hints = { 0 };
|
2019-01-31 09:21:51 -08:00
|
|
|
#endif
|
2019-01-28 11:55:23 -08:00
|
|
|
int ret;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t off, len;
|
|
|
|
uint8_t tmp;
|
2019-01-28 11:55:23 -08:00
|
|
|
|
2019-08-29 08:55:10 +09:00
|
|
|
LOG_DBG("Parse url: %s", log_strdup(url));
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
http_parser_url_init(&parser);
|
|
|
|
ret = http_parser_parse_url(url, strlen(url), 0, &parser);
|
|
|
|
if (ret < 0) {
|
2019-05-29 16:49:18 -07:00
|
|
|
LOG_ERR("Invalid url: %s", log_strdup(url));
|
2019-01-28 11:55:23 -08:00
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
off = parser.field_data[UF_SCHEMA].off;
|
|
|
|
len = parser.field_data[UF_SCHEMA].len;
|
|
|
|
|
|
|
|
/* check for supported protocol */
|
|
|
|
if (strncmp(url + off, "coaps", len) != 0) {
|
2019-01-25 21:43:52 -08:00
|
|
|
return -EPROTONOSUPPORT;
|
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/* check for DTLS requirement */
|
2021-11-04 16:07:54 -03:00
|
|
|
client_ctx->use_dtls = false;
|
2019-03-26 19:57:45 -06:00
|
|
|
if (len == 5U && strncmp(url + off, "coaps", len) == 0) {
|
2019-01-25 21:43:52 -08:00
|
|
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
2021-11-04 16:07:54 -03:00
|
|
|
client_ctx->use_dtls = true;
|
2019-01-25 21:43:52 -08:00
|
|
|
#else
|
|
|
|
return -EPROTONOSUPPORT;
|
|
|
|
#endif /* CONFIG_LWM2M_DTLS_SUPPORT */
|
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
if (!(parser.field_set & (1 << UF_PORT))) {
|
2021-11-04 16:07:54 -03:00
|
|
|
if (is_firmware_uri && client_ctx->use_dtls) {
|
2021-09-23 13:00:46 +02:00
|
|
|
/* Set to default coaps firmware update port */
|
|
|
|
parser.port = CONFIG_LWM2M_FIRMWARE_PORT_SECURE;
|
|
|
|
} else if (is_firmware_uri) {
|
|
|
|
/* Set to default coap firmware update port */
|
|
|
|
parser.port = CONFIG_LWM2M_FIRMWARE_PORT_NONSECURE;
|
|
|
|
} else {
|
|
|
|
/* Set to default LwM2M server port */
|
|
|
|
parser.port = CONFIG_LWM2M_PEER_PORT;
|
|
|
|
}
|
2019-01-25 21:43:52 -08:00
|
|
|
}
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
off = parser.field_data[UF_HOST].off;
|
|
|
|
len = parser.field_data[UF_HOST].len;
|
2019-01-25 21:43:52 -08:00
|
|
|
|
2021-11-04 16:07:54 -03:00
|
|
|
#if defined(CONFIG_LWM2M_DTLS_SUPPORT)
|
|
|
|
/** copy url pointer to be used in socket */
|
|
|
|
client_ctx->desthostname = url + off;
|
|
|
|
client_ctx->desthostnamelen = len;
|
|
|
|
#endif
|
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/* truncate host portion */
|
|
|
|
tmp = url[off + len];
|
|
|
|
url[off + len] = '\0';
|
|
|
|
|
2021-11-04 16:07:54 -03:00
|
|
|
/* initialize remote_addr */
|
|
|
|
(void)memset(&client_ctx->remote_addr, 0, sizeof(client_ctx->remote_addr));
|
2019-01-25 21:43:52 -08:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/* try and set IP address directly */
|
2021-11-04 16:07:54 -03:00
|
|
|
client_ctx->remote_addr.sa_family = AF_INET6;
|
2019-01-28 11:55:23 -08:00
|
|
|
ret = net_addr_pton(AF_INET6, url + off,
|
2021-11-04 16:07:54 -03:00
|
|
|
&((struct sockaddr_in6 *)&client_ctx->remote_addr)->sin6_addr);
|
2019-07-08 10:35:32 -07:00
|
|
|
/* Try to parse again using AF_INET */
|
2019-01-28 11:55:23 -08:00
|
|
|
if (ret < 0) {
|
2021-11-04 16:07:54 -03:00
|
|
|
client_ctx->remote_addr.sa_family = AF_INET;
|
2019-01-28 11:55:23 -08:00
|
|
|
ret = net_addr_pton(AF_INET, url + off,
|
2021-11-04 16:07:54 -03:00
|
|
|
&((struct sockaddr_in *)&client_ctx->remote_addr)->sin_addr);
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ret < 0) {
|
2019-10-15 09:56:03 +02:00
|
|
|
#if defined(CONFIG_LWM2M_DNS_SUPPORT)
|
2019-01-31 09:21:51 -08:00
|
|
|
#if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
#elif defined(CONFIG_NET_IPV6)
|
|
|
|
hints.ai_family = AF_INET6;
|
2019-07-08 10:35:32 -07:00
|
|
|
#elif defined(CONFIG_NET_IPV4)
|
2019-01-31 09:21:51 -08:00
|
|
|
hints.ai_family = AF_INET;
|
2019-07-08 10:35:32 -07:00
|
|
|
#else
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
2019-01-31 09:21:51 -08:00
|
|
|
#endif /* defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) */
|
|
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
ret = getaddrinfo(url + off, NULL, &hints, &res);
|
|
|
|
if (ret != 0) {
|
|
|
|
LOG_ERR("Unable to resolve address");
|
|
|
|
/* DNS error codes don't align with normal errors */
|
|
|
|
ret = -ENOENT;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2021-11-04 16:07:54 -03:00
|
|
|
memcpy(&client_ctx->remote_addr, res->ai_addr, sizeof(client_ctx->remote_addr));
|
|
|
|
client_ctx->remote_addr.sa_family = res->ai_family;
|
2019-08-29 08:55:10 +09:00
|
|
|
freeaddrinfo(res);
|
2019-01-31 09:21:51 -08:00
|
|
|
#else
|
2019-01-28 11:55:23 -08:00
|
|
|
goto cleanup;
|
2019-10-15 09:56:03 +02:00
|
|
|
#endif /* CONFIG_LWM2M_DNS_SUPPORT */
|
2019-01-28 11:55:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set port */
|
2021-11-04 16:07:54 -03:00
|
|
|
if (client_ctx->remote_addr.sa_family == AF_INET6) {
|
|
|
|
net_sin6(&client_ctx->remote_addr)->sin6_port = htons(parser.port);
|
|
|
|
} else if (client_ctx->remote_addr.sa_family == AF_INET) {
|
|
|
|
net_sin(&client_ctx->remote_addr)->sin_port = htons(parser.port);
|
2019-01-28 11:55:23 -08:00
|
|
|
} else {
|
|
|
|
ret = -EPROTONOSUPPORT;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
/* restore host separator */
|
|
|
|
url[off + len] = tmp;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_engine_start(struct lwm2m_ctx *client_ctx)
|
|
|
|
{
|
|
|
|
char pathstr[MAX_RESOURCE_LEN];
|
|
|
|
char *url;
|
2020-05-27 11:26:57 -05:00
|
|
|
uint16_t url_len;
|
|
|
|
uint8_t url_data_flags;
|
2019-01-28 11:55:23 -08:00
|
|
|
int ret = 0U;
|
|
|
|
|
|
|
|
/* get the server URL */
|
|
|
|
snprintk(pathstr, sizeof(pathstr), "0/%d/0", client_ctx->sec_obj_inst);
|
|
|
|
ret = lwm2m_engine_get_res_data(pathstr, (void **)&url, &url_len,
|
|
|
|
&url_data_flags);
|
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
url[url_len] = '\0';
|
2021-11-04 16:07:54 -03:00
|
|
|
ret = lwm2m_parse_peerinfo(url, client_ctx, false);
|
2019-01-28 11:55:23 -08:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwm2m_engine_context_init(client_ctx);
|
|
|
|
return lwm2m_socket_start(client_ctx);
|
2019-01-25 21:43:52 -08:00
|
|
|
}
|
|
|
|
|
2020-04-30 20:33:38 +02:00
|
|
|
static int lwm2m_engine_init(const struct device *dev)
|
2017-07-07 11:04:03 -07:00
|
|
|
{
|
2022-02-28 07:42:53 -08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < LWM2M_ENGINE_MAX_OBSERVER_PATH; i++) {
|
|
|
|
sys_slist_append(&obs_obj_path_list, &observe_paths[i].node);
|
|
|
|
}
|
|
|
|
|
2020-09-16 15:09:30 +02:00
|
|
|
(void)memset(block1_contexts, 0, sizeof(block1_contexts));
|
2017-07-25 16:54:25 +08:00
|
|
|
|
2019-01-28 11:55:23 -08:00
|
|
|
/* start sock receive thread */
|
2021-05-17 16:23:51 +02:00
|
|
|
k_thread_create(&engine_thread_data, &engine_thread_stack[0],
|
2020-07-31 12:45:25 -07:00
|
|
|
K_KERNEL_STACK_SIZEOF(engine_thread_stack),
|
2021-05-17 16:23:51 +02:00
|
|
|
(k_thread_entry_t)socket_loop, NULL, NULL, NULL,
|
|
|
|
THREAD_PRIORITY, 0, K_NO_WAIT);
|
2019-01-28 11:55:23 -08:00
|
|
|
k_thread_name_set(&engine_thread_data, "lwm2m-sock-recv");
|
|
|
|
LOG_DBG("LWM2M engine socket receive thread started");
|
|
|
|
|
2021-05-17 16:23:51 +02:00
|
|
|
return 0;
|
2017-07-07 11:04:03 -07:00
|
|
|
}
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
static struct lwm2m_obj_path_list *lwm2m_engine_get_from_list(sys_slist_t *path_list)
|
|
|
|
{
|
|
|
|
sys_snode_t *path_node = sys_slist_get(path_list);
|
|
|
|
struct lwm2m_obj_path_list *entry;
|
|
|
|
|
|
|
|
if (!path_node) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry = SYS_SLIST_CONTAINER(path_node, entry, node);
|
|
|
|
if (entry) {
|
|
|
|
memset(entry, 0, sizeof(struct lwm2m_obj_path_list));
|
|
|
|
}
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void lwm2m_engine_free_list(sys_slist_t *path_list, sys_slist_t *free_list)
|
|
|
|
{
|
|
|
|
sys_snode_t *node;
|
|
|
|
|
|
|
|
while (NULL != (node = sys_slist_get(path_list))) {
|
|
|
|
/* Add to free list */
|
|
|
|
sys_slist_append(free_list, node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool lwm2m_path_object_compare(struct lwm2m_obj_path *path,
|
|
|
|
struct lwm2m_obj_path *compare_path)
|
|
|
|
{
|
|
|
|
if (path->level != compare_path->level || path->obj_id != compare_path->obj_id ||
|
|
|
|
path->obj_inst_id != compare_path->obj_inst_id ||
|
|
|
|
path->res_id != compare_path->res_id ||
|
|
|
|
path->res_inst_id != compare_path->res_inst_id) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-02-28 07:42:53 -08:00
|
|
|
static struct lwm2m_obj_path *lwm2m_read_first_path_ptr(sys_slist_t *lwm2m_path_list)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *entry;
|
|
|
|
|
|
|
|
entry = (struct lwm2m_obj_path_list *)sys_slist_peek_head(lwm2m_path_list);
|
|
|
|
return &entry->path;
|
|
|
|
}
|
|
|
|
|
2022-01-12 23:28:16 -08:00
|
|
|
void lwm2m_engine_path_list_init(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list,
|
|
|
|
struct lwm2m_obj_path_list path_object_buf[],
|
|
|
|
uint8_t path_object_size)
|
|
|
|
{
|
|
|
|
/* Init list */
|
|
|
|
sys_slist_init(lwm2m_path_list);
|
|
|
|
sys_slist_init(lwm2m_free_list);
|
|
|
|
|
|
|
|
/* Put buffer elements to free list */
|
|
|
|
for (int i = 0; i < path_object_size; i++) {
|
|
|
|
sys_slist_append(lwm2m_free_list, &path_object_buf[i].node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_engine_add_path_to_list(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list,
|
|
|
|
struct lwm2m_obj_path *path)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *prev = NULL;
|
|
|
|
struct lwm2m_obj_path_list *entry;
|
|
|
|
struct lwm2m_obj_path_list *new_entry;
|
|
|
|
bool add_before_current = false;
|
|
|
|
|
|
|
|
if (path->level == LWM2M_PATH_LEVEL_NONE) {
|
|
|
|
/* Clear the list if we are adding the root path which includes all */
|
|
|
|
lwm2m_engine_free_list(lwm2m_path_list, lwm2m_free_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check is it at list already here */
|
|
|
|
new_entry = lwm2m_engine_get_from_list(lwm2m_free_list);
|
|
|
|
if (!new_entry) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_entry->path = *path;
|
|
|
|
if (!sys_slist_is_empty(lwm2m_path_list)) {
|
|
|
|
|
|
|
|
/* Keep list Ordered by Object ID/ Object instance/ resource ID */
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
|
|
|
|
if (entry->path.level == LWM2M_PATH_LEVEL_NONE ||
|
|
|
|
lwm2m_path_object_compare(&entry->path, &new_entry->path)) {
|
|
|
|
/* Already Root request at list or current path is at list */
|
|
|
|
sys_slist_append(lwm2m_free_list, &new_entry->node);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry->path.obj_id > path->obj_id) {
|
|
|
|
/* New entry have smaller Object ID */
|
|
|
|
add_before_current = true;
|
|
|
|
} else if (entry->path.obj_id == path->obj_id &&
|
|
|
|
entry->path.level > path->level) {
|
|
|
|
add_before_current = true;
|
|
|
|
} else if (entry->path.obj_id == path->obj_id &&
|
|
|
|
entry->path.level == path->level) {
|
|
|
|
if (path->level >= LWM2M_PATH_LEVEL_OBJECT_INST &&
|
|
|
|
entry->path.obj_inst_id > path->obj_inst_id) {
|
|
|
|
/*
|
|
|
|
* New have same Object ID
|
|
|
|
* but smaller Object Instance ID
|
|
|
|
*/
|
|
|
|
add_before_current = true;
|
|
|
|
} else if (path->level >= LWM2M_PATH_LEVEL_RESOURCE &&
|
|
|
|
entry->path.obj_inst_id == path->obj_inst_id &&
|
|
|
|
entry->path.res_id > path->res_id) {
|
|
|
|
/*
|
2022-03-16 21:07:43 +00:00
|
|
|
* Object ID and Object Instance id same
|
2022-01-12 23:28:16 -08:00
|
|
|
* but Resource ID is smaller
|
|
|
|
*/
|
|
|
|
add_before_current = true;
|
|
|
|
} else if (path->level >= LWM2M_PATH_LEVEL_RESOURCE_INST &&
|
|
|
|
entry->path.obj_inst_id == path->obj_inst_id &&
|
|
|
|
entry->path.res_id == path->res_id &&
|
|
|
|
entry->path.res_inst_id > path->res_inst_id) {
|
|
|
|
/*
|
2022-03-16 21:07:43 +00:00
|
|
|
* Object ID, Object Instance id & Resource ID same
|
2022-01-12 23:28:16 -08:00
|
|
|
* but Resource instance ID is smaller
|
|
|
|
*/
|
|
|
|
add_before_current = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (add_before_current) {
|
|
|
|
if (prev) {
|
|
|
|
sys_slist_insert(lwm2m_path_list, &prev->node,
|
|
|
|
&new_entry->node);
|
|
|
|
} else {
|
|
|
|
sys_slist_prepend(lwm2m_path_list, &new_entry->node);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
prev = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add First or new tail entry */
|
|
|
|
sys_slist_append(lwm2m_path_list, &new_entry->node);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwm2m_engine_clear_duplicate_path(sys_slist_t *lwm2m_path_list, sys_slist_t *lwm2m_free_list)
|
|
|
|
{
|
|
|
|
struct lwm2m_obj_path_list *prev = NULL;
|
|
|
|
struct lwm2m_obj_path_list *entry, *tmp;
|
|
|
|
bool remove_entry;
|
|
|
|
|
|
|
|
if (sys_slist_is_empty(lwm2m_path_list)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Keep list Ordered but remove if shorter path is similar */
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(lwm2m_path_list, entry, tmp, node) {
|
|
|
|
|
|
|
|
if (prev && prev->path.level < entry->path.level) {
|
|
|
|
if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT &&
|
|
|
|
entry->path.obj_id == prev->path.obj_id) {
|
|
|
|
remove_entry = true;
|
|
|
|
} else if (prev->path.level == LWM2M_PATH_LEVEL_OBJECT_INST &&
|
|
|
|
entry->path.obj_id == prev->path.obj_id &&
|
|
|
|
entry->path.obj_inst_id == prev->path.obj_inst_id) {
|
|
|
|
/* Remove current from the list */
|
|
|
|
remove_entry = true;
|
|
|
|
} else if (prev->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
|
|
|
|
entry->path.obj_id == prev->path.obj_id &&
|
|
|
|
entry->path.obj_inst_id == prev->path.obj_inst_id &&
|
|
|
|
entry->path.res_id == prev->path.res_id) {
|
|
|
|
/* Remove current from the list */
|
|
|
|
remove_entry = true;
|
|
|
|
} else {
|
|
|
|
remove_entry = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (remove_entry) {
|
|
|
|
/* Remove Current entry */
|
|
|
|
sys_slist_remove(lwm2m_path_list, &prev->node, &entry->node);
|
|
|
|
sys_slist_append(lwm2m_free_list, &entry->node);
|
|
|
|
} else {
|
|
|
|
prev = entry;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
prev = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-13 00:02:46 -08:00
|
|
|
|
|
|
|
static int lwm2m_perform_composite_read_root(struct lwm2m_message *msg, uint8_t *num_read)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
struct lwm2m_engine_obj *obj;
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst;
|
|
|
|
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
|
|
|
|
/* Security obj MUST NOT be part of registration message */
|
|
|
|
if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
msg->path.level = 1;
|
|
|
|
msg->path.obj_id = obj->obj_id;
|
|
|
|
|
|
|
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
|
|
|
|
|
|
|
if (!obj_inst) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lwm2m_perform_read_object_instance(msg, obj_inst, num_read);
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_perform_composite_read_op(struct lwm2m_message *msg, uint16_t content_format,
|
2022-02-28 07:42:53 -08:00
|
|
|
sys_slist_t *lwm2m_path_list)
|
2022-01-13 00:02:46 -08:00
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
|
|
|
struct lwm2m_obj_path_list *entry;
|
|
|
|
int ret = 0;
|
|
|
|
uint8_t num_read = 0U;
|
|
|
|
|
|
|
|
/* set output content-format */
|
|
|
|
ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT, content_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) {
|
|
|
|
LOG_ERR("Error appending payload marker: %d", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add object start mark */
|
|
|
|
engine_put_begin(&msg->out, &msg->path);
|
|
|
|
|
|
|
|
/* Read resource from path */
|
2022-02-28 07:42:53 -08:00
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
|
2022-01-13 00:02:46 -08:00
|
|
|
/* Copy path to message path */
|
|
|
|
memcpy(&msg->path, &entry->path, sizeof(struct lwm2m_obj_path));
|
|
|
|
|
|
|
|
if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
|
|
|
|
obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
|
|
|
|
} else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
|
|
|
|
/* find first obj_inst with path's obj_id */
|
|
|
|
obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
|
|
|
|
} else {
|
2022-03-16 21:07:43 +00:00
|
|
|
/* Read root Path */
|
2022-01-13 00:02:46 -08:00
|
|
|
ret = lwm2m_perform_composite_read_root(msg, &num_read);
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
LOG_ERR("Supported message size is too small for read root");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!obj_inst) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
|
|
|
|
if (ret == -ENOMEM) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* did not read anything even if we should have - on single item */
|
|
|
|
if (num_read == 0U) {
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add object end mark */
|
|
|
|
if (engine_put_end(&msg->out, &msg->path) < 0) {
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_send_op(struct lwm2m_message *msg, uint16_t content_format,
|
2022-02-28 07:42:53 -08:00
|
|
|
sys_slist_t *lwm2m_path_list)
|
2022-01-13 00:02:46 -08:00
|
|
|
{
|
|
|
|
switch (content_format) {
|
|
|
|
#if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
|
|
|
|
case LWM2M_FORMAT_APP_SEML_JSON:
|
2022-02-28 07:42:53 -08:00
|
|
|
return do_send_op_senml_json(msg, lwm2m_path_list);
|
2022-01-13 00:02:46 -08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
default:
|
|
|
|
LOG_ERR("Unsupported content-format for /dp: %u", content_format);
|
|
|
|
return -ENOMSG;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int do_send_reply_cb(const struct coap_packet *response,
|
|
|
|
struct coap_reply *reply,
|
|
|
|
const struct sockaddr *from)
|
|
|
|
{
|
|
|
|
uint8_t code;
|
|
|
|
|
|
|
|
code = coap_header_get_code(response);
|
|
|
|
LOG_DBG("Send callback (code:%u.%u)",
|
|
|
|
COAP_RESPONSE_CODE_CLASS(code),
|
|
|
|
COAP_RESPONSE_CODE_DETAIL(code));
|
|
|
|
|
|
|
|
if (code == COAP_RESPONSE_CODE_CHANGED) {
|
|
|
|
LOG_INF("Send done!");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG_ERR("Failed with code %u.%u. Not Retrying.",
|
|
|
|
COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_send_timeout_cb(struct lwm2m_message *msg)
|
|
|
|
{
|
|
|
|
LOG_WRN("Send Timeout");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t path_list_size,
|
|
|
|
bool confirmation_request)
|
|
|
|
{
|
|
|
|
struct lwm2m_message *msg;
|
|
|
|
int ret;
|
|
|
|
uint16_t content_format;
|
|
|
|
|
|
|
|
/* Path list buffer */
|
|
|
|
struct lwm2m_obj_path temp;
|
|
|
|
struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
|
2022-02-28 07:42:53 -08:00
|
|
|
sys_slist_t lwm2m_path_list;
|
|
|
|
sys_slist_t lwm2m_path_free_list;
|
2022-01-13 00:02:46 -08:00
|
|
|
|
|
|
|
/* Init list */
|
2022-02-28 07:42:53 -08:00
|
|
|
lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
|
2022-01-13 00:02:46 -08:00
|
|
|
CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
|
|
|
|
|
|
|
|
if (path_list_size > CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE) {
|
|
|
|
return -E2BIG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Select content format use CBOR when it possible */
|
|
|
|
if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
|
|
|
|
content_format = LWM2M_FORMAT_APP_SEML_JSON;
|
|
|
|
} else {
|
|
|
|
LOG_WRN("SenML CBOR or JSON is not supported");
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse Path to internal used object path format */
|
|
|
|
for (int i = 0; i < path_list_size; i++) {
|
2022-03-07 04:42:06 -08:00
|
|
|
ret = lwm2m_string_to_path(path_list[i], &temp, '/');
|
2022-01-13 00:02:46 -08:00
|
|
|
if (ret < 0) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
/* Add to linked list */
|
2022-02-28 07:42:53 -08:00
|
|
|
if (lwm2m_engine_add_path_to_list(&lwm2m_path_list, &lwm2m_path_free_list, &temp)) {
|
2022-01-13 00:02:46 -08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Clear path which are part are part of recursive path /1 will include /1/0/1 */
|
2022-02-28 07:42:53 -08:00
|
|
|
lwm2m_engine_clear_duplicate_path(&lwm2m_path_list, &lwm2m_path_free_list);
|
2022-01-13 00:02:46 -08:00
|
|
|
|
|
|
|
/* Allocate Message buffer */
|
|
|
|
msg = lwm2m_get_message(ctx);
|
|
|
|
if (!msg) {
|
|
|
|
LOG_ERR("Unable to get a lwm2m message!");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (confirmation_request) {
|
|
|
|
msg->type = COAP_TYPE_CON;
|
|
|
|
msg->reply_cb = do_send_reply_cb;
|
|
|
|
msg->message_timeout_cb = do_send_timeout_cb;
|
|
|
|
} else {
|
|
|
|
msg->type = COAP_TYPE_NON_CON;
|
|
|
|
msg->reply_cb = NULL;
|
|
|
|
msg->message_timeout_cb = NULL;
|
|
|
|
}
|
|
|
|
msg->code = COAP_METHOD_POST;
|
|
|
|
msg->mid = coap_next_id();
|
|
|
|
msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
|
|
|
|
msg->out.out_cpkt = &msg->cpkt;
|
|
|
|
|
|
|
|
ret = lwm2m_init_message(msg);
|
|
|
|
if (ret) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ret = select_writer(&msg->out, content_format);
|
|
|
|
if (ret) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH,
|
|
|
|
LWM2M_DP_CLIENT_URI,
|
|
|
|
strlen(LWM2M_DP_CLIENT_URI));
|
|
|
|
if (ret < 0) {
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write requested path data */
|
2022-02-28 07:42:53 -08:00
|
|
|
ret = do_send_op(msg, content_format, &lwm2m_path_list);
|
2022-01-13 00:02:46 -08:00
|
|
|
if (ret < 0) {
|
|
|
|
LOG_ERR("Send (err:%d)", ret);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
LOG_INF("Send op to server (/dp)");
|
|
|
|
lwm2m_send_message_async(msg);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
cleanup:
|
|
|
|
lwm2m_reset_message(msg, true);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-07-07 11:04:03 -07:00
|
|
|
SYS_INIT(lwm2m_engine_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|