zephyr/subsys/net/lib/lwm2m/lwm2m_engine.c

2558 lines
58 KiB
C
Raw Normal View History

net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
/*
* Copyright (c) 2017 Linaro Limited
*
* 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>
*/
/*
* TODO:
*
* - Use server / security object instance 0 for initial connection
* - Add DNS support for security uri parsing
* - Block-transfer support / Large response messages
* (use Block2 to limit message size to 64 bytes for 6LOWPAN compat.)
* - BOOTSTRAP/DTLS cleanup
* - Handle WRITE_ATTRIBUTES (pmin=10&pmax=60)
* - Handle Resource ObjLink type
*/
#define SYS_LOG_DOMAIN "lib/lwm2m_engine"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <zephyr/types.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <init.h>
#include <misc/printk.h>
#include <net/net_ip.h>
#include <net/net_pkt.h>
#include <net/udp.h>
#include <net/zoap.h>
#include <net/lwm2m.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
#include "lwm2m_rw_plain_text.h"
#include "lwm2m_rw_oma_tlv.h"
#ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
#include "lwm2m_rw_json.h"
#endif
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
#include "lwm2m_rd_client.h"
#endif
#define ENGINE_UPDATE_INTERVAL 500
#define DISCOVER_PREFACE "</.well-known/core>;ct=40"
/*
* TODO: to implement a way for clients to specify alternate path
* via Kconfig (LwM2M specification 8.2.2 Alternate Path)
*
* For now, in order to inform server we support JSON format, we have to
* report 'ct=11543' to the server. '</>' is required in order to append
* content attribute. And resource type attribute is appended because of
* Eclipse wakaama will reject the registration when 'rt="oma.lwm2m"' is
* missing.
*/
#define RESOURCE_TYPE ";rt=\"oma.lwm2m\""
#if defined(CONFIG_LWM2M_RW_JSON_SUPPORT)
#define REG_PREFACE "</>" RESOURCE_TYPE \
";ct=" STRINGIFY(LWM2M_FORMAT_OMA_JSON)
#else
#define REG_PREFACE ""
#endif
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
#define BUF_ALLOC_TIMEOUT K_SECONDS(1)
struct observe_node {
sys_snode_t node;
struct net_context *net_ctx;
struct sockaddr addr;
struct lwm2m_obj_path path;
u8_t token[8];
s64_t event_timestamp;
s64_t last_timestamp;
u32_t min_period_sec;
u32_t max_period_sec;
u32_t counter;
u16_t format;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
bool used;
u8_t tkl;
};
#define NUM_PENDINGS CONFIG_LWM2M_ENGINE_MAX_PENDING
#define NUM_REPLIES CONFIG_LWM2M_ENGINE_MAX_REPLIES
struct zoap_pending pendings[NUM_PENDINGS];
struct zoap_reply replies[NUM_REPLIES];
struct k_delayed_work retransmit_work;
static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER];
static sys_slist_t engine_obj_list;
static sys_slist_t engine_obj_inst_list;
static sys_slist_t engine_observer_list;
/* periodic / notify / observe handling stack */
static K_THREAD_STACK_DEFINE(engine_thread_stack,
CONFIG_LWM2M_ENGINE_STACK_SIZE);
static struct k_thread engine_thread_data;
/* for debugging: to print IP addresses */
char *lwm2m_sprint_ip_addr(const struct sockaddr *addr)
{
static char buf[NET_IPV6_ADDR_LEN];
#if defined(CONFIG_NET_IPV6)
if (addr->family == AF_INET6) {
return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr,
buf, sizeof(buf));
}
#endif
#if defined(CONFIG_NET_IPV4)
if (addr->family == AF_INET) {
return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr,
buf, sizeof(buf));
}
#endif
SYS_LOG_ERR("Unknown IP address family:%d", addr->family);
return NULL;
}
#if CONFIG_SYS_LOG_LWM2M_LEVEL > 3
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
static char *sprint_token(const u8_t *token, u8_t tkl)
{
int i;
static char buf[32];
int pos = 0;
for (i = 0; i < tkl; i++) {
pos += snprintf(&buf[pos], 31 - pos, "%x", token[i]);
}
buf[pos] = '\0';
return buf;
}
#endif
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
/* observer functions */
int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id)
{
struct observe_node *obs;
int ret = 0;
/* look for observers which match our resource */
SYS_SLIST_FOR_EACH_CONTAINER(&engine_observer_list, obs, node) {
if (obs->path.obj_id == obj_id &&
obs->path.obj_inst_id == obj_inst_id &&
(obs->path.level < 3 ||
obs->path.res_id == res_id)) {
/* update the event time for this observer */
obs->event_timestamp = k_uptime_get();
SYS_LOG_DBG("NOTIFY EVENT %u/%u/%u",
obj_id, obj_inst_id, res_id);
ret++;
}
}
return ret;
}
int lwm2m_notify_observer_path(struct lwm2m_obj_path *path)
{
return lwm2m_notify_observer(path->obj_id, path->obj_inst_id,
path->res_id);
}
static int engine_add_observer(struct net_context *net_ctx,
struct sockaddr *addr,
const u8_t *token, u8_t tkl,
struct lwm2m_obj_path *path,
u16_t format)
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
{
struct observe_node *obs;
int i;
if (!addr) {
SYS_LOG_ERR("sockaddr is required");
return -EINVAL;
}
/* make sure this observer doesn't exist already */
SYS_SLIST_FOR_EACH_CONTAINER(&engine_observer_list, obs, node) {
if (memcmp(&obs->addr, addr, sizeof(addr)) == 0 &&
memcmp(&obs->path, path, sizeof(*path)) == 0) {
/* quietly update the token information */
memcpy(obs->token, token, tkl);
obs->tkl = tkl;
SYS_LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]",
path->obj_id, path->obj_inst_id,
path->res_id, path->level,
lwm2m_sprint_ip_addr(addr));
return 0;
}
}
/* find an unused observer index node */
for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
if (!observe_node_data[i].used) {
break;
}
}
/* couldn't find an index */
if (i == CONFIG_LWM2M_ENGINE_MAX_OBSERVER) {
return -ENOMEM;
}
/* copy the values and add it to the list */
observe_node_data[i].used = true;
observe_node_data[i].net_ctx = net_ctx;
memcpy(&observe_node_data[i].addr, addr, sizeof(*addr));
memcpy(&observe_node_data[i].path, path, sizeof(*path));
memcpy(observe_node_data[i].token, token, tkl);
observe_node_data[i].tkl = tkl;
observe_node_data[i].last_timestamp = k_uptime_get();
observe_node_data[i].event_timestamp =
observe_node_data[i].last_timestamp;
/* TODO: use server object instance or WRITE_ATTR values */
observe_node_data[i].min_period_sec = 10;
observe_node_data[i].max_period_sec = 60;
observe_node_data[i].format = format;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
observe_node_data[i].counter = 1;
sys_slist_append(&engine_observer_list,
&observe_node_data[i].node);
SYS_LOG_DBG("OBSERVER ADDED %u/%u/%u(%u) token:'%s' addr:%s",
path->obj_id, path->obj_inst_id, path->res_id, path->level,
sprint_token(token, tkl), lwm2m_sprint_ip_addr(addr));
return 0;
}
static int engine_remove_observer(const u8_t *token, u8_t tkl)
{
struct observe_node *obs, *found_obj = NULL;
if (!token || tkl == 0) {
SYS_LOG_ERR("token(%p) and token length(%u) must be valid.",
token, tkl);
return -EINVAL;
}
/* find the node index */
SYS_SLIST_FOR_EACH_CONTAINER(&engine_observer_list, obs, node) {
if (memcmp(obs->token, token, tkl) == 0) {
found_obj = obs;
break;
}
}
if (!found_obj) {
return -ENOENT;
}
sys_slist_remove(&engine_observer_list, NULL, &found_obj->node);
SYS_LOG_DBG("observer '%s' removed", sprint_token(token, tkl));
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
return 0;
}
/* 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)
{
/* TODO: remove all observer instances */
sys_slist_remove(&engine_obj_list, NULL, &obj->node);
}
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)
{
sys_slist_remove(&engine_obj_inst_list, NULL, &obj_inst->node);
}
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 *
next_engine_obj_inst(struct lwm2m_engine_obj_inst *last,
int obj_id, int obj_inst_id)
{
while (last) {
last = SYS_SLIST_PEEK_NEXT_CONTAINER(last, node);
if (last && last->obj->obj_id == obj_id &&
last->obj_inst_id == obj_inst_id) {
return last;
}
}
return NULL;
}
int lwm2m_create_obj_inst(u16_t obj_id, u16_t obj_inst_id,
struct lwm2m_engine_obj_inst **obj_inst)
{
int i;
struct lwm2m_engine_obj *obj;
*obj_inst = NULL;
obj = get_engine_obj(obj_id);
if (!obj) {
SYS_LOG_ERR("unable to find obj: %u", obj_id);
return -ENOENT;
}
if (!obj->create_cb) {
SYS_LOG_ERR("obj %u has no create_cb", obj_id);
return -EINVAL;
}
if (obj->instance_count + 1 > obj->max_instance_count) {
SYS_LOG_ERR("no more instances available for obj %u", obj_id);
return -ENOMEM;
}
*obj_inst = obj->create_cb(obj_inst_id);
if (!*obj_inst) {
SYS_LOG_ERR("unable to create obj %u instance %u",
obj_id, obj_inst_id);
return -EINVAL;
}
obj->instance_count++;
(*obj_inst)->obj = obj;
(*obj_inst)->obj_inst_id = obj_inst_id;
sprintf((*obj_inst)->path, "%u/%u", obj_id, obj_inst_id);
for (i = 0; i < (*obj_inst)->resource_count; i++) {
sprintf((*obj_inst)->resources[i].path, "%u/%u/%u",
obj_id, obj_inst_id, (*obj_inst)->resources[i].res_id);
}
engine_register_obj_inst(*obj_inst);
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
engine_trigger_update();
#endif
return 0;
}
int lwm2m_delete_obj_inst(u16_t obj_id, u16_t obj_inst_id)
{
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;
}
engine_unregister_obj_inst(obj_inst);
obj->instance_count--;
if (obj->delete_cb) {
return obj->delete_cb(obj_inst_id);
}
return 0;
}
/* utility functions */
static void engine_clear_context(struct lwm2m_engine_context *context)
{
if (context->in) {
memset(context->in, 0, sizeof(struct lwm2m_input_context));
}
if (context->out) {
memset(context->out, 0, sizeof(struct lwm2m_output_context));
}
if (context->path) {
memset(context->path, 0, sizeof(struct lwm2m_obj_path));
}
context->operation = 0;
}
static u16_t atou16(u8_t *buf, u16_t buflen, u16_t *len)
{
u16_t val = 0;
u16_t pos = 0;
/* we should get a value first - consume all numbers */
while (pos < buflen && isdigit(buf[pos])) {
val = val * 10 + (buf[pos] - '0');
pos++;
}
*len = pos;
return val;
}
static void zoap_options_to_path(struct zoap_option *opt, int options_count,
struct lwm2m_obj_path *path)
{
u16_t len;
path->level = options_count;
path->obj_id = atou16(opt[0].value, opt[0].len, &len);
if (len == 0) {
path->level = 0;
}
if (path->level > 1) {
path->obj_inst_id = atou16(opt[1].value, opt[1].len, &len);
if (len == 0) {
path->level = 1;
}
}
if (path->level > 2) {
path->res_id = atou16(opt[2].value, opt[2].len, &len);
if (len == 0) {
path->level = 2;
}
}
if (path->level > 3) {
path->res_inst_id = atou16(opt[3].value, opt[3].len, &len);
if (len == 0) {
path->level = 3;
}
}
}
int lwm2m_init_message(struct net_context *net_ctx, struct zoap_packet *zpkt,
struct net_pkt **pkt, u8_t type, u8_t code, u16_t mid,
const u8_t *token, u8_t tkl)
{
struct net_buf *frag;
int r;
*pkt = net_pkt_get_tx(net_ctx, BUF_ALLOC_TIMEOUT);
if (!*pkt) {
SYS_LOG_ERR("Unable to get TX packet, not enough memory.");
return -ENOMEM;
}
frag = net_pkt_get_data(net_ctx, BUF_ALLOC_TIMEOUT);
if (!frag) {
SYS_LOG_ERR("Unable to get DATA buffer, not enough memory.");
net_pkt_unref(*pkt);
*pkt = NULL;
return -ENOMEM;
}
net_pkt_frag_add(*pkt, frag);
r = zoap_packet_init(zpkt, *pkt);
if (r < 0) {
SYS_LOG_ERR("zoap packet init error (err:%d)", r);
return r;
}
/* FIXME: Could be that zoap_packet_init() sets some defaults */
zoap_header_set_version(zpkt, 1);
zoap_header_set_type(zpkt, type);
zoap_header_set_code(zpkt, code);
if (mid > 0) {
zoap_header_set_id(zpkt, mid);
} else {
zoap_header_set_id(zpkt, zoap_next_id());
}
/* tkl == 0 is for a new TOKEN, tkl == -1 means dont set */
if (token && tkl > 0) {
zoap_header_set_token(zpkt, token, tkl);
} else if (tkl == 0) {
zoap_header_set_token(zpkt, zoap_next_token(), 8);
}
return 0;
}
struct zoap_pending *lwm2m_init_message_pending(struct zoap_packet *zpkt,
struct sockaddr *addr,
struct zoap_pending *zpendings,
int num_zpendings)
{
struct zoap_pending *pending = NULL;
int ret;
pending = zoap_pending_next_unused(zpendings, num_zpendings);
if (!pending) {
SYS_LOG_ERR("Unable to find a free pending to track "
"retransmissions.");
return NULL;
}
ret = zoap_pending_init(pending, zpkt, addr);
if (ret < 0) {
SYS_LOG_ERR("Unable to initialize a pending "
"retransmission (err:%d).", ret);
pending->pkt = NULL;
return NULL;
}
return pending;
}
void lwm2m_init_message_cleanup(struct net_pkt *pkt,
struct zoap_pending *pending,
struct zoap_reply *reply)
{
if (pending) {
zoap_pending_clear(pending);
/* don't unref attached pkt twice */
if (!pending->pkt) {
pkt = NULL;
}
}
if (reply) {
zoap_reply_clear(reply);
}
if (pkt) {
net_pkt_unref(pkt);
}
}
u16_t lwm2m_get_rd_data(u8_t *client_data, u16_t size)
{
struct lwm2m_engine_obj *obj;
struct lwm2m_engine_obj_inst *obj_inst;
u8_t temp[32];
u16_t pos = 0;
int len;
/* Add resource-type/content-type to the registration message */
memcpy(client_data, REG_PREFACE, sizeof(REG_PREFACE) - 1);
pos += sizeof(REG_PREFACE) - 1;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
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;
}
/* Only report <OBJ_ID> when no instance available */
if (obj->instance_count == 0) {
len = snprintf(temp, sizeof(temp), "%s</%u>",
(pos > 0) ? "," : "", obj->obj_id);
if (pos + len >= size) {
/* full buffer -- exit loop */
break;
}
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
memcpy(&client_data[pos], temp, len);
pos += len;
continue;
}
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
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) {
len = snprintf(temp, sizeof(temp),
"%s</%s>",
(pos > 0) ? "," : "",
obj_inst->path);
/*
* TODO: iterate through resources once block
* transfer is handled correctly
*/
if (pos + len >= size) {
/* full buffer -- exit loop */
break;
}
memcpy(&client_data[pos], temp, len);
pos += len;
}
}
}
client_data[pos] = '\0';
return pos;
}
/* input / output selection */
static u16_t select_writer(struct lwm2m_output_context *out, u16_t accept)
{
switch (accept) {
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
default:
SYS_LOG_ERR("Unknown Accept type %u, using LWM2M plain text",
accept);
out->writer = &plain_text_writer;
accept = LWM2M_FORMAT_PLAIN_TEXT;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
break;
}
return accept;
}
static u16_t select_reader(struct lwm2m_input_context *in, u16_t format)
{
switch (format) {
case LWM2M_FORMAT_PLAIN_TEXT:
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
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;
default:
SYS_LOG_ERR("Unknown content type %u, using LWM2M plain text",
format);
in->reader = &plain_text_reader;
format = LWM2M_FORMAT_PLAIN_TEXT;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
break;
}
return format;
}
/* user data setter functions */
static int string_to_path(char *pathstr, struct lwm2m_obj_path *path,
char delim)
{
u16_t value, len;
int i, tokstart = -1, toklen;
int end_index = strlen(pathstr) - 1;
for (i = 0; i <= end_index; i++) {
/* search for first numeric */
if (tokstart == -1) {
if (!isdigit(pathstr[i])) {
continue;
}
tokstart = i;
}
/* find delimiter char or end of string */
if (pathstr[i] == delim || i == end_index) {
toklen = i - tokstart + 1;
/* don't process delimiter char */
if (pathstr[i] == delim) {
toklen--;
}
if (toklen <= 0) {
continue;
}
value = atou16(&pathstr[tokstart], toklen, &len);
switch (path->level) {
case 0:
path->obj_id = value;
break;
case 1:
path->obj_inst_id = value;
break;
case 2:
path->res_id = value;
break;
case 3:
path->res_inst_id = value;
break;
default:
SYS_LOG_ERR("invalid level (%d)",
path->level);
return -EINVAL;
}
/* increase the path level for each token found */
path->level++;
tokstart = -1;
}
}
return 0;
}
int lwm2m_engine_create_obj_inst(char *pathstr)
{
struct lwm2m_obj_path path;
struct lwm2m_engine_obj_inst *obj_inst;
int ret = 0;
SYS_LOG_DBG("path:%s", pathstr);
/* translate path -> path_obj */
memset(&path, 0, sizeof(path));
ret = string_to_path(pathstr, &path, '/');
if (ret < 0) {
return ret;
}
if (path.level != 2) {
SYS_LOG_ERR("path must have 2 parts");
return -EINVAL;
}
return lwm2m_create_obj_inst(path.obj_id, path.obj_inst_id, &obj_inst);
}
static int lwm2m_engine_set(char *pathstr, void *value, u16_t len)
{
int ret = 0, i;
struct lwm2m_obj_path path;
struct lwm2m_engine_obj_inst *obj_inst;
struct lwm2m_engine_obj_field *obj_field;
struct lwm2m_engine_res_inst *res = NULL;
bool changed = false;
void *data_ptr = NULL;
size_t data_len = 0;
SYS_LOG_DBG("path:%s, value:%p, len:%d", pathstr, value, len);
/* translate path -> path_obj */
memset(&path, 0, sizeof(path));
ret = string_to_path(pathstr, &path, '/');
if (ret < 0) {
return ret;
}
if (path.level < 3) {
SYS_LOG_ERR("path must have 3 parts");
return -EINVAL;
}
/* find obj_inst/res_id */
obj_inst = get_engine_obj_inst(path.obj_id, path.obj_inst_id);
if (!obj_inst) {
SYS_LOG_ERR("obj instance %d/%d not found",
path.obj_id, path.obj_inst_id);
return -ENOENT;
}
if (!obj_inst->resources || obj_inst->resource_count == 0) {
SYS_LOG_ERR("obj instance has no resources");
return -EINVAL;
}
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj, path.res_id);
if (!obj_field) {
SYS_LOG_ERR("obj field %d not found", path.res_id);
return -ENOENT;
}
for (i = 0; i < obj_inst->resource_count; i++) {
if (obj_inst->resources[i].res_id == path.res_id) {
res = &obj_inst->resources[i];
break;
}
}
if (!res) {
SYS_LOG_ERR("res instance %d not found", path.res_id);
return -ENOENT;
}
/* setup initial data elements */
data_ptr = res->data_ptr;
data_len = res->data_len;
/* allow user to override data elements via callback */
if (res->pre_write_cb) {
data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, &data_len);
}
if (!data_ptr) {
SYS_LOG_ERR("res data pointer is NULL");
return -EINVAL;
}
/* check length (note: we add 1 to string length for NULL pad) */
if (len > res->data_len -
(obj_field->data_type == LWM2M_RES_TYPE_STRING ? 1 : 0)) {
SYS_LOG_ERR("length %u is too long for resource %d data",
len, path.res_id);
return -ENOMEM;
}
if (memcmp(data_ptr, value, len) != 0) {
changed = true;
}
switch (obj_field->data_type) {
case LWM2M_RES_TYPE_STRING:
memcpy((u8_t *)data_ptr, value, len);
((u8_t *)data_ptr)[len] = '\0';
break;
case LWM2M_RES_TYPE_U64:
*((u64_t *)data_ptr) = *(u64_t *)value;
break;
case LWM2M_RES_TYPE_U32:
case LWM2M_RES_TYPE_TIME:
*((u32_t *)data_ptr) = *(u32_t *)value;
break;
case LWM2M_RES_TYPE_U16:
*((u16_t *)data_ptr) = *(u16_t *)value;
break;
case LWM2M_RES_TYPE_U8:
*((u8_t *)data_ptr) = *(u8_t *)value;
break;
case LWM2M_RES_TYPE_S64:
*((s64_t *)data_ptr) = *(s64_t *)value;
break;
case LWM2M_RES_TYPE_S32:
*((s32_t *)data_ptr) = *(s32_t *)value;
break;
case LWM2M_RES_TYPE_S16:
*((s16_t *)data_ptr) = *(s16_t *)value;
break;
case LWM2M_RES_TYPE_S8:
*((s8_t *)data_ptr) = *(s8_t *)value;
break;
case LWM2M_RES_TYPE_BOOL:
*((bool *)data_ptr) = *(bool *)value;
break;
case LWM2M_RES_TYPE_FLOAT32:
((float32_value_t *)data_ptr)->val1 =
((float32_value_t *)value)->val1;
((float32_value_t *)data_ptr)->val2 =
((float32_value_t *)value)->val2;
break;
case LWM2M_RES_TYPE_FLOAT64:
((float64_value_t *)data_ptr)->val1 =
((float64_value_t *)value)->val1;
((float64_value_t *)data_ptr)->val2 =
((float64_value_t *)value)->val2;
break;
default:
SYS_LOG_ERR("unknown obj data_type %d",
obj_field->data_type);
return -EINVAL;
}
if (res->post_write_cb) {
/* ignore return value here */
res->post_write_cb(obj_inst->obj_inst_id, data_ptr, len,
false, 0);
}
if (changed) {
NOTIFY_OBSERVER_PATH(&path);
}
return ret;
}
int lwm2m_engine_set_string(char *pathstr, char *data_ptr)
{
return lwm2m_engine_set(pathstr, data_ptr, strlen(data_ptr));
}
int lwm2m_engine_set_u8(char *pathstr, u8_t value)
{
return lwm2m_engine_set(pathstr, &value, 1);
}
int lwm2m_engine_set_u16(char *pathstr, u16_t value)
{
return lwm2m_engine_set(pathstr, &value, 2);
}
int lwm2m_engine_set_u32(char *pathstr, u32_t value)
{
return lwm2m_engine_set(pathstr, &value, 4);
}
int lwm2m_engine_set_u64(char *pathstr, u64_t value)
{
return lwm2m_engine_set(pathstr, &value, 8);
}
int lwm2m_engine_set_s8(char *pathstr, s8_t value)
{
return lwm2m_engine_set(pathstr, &value, 1);
}
int lwm2m_engine_set_s16(char *pathstr, s16_t value)
{
return lwm2m_engine_set(pathstr, &value, 2);
}
int lwm2m_engine_set_s32(char *pathstr, s32_t value)
{
return lwm2m_engine_set(pathstr, &value, 4);
}
int lwm2m_engine_set_s64(char *pathstr, s64_t value)
{
return lwm2m_engine_set(pathstr, &value, 8);
}
int lwm2m_engine_set_bool(char *pathstr, bool value)
{
u8_t temp = (value != 0 ? 1 : 0);
return lwm2m_engine_set(pathstr, &temp, 1);
}
int lwm2m_engine_set_float32(char *pathstr, float32_value_t *value)
{
return lwm2m_engine_set(pathstr, value, sizeof(float32_value_t));
}
int lwm2m_engine_set_float64(char *pathstr, float64_value_t *value)
{
return lwm2m_engine_set(pathstr, value, sizeof(float64_value_t));
}
/* user data getter functions */
static int lwm2m_engine_get(char *pathstr, void *buf, u16_t buflen)
{
int ret = 0, i;
struct lwm2m_obj_path path;
struct lwm2m_engine_obj_inst *obj_inst;
struct lwm2m_engine_obj_field *obj_field;
struct lwm2m_engine_res_inst *res = NULL;
u16_t len = 0;
void *data_ptr = NULL;
size_t data_len = 0;
SYS_LOG_DBG("path:%s, buf:%p, buflen:%d", pathstr, buf, buflen);
/* translate path -> path_obj */
memset(&path, 0, sizeof(path));
ret = string_to_path(pathstr, &path, '/');
if (ret < 0) {
return ret;
}
if (path.level < 3) {
SYS_LOG_ERR("path must have 3 parts");
return -EINVAL;
}
/* find obj_inst/res_id */
obj_inst = get_engine_obj_inst(path.obj_id, path.obj_inst_id);
if (!obj_inst) {
SYS_LOG_ERR("obj instance %d/%d not found",
path.obj_id, path.obj_inst_id);
return -ENOENT;
}
if (!obj_inst->resources || obj_inst->resource_count == 0) {
SYS_LOG_ERR("obj instance has no resources");
return -EINVAL;
}
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj, path.res_id);
if (!obj_field) {
SYS_LOG_ERR("obj field %d not found", path.res_id);
return -ENOENT;
}
for (i = 0; i < obj_inst->resource_count; i++) {
if (obj_inst->resources[i].res_id == path.res_id) {
res = &obj_inst->resources[i];
break;
}
}
if (!res) {
SYS_LOG_ERR("res instance %d not found", path.res_id);
return -ENOENT;
}
/* setup initial data elements */
data_ptr = res->data_ptr;
data_len = res->data_len;
/* allow user to override data elements via callback */
if (res->read_cb) {
data_ptr = res->read_cb(obj_inst->obj_inst_id, &data_len);
}
/* TODO: handle data_len > buflen case */
if (data_ptr && data_len > 0) {
switch (obj_field->data_type) {
case LWM2M_RES_TYPE_OPAQUE:
break;
case LWM2M_RES_TYPE_STRING:
strncpy((u8_t *)buf, (u8_t *)data_ptr, buflen);
len = strlen(buf);
break;
case LWM2M_RES_TYPE_U64:
*(u64_t *)buf = *(u64_t *)data_ptr;
break;
case LWM2M_RES_TYPE_U32:
case LWM2M_RES_TYPE_TIME:
*(u32_t *)buf = *(u32_t *)data_ptr;
break;
case LWM2M_RES_TYPE_U16:
*(u16_t *)buf = *(u16_t *)data_ptr;
break;
case LWM2M_RES_TYPE_U8:
*(u8_t *)buf = *(u8_t *)data_ptr;
break;
case LWM2M_RES_TYPE_S64:
*(s64_t *)buf = *(s64_t *)data_ptr;
break;
case LWM2M_RES_TYPE_S32:
*(s32_t *)buf = *(s32_t *)data_ptr;
break;
case LWM2M_RES_TYPE_S16:
*(s16_t *)buf = *(s16_t *)data_ptr;
break;
case LWM2M_RES_TYPE_S8:
*(s8_t *)buf = *(s8_t *)data_ptr;
break;
case LWM2M_RES_TYPE_BOOL:
*(bool *)buf = *(bool *)data_ptr;
break;
case LWM2M_RES_TYPE_FLOAT32:
((float32_value_t *)buf)->val1 =
((float32_value_t *)data_ptr)->val1;
((float32_value_t *)buf)->val2 =
((float32_value_t *)data_ptr)->val2;
break;
case LWM2M_RES_TYPE_FLOAT64:
((float64_value_t *)buf)->val1 =
((float64_value_t *)data_ptr)->val1;
((float64_value_t *)buf)->val2 =
((float64_value_t *)data_ptr)->val2;
break;
default:
SYS_LOG_ERR("unknown obj data_type %d",
obj_field->data_type);
return -EINVAL;
}
}
return 0;
}
int lwm2m_engine_get_string(char *pathstr, void *str, u16_t strlen)
{
return lwm2m_engine_get(pathstr, str, strlen);
}
u8_t lwm2m_engine_get_u8(char *pathstr)
{
u8_t value = 0;
lwm2m_engine_get(pathstr, &value, 1);
return value;
}
u16_t lwm2m_engine_get_u16(char *pathstr)
{
u16_t value = 0;
lwm2m_engine_get(pathstr, &value, 2);
return value;
}
u32_t lwm2m_engine_get_u32(char *pathstr)
{
u32_t value = 0;
lwm2m_engine_get(pathstr, &value, 4);
return value;
}
u64_t lwm2m_engine_get_u64(char *pathstr)
{
u64_t value = 0;
lwm2m_engine_get(pathstr, &value, 8);
return value;
}
s8_t lwm2m_engine_get_s8(char *pathstr)
{
s8_t value = 0;
lwm2m_engine_get(pathstr, &value, 1);
return value;
}
s16_t lwm2m_engine_get_s16(char *pathstr)
{
s16_t value = 0;
lwm2m_engine_get(pathstr, &value, 2);
return value;
}
s32_t lwm2m_engine_get_s32(char *pathstr)
{
s32_t value = 0;
lwm2m_engine_get(pathstr, &value, 4);
return value;
}
s64_t lwm2m_engine_get_s64(char *pathstr)
{
s64_t value = 0;
lwm2m_engine_get(pathstr, &value, 8);
return value;
}
bool lwm2m_engine_get_bool(char *pathstr)
{
return (lwm2m_engine_get_s8(pathstr) != 0);
}
int lwm2m_engine_get_float32(char *pathstr, float32_value_t *buf)
{
return lwm2m_engine_get(pathstr, buf, sizeof(float32_value_t));
}
int lwm2m_engine_get_float64(char *pathstr, float64_value_t *buf)
{
return lwm2m_engine_get(pathstr, buf, sizeof(float64_value_t));
}
/* user callback functions */
static int engine_get_resource(struct lwm2m_obj_path *path,
struct lwm2m_engine_res_inst **res)
{
int i;
struct lwm2m_engine_obj_inst *obj_inst;
if (!path) {
return -EINVAL;
}
/* find obj_inst/res_id */
obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
if (!obj_inst) {
SYS_LOG_ERR("obj instance %d/%d not found",
path->obj_id, path->obj_inst_id);
return -ENOENT;
}
if (!obj_inst->resources || obj_inst->resource_count == 0) {
SYS_LOG_ERR("obj instance has no resources");
return -EINVAL;
}
for (i = 0; i < obj_inst->resource_count; i++) {
if (obj_inst->resources[i].res_id == path->res_id) {
*res = &obj_inst->resources[i];
break;
}
}
if (!*res) {
SYS_LOG_ERR("res instance %d not found", path->res_id);
return -ENOENT;
}
return 0;
}
static int engine_get_resource_from_pathstr(char *pathstr,
struct lwm2m_engine_res_inst **res)
{
int ret;
struct lwm2m_obj_path path;
memset(&path, 0, sizeof(path));
ret = string_to_path(pathstr, &path, '/');
if (ret < 0) {
return ret;
}
if (path.level < 3) {
SYS_LOG_ERR("path must have 3 parts");
return -EINVAL;
}
return engine_get_resource(&path, res);
}
int lwm2m_engine_register_read_callback(char *pathstr,
lwm2m_engine_get_data_cb_t cb)
{
int ret;
struct lwm2m_engine_res_inst *res = NULL;
ret = engine_get_resource_from_pathstr(pathstr, &res);
if (ret < 0) {
return ret;
}
res->read_cb = cb;
return 0;
}
int lwm2m_engine_register_pre_write_callback(char *pathstr,
lwm2m_engine_get_data_cb_t cb)
{
int ret;
struct lwm2m_engine_res_inst *res = NULL;
ret = engine_get_resource_from_pathstr(pathstr, &res);
if (ret < 0) {
return ret;
}
res->pre_write_cb = cb;
return 0;
}
int lwm2m_engine_register_post_write_callback(char *pathstr,
lwm2m_engine_set_data_cb_t cb)
{
int ret;
struct lwm2m_engine_res_inst *res = NULL;
ret = engine_get_resource_from_pathstr(pathstr, &res);
if (ret < 0) {
return ret;
}
res->post_write_cb = cb;
return 0;
}
int lwm2m_engine_register_exec_callback(char *pathstr,
lwm2m_engine_exec_cb_t cb)
{
int ret;
struct lwm2m_engine_res_inst *res = NULL;
ret = engine_get_resource_from_pathstr(pathstr, &res);
if (ret < 0) {
return ret;
}
res->execute_cb = cb;
return 0;
}
/* generic data handlers */
static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst,
struct lwm2m_engine_res_inst *res,
struct lwm2m_engine_obj_field *obj_field,
struct lwm2m_engine_context *context)
{
struct lwm2m_output_context *out = context->out;
struct lwm2m_obj_path *path = context->path;
int i, loop_max = 1;
u16_t res_inst_id_tmp = 0;
void *data_ptr = NULL;
size_t data_len = 0;
if (!obj_inst || !res || !obj_field || !context) {
return -EINVAL;
}
/* setup initial data elements */
data_ptr = res->data_ptr;
data_len = res->data_len;
/* allow user to override data elements via callback */
if (res->read_cb) {
data_ptr = res->read_cb(obj_inst->obj_inst_id, &data_len);
}
if (!data_ptr || data_len == 0) {
return -EINVAL;
}
if (res->multi_count_var != NULL) {
engine_put_begin_ri(out, path);
loop_max = *res->multi_count_var;
res_inst_id_tmp = path->res_inst_id;
}
for (i = 0; i < loop_max; i++) {
if (res->multi_count_var != NULL) {
path->res_inst_id = (u16_t) i;
}
switch (obj_field->data_type) {
/* do nothing for OPAQUE (probably has a callback) */
case LWM2M_RES_TYPE_OPAQUE:
break;
/* TODO: handle multi count for string? */
case LWM2M_RES_TYPE_STRING:
engine_put_string(out, path, (u8_t *)data_ptr,
strlen((u8_t *)data_ptr));
break;
case LWM2M_RES_TYPE_U64:
engine_put_s64(out, path,
(s64_t)((u64_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_U32:
case LWM2M_RES_TYPE_TIME:
engine_put_s32(out, path,
(s32_t)((u32_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_U16:
engine_put_s16(out, path,
(s16_t)((u16_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_U8:
engine_put_s8(out, path,
(s8_t)((u8_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_S64:
engine_put_s64(out, path,
((s64_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_S32:
engine_put_s32(out, path,
((s32_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_S16:
engine_put_s16(out, path,
((s16_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_S8:
engine_put_s8(out, path,
((s8_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_BOOL:
engine_put_bool(out, path,
((bool *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_FLOAT32:
engine_put_float32fix(out, path,
&((float32_value_t *)data_ptr)[i]);
break;
case LWM2M_RES_TYPE_FLOAT64:
engine_put_float64fix(out, path,
&((float64_value_t *)data_ptr)[i]);
break;
default:
SYS_LOG_ERR("unknown obj data_type %d",
obj_field->data_type);
return -EINVAL;
}
}
if (res->multi_count_var != NULL) {
engine_put_end_ri(out, path);
path->res_inst_id = res_inst_id_tmp;
}
return 0;
}
/* This function is exposed for the content format writers */
int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
struct lwm2m_engine_res_inst *res,
struct lwm2m_engine_obj_field *obj_field,
struct lwm2m_engine_context *context)
{
struct lwm2m_input_context *in = context->in;
struct lwm2m_obj_path *path = context->path;
s64_t temp64 = 0;
s32_t temp32 = 0;
void *data_ptr = NULL;
size_t data_len = 0;
size_t len = 0;
if (!obj_inst || !res || !obj_field || !context) {
return -EINVAL;
}
/* setup initial data elements */
data_ptr = res->data_ptr;
data_len = res->data_len;
/* allow user to override data elements via callback */
if (res->pre_write_cb) {
data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, &data_len);
}
/* TODO: check for block transfer fields here */
if (data_ptr && data_len > 0) {
switch (obj_field->data_type) {
/* do nothing for OPAQUE (probably has a callback) */
case LWM2M_RES_TYPE_OPAQUE:
data_ptr = in->inbuf;
len = in->insize;
break;
case LWM2M_RES_TYPE_STRING:
engine_get_string(in, (u8_t *)data_ptr, data_len);
len = strlen((char *)data_ptr);
break;
case LWM2M_RES_TYPE_U64:
engine_get_s64(in, &temp64);
*(u64_t *)data_ptr = temp64;
len = 8;
break;
case LWM2M_RES_TYPE_U32:
case LWM2M_RES_TYPE_TIME:
engine_get_s32(in, &temp32);
*(u32_t *)data_ptr = temp32;
len = 4;
break;
case LWM2M_RES_TYPE_U16:
engine_get_s32(in, &temp32);
*(u16_t *)data_ptr = temp32;
len = 2;
break;
case LWM2M_RES_TYPE_U8:
engine_get_s32(in, &temp32);
*(u8_t *)data_ptr = temp32;
len = 1;
break;
case LWM2M_RES_TYPE_S64:
engine_get_s64(in, (s64_t *)data_ptr);
len = 8;
break;
case LWM2M_RES_TYPE_S32:
engine_get_s32(in, (s32_t *)data_ptr);
len = 4;
break;
case LWM2M_RES_TYPE_S16:
engine_get_s32(in, &temp32);
*(s16_t *)data_ptr = temp32;
len = 2;
break;
case LWM2M_RES_TYPE_S8:
engine_get_s32(in, &temp32);
*(s8_t *)data_ptr = temp32;
len = 1;
break;
case LWM2M_RES_TYPE_BOOL:
engine_get_bool(in, (bool *)data_ptr);
len = 1;
break;
case LWM2M_RES_TYPE_FLOAT32:
engine_get_float32fix(in,
(float32_value_t *)data_ptr);
len = 4;
break;
case LWM2M_RES_TYPE_FLOAT64:
engine_get_float64fix(in,
(float64_value_t *)data_ptr);
len = 8;
break;
default:
SYS_LOG_ERR("unknown obj data_type %d",
obj_field->data_type);
return -EINVAL;
}
}
if (res->post_write_cb) {
/* ignore return value here */
res->post_write_cb(obj_inst->obj_inst_id, data_ptr, len,
false, 0);
}
NOTIFY_OBSERVER_PATH(path);
return 0;
}
static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
if (!obj || !context) {
return -EINVAL;
}
/* TODO: set parameters on resource for notification */
return 0;
}
static int lwm2m_exec_handler(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
struct lwm2m_obj_path *path = context->path;
struct lwm2m_engine_obj_inst *obj_inst;
struct lwm2m_engine_res_inst *res = NULL;
int ret;
if (!obj || !context) {
return -EINVAL;
}
obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
if (!obj_inst) {
return -ENOENT;
}
ret = engine_get_resource(path, &res);
if (ret < 0) {
return ret;
}
if (res->execute_cb) {
return res->execute_cb(obj_inst->obj_inst_id);
}
/* TODO: something else to handle for execute? */
return -ENOENT;
}
static int lwm2m_delete_handler(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
struct lwm2m_obj_path *path = context->path;
struct lwm2m_engine_obj_inst *obj_inst;
SYS_LOG_DBG(">> DELETE [path:%u/%u/%u(%u)]",
path->obj_id, path->obj_inst_id, path->res_id, path->level);
if (!obj || !context) {
return -EINVAL;
}
obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
if (!obj_inst) {
return -ENOENT;
}
engine_unregister_obj_inst(obj_inst);
obj->instance_count--;
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
engine_trigger_update();
#endif
if (obj->delete_cb) {
return obj->delete_cb(path->obj_inst_id);
}
return 0;
}
/*
* ZoAP API needs to create the net_pkt buffer in the correct order.
* This function performs last minute verification that outbuf is initialized.
*/
static void outbuf_init_check(struct lwm2m_output_context *out)
{
if (!out->outbuf) {
out->outbuf = zoap_packet_get_payload(out->out_zpkt,
&out->outsize);
}
}
#define MATCH_NONE 0
#define MATCH_ALL 1
#define MATCH_SINGLE 2
static int do_read_op(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
struct lwm2m_output_context *out = context->out;
struct lwm2m_obj_path *path = context->path;
struct lwm2m_engine_obj_inst *obj_inst;
int ret = 0, pos = 0, index, match_type;
u8_t num_read = 0;
u8_t initialized;
struct lwm2m_engine_res_inst *res;
struct lwm2m_engine_obj_field *obj_field;
u16_t temp_res_id;
obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
if (!obj_inst) {
return -ENOENT;
}
while (obj_inst) {
if (!obj_inst->resources || obj_inst->resource_count == 0) {
continue;
}
match_type = MATCH_NONE;
/* check obj_inst path for at least partial match */
if (path->obj_id == obj_inst->obj->obj_id &&
path->obj_inst_id == obj_inst->obj_inst_id) {
if (path->level > 2) {
match_type = MATCH_SINGLE;
} else {
match_type = MATCH_ALL;
}
}
if (match_type == MATCH_NONE) {
continue;
}
/* save path's res_id because we may need to change it below */
temp_res_id = path->res_id;
initialized = 0;
for (index = 0; index < obj_inst->resource_count; index++) {
res = &obj_inst->resources[index];
/*
* On a MATCH_ALL loop, we need to set path's res_id
* for lwm2m_read_handler to read this specific
* resource.
*/
if (match_type == MATCH_ALL) {
path->res_id = res->res_id;
} else if (path->res_id != res->res_id) {
continue;
}
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj,
res->res_id);
if (!obj_field) {
ret = -ENOENT;
} else if ((obj_field->permissions &
LWM2M_PERM_R) != LWM2M_PERM_R) {
ret = -EPERM;
} else {
/* outbuf init / formatter startup if needed */
if (!initialized) {
outbuf_init_check(out);
engine_put_begin(out, path);
initialized = 1;
}
/* perform read operation on this resource */
ret = lwm2m_read_handler(obj_inst, res,
obj_field, context);
if (ret < 0) {
/* What to do here? */
SYS_LOG_ERR("READ OP failed: %d", ret);
} else {
num_read += 1;
pos = out->outlen;
}
}
/* on single read break if errors */
if (ret < 0 && match_type == MATCH_SINGLE) {
break;
}
/* when reading multiple resources ignore return code */
ret = 0;
}
/* restore path's res_id in case it was changed */
path->res_id = temp_res_id;
/* if we wrote anything, finish formatting */
if (initialized) {
engine_put_end(out, path);
pos = out->outlen;
}
/* advance to the next object instance */
obj_inst = next_engine_obj_inst(obj_inst, path->obj_id,
path->obj_inst_id);
}
/* did not read anything even if we should have - on single item */
if (ret == 0 && num_read == 0 && path->level == 3) {
return -ENOENT;
}
out->outlen = pos;
return ret;
}
static int do_discover_op(struct lwm2m_engine_context *context)
{
struct lwm2m_output_context *out = context->out;
struct lwm2m_engine_obj_inst *obj_inst;
int i = 0;
/* set output content-format */
zoap_add_option_int(out->out_zpkt,
ZOAP_OPTION_CONTENT_FORMAT,
LWM2M_FORMAT_APP_LINK_FORMAT);
/* init the outbuffer */
out->outbuf = zoap_packet_get_payload(out->out_zpkt,
&out->outsize);
/* </.well-known/core>,**;ct=40 */
memcpy(out->outbuf, DISCOVER_PREFACE, strlen(DISCOVER_PREFACE));
out->outlen += strlen(DISCOVER_PREFACE);
SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst, node) {
/* TODO: support bootstrap discover
* Avoid discovery for security object (5.2.7.3)
*/
if (obj_inst->obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
continue;
}
out->outlen += sprintf(&out->outbuf[out->outlen], ",</%u/%u>",
obj_inst->obj->obj_id,
obj_inst->obj_inst_id);
for (i = 0; i < obj_inst->resource_count; i++) {
out->outlen += sprintf(&out->outbuf[out->outlen],
",</%u/%u/%u>",
obj_inst->obj->obj_id,
obj_inst->obj_inst_id,
obj_inst->resources[i].res_id);
}
}
out->outbuf[out->outlen] = '\0';
return 0;
}
int lwm2m_get_or_create_engine_obj(struct lwm2m_engine_context *context,
struct lwm2m_engine_obj_inst **obj_inst,
u8_t *created)
{
struct lwm2m_obj_path *path = context->path;
int ret = 0;
if (created) {
*created = 0;
}
*obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
if (!*obj_inst) {
ret = lwm2m_create_obj_inst(path->obj_id, path->obj_inst_id,
obj_inst);
if (ret < 0) {
return ret;
}
zoap_header_set_code(context->in->in_zpkt,
ZOAP_RESPONSE_CODE_CREATED);
/* set created flag to one */
if (created) {
*created = 1;
}
}
return ret;
}
static int do_write_op(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context,
u16_t format)
{
switch (format) {
case LWM2M_FORMAT_PLAIN_TEXT:
case LWM2M_FORMAT_OMA_PLAIN_TEXT:
return do_write_op_plain_text(obj, context);
case LWM2M_FORMAT_OMA_TLV:
case LWM2M_FORMAT_OMA_OLD_TLV:
return do_write_op_tlv(obj, context);
#ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
case LWM2M_FORMAT_OMA_JSON:
case LWM2M_FORMAT_OMA_OLD_JSON:
return do_write_op_json(obj, context);
#endif
default:
SYS_LOG_ERR("Unsupported format: %u", format);
return -EINVAL;
}
}
static int get_observe_option(const struct zoap_packet *zpkt)
{
struct zoap_option option = {};
u16_t count = 1;
int r;
r = zoap_find_options(zpkt, ZOAP_OPTION_OBSERVE, &option, count);
if (r <= 0) {
return -ENOENT;
}
return zoap_option_value_to_int(&option);
}
static int handle_request(struct zoap_packet *request,
struct zoap_packet *response,
struct sockaddr *from_addr)
{
int r;
u8_t code;
struct zoap_option options[4];
struct lwm2m_engine_obj *obj;
const u8_t *token;
u8_t tkl = 0;
u16_t format, accept;
struct lwm2m_input_context in;
struct lwm2m_output_context out;
struct lwm2m_obj_path path;
struct lwm2m_engine_context context;
int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
bool discover = false;
/* setup engine context */
memset(&context, 0, sizeof(struct lwm2m_engine_context));
context.in = &in;
context.out = &out;
context.path = &path;
engine_clear_context(&context);
/* set ZoAP request / response */
in.in_zpkt = request;
out.out_zpkt = response;
/* set default reader/writer */
in.reader = &plain_text_reader;
out.writer = &plain_text_writer;
/* parse the URL path into components */
r = zoap_find_options(in.in_zpkt, ZOAP_OPTION_URI_PATH, options, 4);
if (r > 0) {
/* check for .well-known/core URI query (DISCOVER) */
if (r == 2 &&
(options[0].len == 11 &&
strncmp(options[0].value, ".well-known", 11) == 0) &&
(options[1].len == 4 &&
strncmp(options[1].value, "core", 4) == 0)) {
discover = true;
} else {
zoap_options_to_path(options, r, &path);
}
}
/* read Content Format */
r = zoap_find_options(in.in_zpkt, ZOAP_OPTION_CONTENT_FORMAT,
options, 1);
if (r > 0) {
format = zoap_option_value_to_int(&options[0]);
} else {
SYS_LOG_DBG("No content-format given. Assume text plain.");
format = LWM2M_FORMAT_PLAIN_TEXT;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
}
/* read Accept */
r = zoap_find_options(in.in_zpkt, ZOAP_OPTION_ACCEPT, options, 1);
if (r > 0) {
accept = zoap_option_value_to_int(&options[0]);
} else {
SYS_LOG_DBG("No accept option given. Assume OMA TLV.");
accept = LWM2M_FORMAT_OMA_TLV;
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
}
/* TODO: Handle bootstrap deleted -- re-add when DTLS support ready */
code = zoap_header_get_code(in.in_zpkt);
/* find registered obj */
obj = get_engine_obj(path.obj_id);
if (!obj) {
/* No matching object found - ignore request */
return -ENOENT;
}
format = select_reader(&in, format);
accept = select_writer(&out, accept);
/* set the operation */
switch (code & ZOAP_REQUEST_MASK) {
case ZOAP_METHOD_GET:
if (discover || format == LWM2M_FORMAT_APP_LINK_FORMAT) {
context.operation = LWM2M_OP_DISCOVER;
accept = LWM2M_FORMAT_APP_LINK_FORMAT;
} else {
context.operation = LWM2M_OP_READ;
}
/* check for observe */
observe = get_observe_option(in.in_zpkt);
zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CONTENT);
break;
case ZOAP_METHOD_POST:
if (path.level < 2) {
/* write/create a object instance */
context.operation = LWM2M_OP_WRITE;
} else {
context.operation = LWM2M_OP_EXECUTE;
}
zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CHANGED);
break;
case ZOAP_METHOD_PUT:
context.operation = LWM2M_OP_WRITE;
zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CHANGED);
break;
case ZOAP_METHOD_DELETE:
context.operation = LWM2M_OP_DELETE;
zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_DELETED);
break;
default:
break;
}
/* set response token */
token = zoap_header_get_token(in.in_zpkt, &tkl);
if (tkl) {
zoap_header_set_token(out.out_zpkt, token, tkl);
}
in.inpos = 0;
in.inbuf = zoap_packet_get_payload(in.in_zpkt, &in.insize);
/* TODO: check for block transfer? */
switch (context.operation) {
case LWM2M_OP_READ:
if (observe == 0) {
/* add new observer */
if (token) {
r = zoap_add_option_int(out.out_zpkt,
ZOAP_OPTION_OBSERVE, 1);
if (r) {
SYS_LOG_ERR("OBSERVE option error: %d",
r);
}
r = engine_add_observer(
net_pkt_context(in.in_zpkt->pkt),
from_addr, token, tkl, &path, accept);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
if (r < 0) {
SYS_LOG_ERR("add OBSERVE error: %d", r);
}
} else {
SYS_LOG_ERR("OBSERVE request missing token");
}
} else if (observe == 1) {
/* use token from this request */
token = zoap_header_get_token(in.in_zpkt, &tkl);
/* remove observer */
r = engine_remove_observer(token, tkl);
if (r < 0) {
SYS_LOG_ERR("remove obserer error: %d", r);
}
}
/* set output content-format */
r = zoap_add_option_int(out.out_zpkt,
ZOAP_OPTION_CONTENT_FORMAT, accept);
if (r > 0) {
SYS_LOG_ERR("Error setting response content-format: %d",
r);
}
r = do_read_op(obj, &context);
break;
case LWM2M_OP_DISCOVER:
r = do_discover_op(&context);
break;
case LWM2M_OP_WRITE:
r = do_write_op(obj, &context, format);
break;
case LWM2M_OP_WRITE_ATTR:
r = lwm2m_write_attr_handler(obj, &context);
break;
case LWM2M_OP_EXECUTE:
r = lwm2m_exec_handler(obj, &context);
break;
case LWM2M_OP_DELETE:
r = lwm2m_delete_handler(obj, &context);
break;
default:
SYS_LOG_ERR("Unknown operation: %u", context.operation);
return -EINVAL;
}
if (r == 0) {
/* TODO: Handle blockwise 1 */
if (out.outlen > 0) {
SYS_LOG_DBG("replying with %u bytes", out.outlen);
zoap_packet_set_used(out.out_zpkt, out.outlen);
} else {
SYS_LOG_DBG("no data in reply");
}
} else {
if (r == -ENOENT) {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_NOT_FOUND);
r = 0;
} else if (r == -EPERM) {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_NOT_ALLOWED);
r = 0;
} else {
/* Failed to handle the request */
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_INTERNAL_ERROR);
r = 0;
}
}
return r;
}
int lwm2m_udp_sendto(struct net_pkt *pkt, const struct sockaddr *dst_addr)
{
return net_context_sendto(pkt, dst_addr, NET_SOCKADDR_MAX_SIZE,
NULL, K_NO_WAIT, NULL, NULL);
}
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
void lwm2m_udp_receive(struct net_context *ctx, struct net_pkt *pkt,
struct zoap_pending *zpendings, int num_zpendings,
struct zoap_reply *zreplies, int num_zreplies,
bool handle_separate_response,
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
int (*udp_request_handler)(struct zoap_packet *,
struct zoap_packet *,
struct sockaddr *))
{
struct net_udp_hdr hdr, *udp_hdr;
struct zoap_pending *pending;
struct zoap_reply *reply;
struct zoap_packet response;
struct sockaddr from_addr;
struct zoap_packet response2;
struct net_pkt *pkt2;
int header_len, r;
const u8_t *token;
u8_t tkl;
udp_hdr = net_udp_get_hdr(pkt, &hdr);
if (!udp_hdr) {
SYS_LOG_ERR("Invalid UDP data");
return;
}
/* Save the from address */
#if defined(CONFIG_NET_IPV6)
if (net_pkt_family(pkt) == AF_INET6) {
net_ipaddr_copy(&net_sin6(&from_addr)->sin6_addr,
&NET_IPV6_HDR(pkt)->src);
net_sin6(&from_addr)->sin6_port = udp_hdr->src_port;
net_sin6(&from_addr)->sin6_family = AF_INET6;
}
#endif
#if defined(CONFIG_NET_IPV4)
if (net_pkt_family(pkt) == AF_INET) {
net_ipaddr_copy(&net_sin(&from_addr)->sin_addr,
&NET_IPV4_HDR(pkt)->src);
net_sin(&from_addr)->sin_port = udp_hdr->src_port;
net_sin(&from_addr)->sin_family = AF_INET;
}
#endif
/*
* zoap expects that buffer->data starts at the
* beginning of the CoAP header
*/
header_len = net_pkt_appdata(pkt) - pkt->frags->data;
net_buf_pull(pkt->frags, header_len);
r = zoap_packet_parse(&response, pkt);
if (r < 0) {
SYS_LOG_ERR("Invalid data received (err:%d)", r);
goto cleanup;
}
token = zoap_header_get_token(&response, &tkl);
pending = zoap_pending_received(&response, zpendings, num_zpendings);
if (pending) {
/* TODO: If necessary cancel retransmissions */
}
SYS_LOG_DBG("checking for reply from [%s]",
lwm2m_sprint_ip_addr(&from_addr));
reply = zoap_response_received(&response, &from_addr,
zreplies, num_zreplies);
if (!reply) {
/*
* 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 &&
zoap_header_get_type(&response) == ZOAP_TYPE_CON) {
/* Create a response packet if we reach this point */
r = lwm2m_init_message(ctx, &response2, &pkt2,
ZOAP_TYPE_ACK,
zoap_header_get_code(&response),
zoap_header_get_id(&response),
NULL, -1);
if (r < 0) {
if (pkt2) {
net_pkt_unref(pkt2);
}
goto cleanup;
}
/*
* The "response" here is actually a new request
*/
r = udp_request_handler(&response, &response2,
&from_addr);
if (r < 0) {
SYS_LOG_ERR("Request handler error: %d", r);
} else {
r = lwm2m_udp_sendto(pkt2, &from_addr);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
if (r < 0) {
SYS_LOG_ERR("Err sending response: %d",
r);
}
}
} else {
SYS_LOG_ERR("No handler for response");
}
} else {
/*
* Separate response is composed of 2 messages, empty ACK with
* no token and an additional message with a matching token id
* (based on the token used by the CON request).
*
* Since the ACK received by the notify CON messages are also
* empty with no token (consequence of always using the same
* token id for all notifications), we have to use an
* additional flag to decide when to clear the reply callback.
*/
if (handle_separate_response && !tkl &&
zoap_header_get_type(&response) == ZOAP_TYPE_ACK) {
SYS_LOG_DBG("separated response, not removing reply");
} else {
SYS_LOG_DBG("reply %p handled and removed", reply);
zoap_reply_clear(reply);
}
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
}
cleanup:
if (pkt) {
net_pkt_unref(pkt);
}
}
static void udp_receive(struct net_context *ctx, struct net_pkt *pkt,
int status, void *user_data)
{
lwm2m_udp_receive(ctx, pkt, pendings, NUM_PENDINGS,
replies, NUM_REPLIES, false, handle_request);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
}
static void retransmit_request(struct k_work *work)
{
struct zoap_pending *pending;
int r;
pending = zoap_pending_next_to_expire(pendings, NUM_PENDINGS);
if (!pending) {
return;
}
r = lwm2m_udp_sendto(pending->pkt, &pending->addr);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
if (r < 0) {
return;
}
if (!zoap_pending_cycle(pending)) {
zoap_pending_clear(pending);
return;
}
k_delayed_work_submit(&retransmit_work, pending->timeout);
}
static int notify_message_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const struct sockaddr *from)
{
int ret = 0;
u8_t type, code;
type = zoap_header_get_type(response);
code = zoap_header_get_code(response);
SYS_LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'",
type,
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code),
sprint_token(reply->token, reply->tkl));
/* remove observer on ZOAP_TYPE_RESET */
if (type == ZOAP_TYPE_RESET) {
if (reply->tkl > 0) {
ret = engine_remove_observer(reply->token, reply->tkl);
if (ret) {
SYS_LOG_ERR("remove obserer error: %d", ret);
}
} else {
SYS_LOG_ERR("notify reply missing token -- ignored.");
}
}
return 0;
}
static int generate_notify_message(struct observe_node *obs,
bool manual_trigger)
{
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
struct zoap_packet request;
struct lwm2m_engine_obj_inst *obj_inst;
struct lwm2m_output_context out;
struct lwm2m_engine_context context;
struct lwm2m_obj_path path;
int ret = 0;
/* setup engine context */
memset(&context, 0, sizeof(struct lwm2m_engine_context));
context.out = &out;
engine_clear_context(&context);
/* dont clear the path */
memcpy(&path, &obs->path, sizeof(struct lwm2m_obj_path));
context.path = &path;
context.operation = LWM2M_OP_READ;
out.out_zpkt = &request;
SYS_LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
manual_trigger ? "MANUAL" : "AUTO",
obs->path.obj_id,
obs->path.obj_inst_id,
obs->path.res_id,
obs->path.level,
sprint_token(obs->token, obs->tkl),
lwm2m_sprint_ip_addr(&obs->addr),
k_uptime_get());
obj_inst = get_engine_obj_inst(obs->path.obj_id,
obs->path.obj_inst_id);
if (!obj_inst) {
SYS_LOG_ERR("unable to get engine obj for %u/%u",
obs->path.obj_id,
obs->path.obj_inst_id);
return -EINVAL;
}
ret = lwm2m_init_message(obs->net_ctx, out.out_zpkt, &pkt,
ZOAP_TYPE_CON, ZOAP_RESPONSE_CODE_CONTENT,
0, obs->token, obs->tkl);
if (ret) {
goto cleanup;
}
/* each notification should increment the obs counter */
obs->counter++;
ret = zoap_add_option_int(out.out_zpkt, ZOAP_OPTION_OBSERVE,
obs->counter);
if (ret) {
SYS_LOG_ERR("OBSERVE option error: %d", ret);
goto cleanup;
}
/* set the output writer */
select_writer(&out, obs->format);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
/* set response content-format */
ret = zoap_add_option_int(out.out_zpkt, ZOAP_OPTION_CONTENT_FORMAT,
obs->format);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
if (ret > 0) {
SYS_LOG_ERR("error setting content-format (err:%d)", ret);
goto cleanup;
}
ret = do_read_op(obj_inst->obj, &context);
if (ret == 0) {
if (out.outlen > 0) {
zoap_packet_set_used(out.out_zpkt, out.outlen);
} else {
SYS_LOG_DBG("no data in reply");
}
} else {
SYS_LOG_ERR("error in multi-format read (err:%d)", ret);
goto cleanup;
}
pending = lwm2m_init_message_pending(out.out_zpkt,
&obs->addr,
pendings, NUM_PENDINGS);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
reply = zoap_reply_next_unused(replies, NUM_REPLIES);
if (!reply) {
SYS_LOG_ERR("No resources for waiting for replies.");
ret = -ENOMEM;
goto cleanup;
}
zoap_reply_init(reply, &request);
reply->reply = notify_message_reply_cb;
ret = lwm2m_udp_sendto(pkt, &obs->addr);
net: lwm2m: initial library support for LWM2M Origin: SICS-IoT / Contiki OS URL: https://github.com/sics-iot/lwm2m-contiki/tree/lwm2m-standalone-dtls commit: d07b0bcd77ec7e8b93787669507f3d86cfbea64a Purpose: Introduction of LwM2M client library. Maintained-by: Zephyr Lightweight Machine-to-Machine (LwM2M) is a protocol stack extension of the Constrained Application Protocol (CoAP) which uses UDP transmission packets. This library was based on source worked on by Joakim Eriksson, Niclas Finne and Joel Hoglund which was adopted by Contiki and then later revamped to work as a stand-alone library. A VERY high level summary of the changes made: - [ALL] sources were re-formatted to Zephyr coding standards - [engine] The engine portion was re-written due to the heavy reliance on ER-CoAP APIs which are not compatible to the Zephyr CoAP APIs as well as other Zephyr specific needs. - [engine] All LWM2M/IPSO object data is now abstracted into resource data which stores information like the data type, length, callbacks to help with read/write. The engine modifies this data directly (or makes callbacks) instead of all of the logic for this living in each object's code. (This wasn't scaling well as I was implementing changes). - [engine] Related to the above change, I also added a generic set of getter/setter functions that user applications can call to change the object data instead of having to add getter/setting methods in each object. - [engine] The original sources shared the engine's context structure quite extensively causing a problem with portability. I broke up the context into it's individual parts: LWM2M path data, input data and output data and pass only the needed data into each set of APIs. - [content format read/writer] sources were re-organized into single .c/h files per content formatter. - [content format read/writer] sources were re-written where necessary to remove the sharing of the lwm2m engine's context and instead only requires the path and input or output data specific to it's function. - [LwM2M objects] re-written using the new engine's abstractions Signed-off-by: Michael Scott <michael.scott@linaro.org>
2017-07-07 11:04:03 -07:00
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).", ret);
goto cleanup;
}
SYS_LOG_DBG("NOTIFY MSG: SENT");
zoap_pending_cycle(pending);
k_delayed_work_submit(&retransmit_work, pending->timeout);
return ret;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
return ret;
}
/* TODO: this needs to be triggered via work_queue */
static void lwm2m_engine_service(void)
{
struct observe_node *obs;
s64_t timestamp;
while (true) {
/*
* 1. scan the observer list
* 2. For each notify event found, scan the observer list
* 3. For each observer match, generate a NOTIFY message,
* attaching the notify response handler
*/
timestamp = k_uptime_get();
SYS_SLIST_FOR_EACH_CONTAINER(&engine_observer_list, obs, node) {
/*
* manual notify requirements:
* - event_timestamp > last_timestamp
* - current timestamp > last_timestamp + min_period_sec
*/
if (obs->event_timestamp > obs->last_timestamp &&
timestamp > obs->last_timestamp +
K_SECONDS(obs->min_period_sec)) {
obs->last_timestamp = k_uptime_get();
generate_notify_message(obs, true);
/*
* automatic time-based notify requirements:
* - current timestamp > last_timestamp + max_period_sec
*/
} else if (timestamp > obs->last_timestamp +
K_SECONDS(obs->min_period_sec)) {
obs->last_timestamp = k_uptime_get();
generate_notify_message(obs, false);
}
}
k_sleep(K_MSEC(ENGINE_UPDATE_INTERVAL));
}
}
int lwm2m_engine_start(struct net_context *net_ctx)
{
int ret = 0;
/* set callback */
ret = net_context_recv(net_ctx, udp_receive, 0, NULL);
if (ret) {
SYS_LOG_ERR("Could not set receive for net context (err:%d)",
ret);
}
return ret;
}
static int lwm2m_engine_init(struct device *dev)
{
/* start thread to handle OBSERVER / NOTIFY events */
k_thread_create(&engine_thread_data,
&engine_thread_stack[0],
K_THREAD_STACK_SIZEOF(engine_thread_stack),
(k_thread_entry_t) lwm2m_engine_service,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
k_delayed_work_init(&retransmit_work, retransmit_request);
SYS_LOG_DBG("LWM2M engine thread started");
return 0;
}
SYS_INIT(lwm2m_engine_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);