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>
This commit is contained in:
Michael Scott 2017-07-07 11:04:03 -07:00 committed by Jukka Rissanen
commit c46c206f8c
23 changed files with 7371 additions and 0 deletions

View file

@ -69,6 +69,12 @@ can be disabled if not needed.
:ref:`coap-server-sample` using DTLS (Datagram Transport Layer Security)
(RFC 6347) are also implemented.
* **LWM2M** OMA Lightweight Machine-to-Machine Protocol (V1.0 Feb 2017) is
supported via the "Register Device" API (Register, De-Register and Update)
and has template implementations for Securty, Server, Device Management and
Firmware objects. DTLS and Bootstrap support are currently not supported.
:ref:`lwm2m-client-sample` implements the library as an example.
* **RPL** IPv6 Routing Protocol for Low-Power and Lossy Networks (RFC 6550)
is supported. RPL is an IPv6 based mesh routing protocol.

155
include/net/lwm2m.h Normal file
View file

@ -0,0 +1,155 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __LWM2M_H__
#define __LWM2M_H__
#include <net/net_context.h>
/* LWM2M Objects defined by OMA */
#define LWM2M_OBJECT_SECURITY_ID 0
#define LWM2M_OBJECT_SERVER_ID 1
#define LWM2M_OBJECT_ACCESS_CONTROL_ID 2
#define LWM2M_OBJECT_DEVICE_ID 3
#define LWM2M_OBJECT_CONNECTIVITY_MONITORING_ID 4
#define LWM2M_OBJECT_FIRMWARE_ID 5
#define LWM2M_OBJECT_LOCATION_ID 6
#define LWM2M_OBJECT_CONNECTIVITY_STATISTICS_ID 7
/* callback can return 1 if handled (don't update value) */
typedef void *(*lwm2m_engine_get_data_cb_t)(u16_t obj_inst_id,
size_t *data_len);
typedef int (*lwm2m_engine_set_data_cb_t)(u16_t obj_inst_id,
u8_t *data, u16_t data_len,
bool last_block, size_t total_size);
typedef int (*lwm2m_engine_exec_cb_t)(u16_t obj_inst_id);
/* LWM2M Device Object */
#define LWM2M_DEVICE_PWR_SRC_TYPE_DC_POWER 0
#define LWM2M_DEVICE_PWR_SRC_TYPE_BAT_INT 1
#define LWM2M_DEVICE_PWR_SRC_TYPE_BAT_EXT 2
#define LWM2M_DEVICE_PWR_SRC_TYPE_UNUSED 3
#define LWM2M_DEVICE_PWR_SRC_TYPE_PWR_OVER_ETH 4
#define LWM2M_DEVICE_PWR_SRC_TYPE_USB 5
#define LWM2M_DEVICE_PWR_SRC_TYPE_AC_POWER 6
#define LWM2M_DEVICE_PWR_SRC_TYPE_SOLAR 7
#define LWM2M_DEVICE_PWR_SRC_TYPE_MAX 8
#define LWM2M_DEVICE_ERROR_NONE 0
#define LWM2M_DEVICE_ERROR_LOW_POWER 1
#define LWM2M_DEVICE_ERROR_EXT_POWER_SUPPLY_OFF 2
#define LWM2M_DEVICE_ERROR_GPS_FAILURE 3
#define LWM2M_DEVICE_ERROR_LOW_SIGNAL_STRENGTH 4
#define LWM2M_DEVICE_ERROR_OUT_OF_MEMORY 5
#define LWM2M_DEVICE_ERROR_SMS_FAILURE 6
#define LWM2M_DEVICE_ERROR_NETWORK_FAILURE 7
#define LWM2M_DEVICE_ERROR_PERIPHERAL_FAILURE 8
#define LWM2M_DEVICE_BATTERY_STATUS_NORMAL 0
#define LWM2M_DEVICE_BATTERY_STATUS_CHARGING 1
#define LWM2M_DEVICE_BATTERY_STATUS_CHARGE_COMP 2
#define LWM2M_DEVICE_BATTERY_STATUS_DAMAGED 3
#define LWM2M_DEVICE_BATTERY_STATUS_LOW 4
#define LWM2M_DEVICE_BATTERY_STATUS_NOT_INST 5
#define LWM2M_DEVICE_BATTERY_STATUS_UNKNOWN 6
int lwm2m_device_add_pwrsrc(u8_t pwr_src_type); /* returns index */
int lwm2m_device_remove_pwrsrc(int index);
int lwm2m_device_set_pwrsrc_voltage_mv(int index, int voltage_mv);
int lwm2m_device_set_pwrsrc_current_ma(int index, int current_ma);
int lwm2m_device_add_err(u8_t error_code);
/* LWM2M Firemware Update Object */
#define STATE_IDLE 0
#define STATE_DOWNLOADING 1
#define STATE_DOWNLOADED 2
#define STATE_UPDATING 3
#define RESULT_DEFAULT 0
#define RESULT_SUCCESS 1
#define RESULT_NO_STORAGE 2
#define RESULT_OUT_OF_MEM 3
#define RESULT_CONNECTION_LOST 4
#define RESULT_INTEGRITY_FAILED 5
#define RESULT_UNSUP_FW 6
#define RESULT_INVALID_URI 7
#define RESULT_UPDATE_FAILED 8
#define RESULT_UNSUP_PROTO 9
void lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb);
lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void);
/* LWM2M Engine */
/*
* float type below use the following logic:
* val1 is the whole number portion of decimal
* val2 is the decimal portion *1000000 for 32bit, *1000000000 for 64bit
* Example: 123.456 == val1: 123, val2:456000
* Example: 123.000456 = val1: 123, val2:456
*/
typedef struct float32_value {
s32_t val1;
s32_t val2;
} float32_value_t;
typedef struct float64_value {
s64_t val1;
s64_t val2;
} float64_value_t;
int lwm2m_engine_create_obj_inst(char *pathstr);
int lwm2m_engine_set_string(char *path, char *data_ptr);
int lwm2m_engine_set_u8(char *path, u8_t value);
int lwm2m_engine_set_u16(char *path, u16_t value);
int lwm2m_engine_set_u32(char *path, u32_t value);
int lwm2m_engine_set_u64(char *path, u64_t value);
int lwm2m_engine_set_s8(char *path, s8_t value);
int lwm2m_engine_set_s16(char *path, s16_t value);
int lwm2m_engine_set_s32(char *path, s32_t value);
int lwm2m_engine_set_s64(char *path, s64_t value);
int lwm2m_engine_set_bool(char *path, bool value);
int lwm2m_engine_set_float32(char *pathstr, float32_value_t *value);
int lwm2m_engine_set_float64(char *pathstr, float64_value_t *value);
int lwm2m_engine_get_string(char *path, void *str, u16_t strlen);
u8_t lwm2m_engine_get_u8(char *path);
u16_t lwm2m_engine_get_u16(char *path);
u32_t lwm2m_engine_get_u32(char *path);
u64_t lwm2m_engine_get_u64(char *path);
s8_t lwm2m_engine_get_s8(char *path);
s16_t lwm2m_engine_get_s16(char *path);
s32_t lwm2m_engine_get_s32(char *path);
s64_t lwm2m_engine_get_s64(char *path);
bool lwm2m_engine_get_bool(char *path);
int lwm2m_engine_get_float32(char *pathstr, float32_value_t *buf);
int lwm2m_engine_get_float64(char *pathstr, float64_value_t *buf);
int lwm2m_engine_register_read_callback(char *path,
lwm2m_engine_get_data_cb_t cb);
int lwm2m_engine_register_pre_write_callback(char *path,
lwm2m_engine_get_data_cb_t cb);
int lwm2m_engine_register_post_write_callback(char *path,
lwm2m_engine_set_data_cb_t cb);
int lwm2m_engine_register_exec_callback(char *path,
lwm2m_engine_exec_cb_t cb);
int lwm2m_engine_start(struct net_context *net_ctx);
/* LWM2M RD Client */
int lwm2m_rd_client_start(struct net_context *net_ctx,
struct sockaddr *peer_addr,
const char *ep_name);
#endif /* __LWM2M_H__ */

View file

@ -4,3 +4,4 @@ obj-$(CONFIG_DNS_RESOLVER) += dns/
obj-$(CONFIG_MQTT_LIB) += mqtt/
obj-$(CONFIG_HTTP) += http/
obj-$(CONFIG_NET_APP_SETTINGS) += app/
obj-$(CONFIG_LWM2M) += lwm2m/

View file

@ -16,6 +16,8 @@ source "subsys/net/lib/mqtt/Kconfig"
source "subsys/net/lib/http/Kconfig"
source "subsys/net/lib/lwm2m/Kconfig"
endmenu
menu "Network Applications"

View file

@ -21,3 +21,7 @@ endif
ifdef CONFIG_NET_APP_SETTINGS
include $(srctree)/subsys/net/lib/app/Makefile
endif
ifdef CONFIG_LWM2M
include $(srctree)/subsys/net/lib/lwm2m/Makefile
endif

View file

@ -0,0 +1,144 @@
#
# Copyright (c) 2017 Linaro Limited
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig LWM2M
bool "OMA LWM2M protocol stack"
default n
select ZOAP
help
This option adds logic for managing OMA LWM2M data
if LWM2M
config SYS_LOG_LWM2M_LEVEL
int "LWM2M log level"
depends on SYS_LOG
default 1
range 0 4
help
Set the log level for the LWM2M library.
config LWM2M_ENGINE_STACK_SIZE
int "LWM2M engine stack size"
default 1024
help
Set the stack size for the LWM2M library engine (used for handling
OBSERVE and NOTIFY events)
config LWM2M_ENGINE_MAX_PENDING
int "LWM2M engine max. pending objects"
default 5
help
Set the maximum pending objects for the LWM2M library client
config LWM2M_ENGINE_MAX_REPLIES
int "LWM2M engine max. reply objects"
default 5
help
Set the maximum reply objects for the LWM2M library client
config LWM2M_ENGINE_MAX_OBSERVER
int "Maximum # of observeable LWM2M resources"
default 50
range 10 200
help
This value sets the maximum number of resources which can be
added to the observe notification list.
config LWM2M_ENGINE_DEFAULT_LIFETIME
int "LWM2M engine default server connection lifetime"
default 30
help
Set the default lifetime (in seconds) for the LWM2M library engine
config LWM2M_LOCAL_PORT
int "LWM2M client port"
default 5683
help
This is the client port for LWM2M communication (0 = random, defaults
to 5683)
config LWM2M_SECURITY_INSTANCE_COUNT
int "Maximum # of LWM2M Security object instances"
default 3
range 1 10
help
This setting establishes the total count of LWM2M Security instances
available to the client.
config LWM2M_SERVER_INSTANCE_COUNT
int "Maximum # of LWM2M Server object instances"
default 3
range 1 10
help
This setting establishes the total count of LWM2M Server instances
available to the client (including: bootstrap and regular servers).
config LWM2M_RD_CLIENT_SUPPORT
bool "support for LWM2M client bootstrap/registration state machine"
default y
help
Client will use registration state machine to locate and connect to
LWM2M servers (including bootstrap server support)
config LWM2M_RD_CLIENT_STACK_SIZE
int "LWM2M RD client stack size"
default 1024
help
Set the stack size for the LWM2M RD client
config LWM2M_RD_CLIENT_INSTANCE_COUNT
int "Maximum # of LWM2M RD client instances"
default 2
help
This setting establishes the total count of LWM2M RD client instances
available.
config LWM2M_PEER_PORT
int "LWM2M server port"
depends on LWM2M_RD_CLIENT_SUPPORT
default 5683
help
This is the server port to connect to for LWM2M communication
config LWM2M_BOOTSTRAP_PORT
int "LWM2M bootstrap port"
depends on LWM2M_RD_CLIENT_SUPPORT
default 5684
help
This is the bootstrap port to connect to for security configuration
config LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT
bool "Firmware Update object support"
default y
help
Include support for LWM2M Firmware Update Object (ID 5)
config LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
bool "Firmware Update object pull support"
default y
depends on LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT
help
Include support for pulling a file from a remote server via
block transfer and "FIRMWARE PACKAGE URI" resource. This option
adds another UDP context and packet handling.
config LWM2M_RW_JSON_SUPPORT
bool "support for JSON writer"
default y
help
Include support for writing JSON data
config LWM2M_DEVICE_ERROR_CODE_MAX
int "Maximum # of device obj error codes to store"
default 10
range 1 20
help
This value sets the maximum number of error codes that the device
object will store before ignoring new values.
endif # LWM2M

View file

@ -0,0 +1,27 @@
# Makefile - LWM2M library
#
# Copyright (c) 2017 Linaro
#
# SPDX-License-Identifier: Apache-2.0
#
ccflags-y += -I$(srctree)/subsys/net/lib/lwm2m
obj-y := \
lwm2m_engine.o \
lwm2m_obj_security.o \
lwm2m_obj_server.o \
lwm2m_obj_device.o \
lwm2m_rw_plain_text.o \
lwm2m_rw_oma_tlv.o
# LWM2M RD Client Support
obj-$(CONFIG_LWM2M_RD_CLIENT_SUPPORT) += lwm2m_rd_client.o
# LWM2M Object Support
obj-$(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT) += lwm2m_obj_firmware.o
obj-$(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT) += lwm2m_obj_firmware_pull.o
# JSON Support
obj-$(CONFIG_LWM2M_RW_JSON_SUPPORT) += lwm2m_rw_json.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,60 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef LWM2M_ENGINE_H
#define LWM2M_ENGINE_H
#include "lwm2m_object.h"
#define ZOAP_RESPONSE_CODE_CLASS(x) (x >> 5)
#define ZOAP_RESPONSE_CODE_DETAIL(x) (x & 0x1F)
/* TODO: */
#define NOTIFY_OBSERVER(o, i, r) lwm2m_notify_observer(o, i, r)
#define NOTIFY_OBSERVER_PATH(path) lwm2m_notify_observer_path(path)
char *lwm2m_sprint_ip_addr(const struct sockaddr *addr);
int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id);
int lwm2m_notify_observer_path(struct lwm2m_obj_path *path);
void lwm2m_register_obj(struct lwm2m_engine_obj *obj);
void lwm2m_unregister_obj(struct lwm2m_engine_obj *obj);
struct lwm2m_engine_obj_field *
lwm2m_get_engine_obj_field(struct lwm2m_engine_obj *obj, int res_id);
int lwm2m_create_obj_inst(u16_t obj_id, u16_t obj_inst_id,
struct lwm2m_engine_obj_inst **obj_inst);
int lwm2m_delete_obj_inst(u16_t obj_id, u16_t obj_inst_id);
int lwm2m_get_or_create_engine_obj(struct lwm2m_engine_context *context,
struct lwm2m_engine_obj_inst **obj_inst,
u8_t *created);
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 zoap_pending *lwm2m_init_message_pending(struct zoap_packet *zpkt,
struct sockaddr *addr,
struct zoap_pending *zpendings,
int num_zpendings);
void lwm2m_init_message_cleanup(struct net_pkt *pkt,
struct zoap_pending *pending,
struct zoap_reply *reply);
u16_t lwm2m_get_rd_data(u8_t *client_data, u16_t size);
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);
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,
int (*udp_request_handler)(struct zoap_packet *request,
struct zoap_packet *response,
struct sockaddr *from_addr));
#endif /* LWM2M_ENGINE_H */

View file

@ -0,0 +1,382 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* TODO:
* - Implement UTC_OFFSET & TIMEZONE
* - Configurable CURRENT_TIME notification delay
*/
#define SYS_LOG_DOMAIN "lwm2m_obj_device"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <string.h>
#include <stdio.h>
#include <init.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
/* Device resource IDs */
#define DEVICE_MANUFACTURER_ID 0
#define DEVICE_MODEL_NUMBER_ID 1
#define DEVICE_SERIAL_NUMBER_ID 2
#define DEVICE_FIRMWARE_VERSION_ID 3
#define DEVICE_REBOOT_ID 4
#define DEVICE_FACTORY_DEFAULT_ID 5
#define DEVICE_AVAILABLE_POWER_SOURCES_ID 6
#define DEVICE_POWER_SOURCE_VOLTAGE_ID 7
#define DEVICE_POWER_SOURCE_CURRENT_ID 8
#define DEVICE_BATTERY_LEVEL_ID 9
#define DEVICE_MEMORY_FREE_ID 10
#define DEVICE_ERROR_CODE_ID 11
#define DEVICE_RESET_ERROR_CODE_ID 12
#define DEVICE_CURRENT_TIME_ID 13
#define DEVICE_UTC_OFFSET_ID 14
#define DEVICE_TIMEZONE_ID 15
#define DEVICE_SUPPORTED_BINDING_MODES_ID 16
#define DEVICE_TYPE_ID 17
#define DEVICE_HARDWARE_VERSION_ID 18
#define DEVICE_SOFTWARE_VERSION_ID 19
#define DEVICE_BATTERY_STATUS_ID 20
#define DEVICE_MEMORY_TOTAL_ID 21
#define DEVICE_MAX_ID 22
#ifdef CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX
#define DEVICE_ERROR_CODE_MAX CONFIG_LWM2M_DEVICE_ERROR_CODE_MAX
#else
#define DEVICE_ERROR_CODE_MAX 10
#endif
#ifdef CONFIG_LWM2M_DEVICE_PWRSRC_MAX
#define DEVICE_PWRSRC_MAX CONFIG_LWM2M_DEVICE_PWRSRC_MAX
#else
#define DEVICE_PWRSRC_MAX 5
#endif
#define DEVICE_STRING_LONG 32
#define DEVICE_STRING_SHORT 8
/* periodic notification thread */
static K_THREAD_STACK_DEFINE(device_thread_stack, 512);
static struct k_thread device_thread_data;
/* resource state variables */
static u8_t manufacturer[DEVICE_STRING_LONG];
static u8_t model_no[DEVICE_STRING_LONG];
static u8_t serial_no[DEVICE_STRING_LONG];
static u8_t firmware_version[DEVICE_STRING_SHORT];
static s8_t pwrsrc_available[DEVICE_PWRSRC_MAX];
static s32_t pwrsrc_voltage_mv[DEVICE_PWRSRC_MAX];
static s32_t pwrsrc_current_ma[DEVICE_PWRSRC_MAX];
static u8_t battery_level;
static s32_t mem_free_kb;
static u8_t error_code_list[DEVICE_ERROR_CODE_MAX];
static s32_t time_temp;
static u32_t time_offset;
static u8_t binding_mode[DEVICE_STRING_SHORT];
static u8_t device_type[DEVICE_STRING_LONG];
static u8_t hardware_version[DEVICE_STRING_LONG];
static u8_t software_version[DEVICE_STRING_LONG];
static u8_t battery_status;
static s32_t mem_total_kb;
static u8_t pwrsrc_count;
static u8_t error_code_count;
/* only 1 instance of device object exists */
static struct lwm2m_engine_obj device;
static struct lwm2m_engine_obj_field fields[] = {
OBJ_FIELD_DATA(DEVICE_MANUFACTURER_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_MODEL_NUMBER_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_SERIAL_NUMBER_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_FIRMWARE_VERSION_ID, R, STRING),
OBJ_FIELD_EXECUTE(DEVICE_REBOOT_ID),
OBJ_FIELD_EXECUTE(DEVICE_FACTORY_DEFAULT_ID),
OBJ_FIELD_MULTI_DATA(DEVICE_AVAILABLE_POWER_SOURCES_ID, R, U8,
DEVICE_PWRSRC_MAX),
OBJ_FIELD_MULTI_DATA(DEVICE_POWER_SOURCE_VOLTAGE_ID, R, S32,
DEVICE_PWRSRC_MAX),
OBJ_FIELD_MULTI_DATA(DEVICE_POWER_SOURCE_CURRENT_ID, R, S32,
DEVICE_PWRSRC_MAX),
OBJ_FIELD_DATA(DEVICE_BATTERY_LEVEL_ID, R, U8),
OBJ_FIELD_DATA(DEVICE_MEMORY_FREE_ID, R, S32),
OBJ_FIELD_MULTI_DATA(DEVICE_ERROR_CODE_ID, R, U8,
DEVICE_ERROR_CODE_MAX),
OBJ_FIELD_EXECUTE(DEVICE_RESET_ERROR_CODE_ID),
OBJ_FIELD_DATA(DEVICE_CURRENT_TIME_ID, RW, TIME),
OBJ_FIELD_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_TYPE_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_HARDWARE_VERSION_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_SOFTWARE_VERSION_ID, R, STRING),
OBJ_FIELD_DATA(DEVICE_BATTERY_STATUS_ID, R, U8),
OBJ_FIELD_DATA(DEVICE_MEMORY_TOTAL_ID, R, S32)
};
static struct lwm2m_engine_obj_inst inst;
static struct lwm2m_engine_res_inst res[DEVICE_MAX_ID];
/* callbacks */
static int reboot_cb(u16_t obj_inst_id)
{
SYS_LOG_DBG("REBOOT");
return -EPERM;
}
static int factory_default_cb(u16_t obj_inst_id)
{
SYS_LOG_DBG("FACTORY_DEFAULT");
return -EPERM;
}
static int reset_error_list_cb(u16_t obj_inst_id)
{
error_code_count = 0;
return 0;
}
static void *current_time_read_cb(u16_t obj_inst_id, size_t *data_len)
{
time_temp = time_offset + (k_uptime_get() / 1000);
*data_len = sizeof(time_temp);
return &time_temp;
}
static void *current_time_pre_write_cb(u16_t obj_inst_id, size_t *data_len)
{
*data_len = sizeof(time_temp);
return &time_temp;
}
static int current_time_post_write_cb(u16_t obj_inst_id,
u8_t *data, u16_t data_len,
bool last_block, size_t total_size)
{
if (data_len == 4) {
time_offset = *(s32_t *)data - (s32_t)(k_uptime_get() / 1000);
return 1;
}
SYS_LOG_ERR("unknown size %u", data_len);
return 0;
}
/* special setter functions */
int lwm2m_device_add_pwrsrc(u8_t pwrsrc_type)
{
int index;
if (pwrsrc_type < 0 || pwrsrc_type >= LWM2M_DEVICE_PWR_SRC_TYPE_MAX) {
SYS_LOG_ERR("power source id %d is invalid",
pwrsrc_type);
return -EINVAL;
}
for (index = 0; index < DEVICE_PWRSRC_MAX; index++) {
if (pwrsrc_available[index] < 0) {
break;
}
}
if (index >= DEVICE_PWRSRC_MAX) {
return -ENOMEM;
}
pwrsrc_available[index] = pwrsrc_type;
pwrsrc_voltage_mv[index] = 0;
pwrsrc_current_ma[index] = 0;
pwrsrc_count++;
NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0,
DEVICE_AVAILABLE_POWER_SOURCES_ID);
return index;
}
/*
* TODO: this will disable the index, but current printing function expects
* all indexes to be in order up to pwrsrc_count
*/
int lwm2m_device_remove_pwrsrc(int index)
{
if (index < 0 || index >= DEVICE_PWRSRC_MAX) {
SYS_LOG_ERR("index is out of range: %d", index);
return -EINVAL;
}
if (pwrsrc_available[index] < 0) {
SYS_LOG_ERR("Power source index %d isn't registered", index);
return -EINVAL;
}
pwrsrc_available[index] = -1;
pwrsrc_voltage_mv[index] = 0;
pwrsrc_current_ma[index] = 0;
pwrsrc_count--;
NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0,
DEVICE_AVAILABLE_POWER_SOURCES_ID);
return 0;
}
int lwm2m_device_set_pwrsrc_voltage_mv(int index, int voltage_mv)
{
if (index < 0 || index >= DEVICE_PWRSRC_MAX) {
SYS_LOG_ERR("index is out of range: %d", index);
return -EINVAL;
}
if (pwrsrc_available[index] < 0) {
SYS_LOG_ERR("Power source index %d isn't registered.", index);
return -EINVAL;
}
pwrsrc_voltage_mv[index] = voltage_mv;
NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0,
DEVICE_POWER_SOURCE_VOLTAGE_ID);
return 0;
}
int lwm2m_device_set_pwrsrc_current_ma(int index, int current_ma)
{
if (index < 0 || index >= DEVICE_PWRSRC_MAX) {
SYS_LOG_ERR("index is out of range: %d", index);
return -EINVAL;
}
if (pwrsrc_available[index] < 0) {
SYS_LOG_ERR("Power source index %d isn't registered.", index);
return -EINVAL;
}
pwrsrc_current_ma[index] = current_ma;
NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0,
DEVICE_POWER_SOURCE_CURRENT_ID);
return 0;
}
/* error code function */
int lwm2m_device_add_err(u8_t error_code)
{
if (error_code_count < DEVICE_ERROR_CODE_MAX) {
error_code_list[error_code_count] = error_code;
error_code_count++;
return 0;
}
return -ENOMEM;
}
static void device_periodic_service(void)
{
while (true) {
/* TODO: make this delay configurable */
k_sleep(K_SECONDS(10));
NOTIFY_OBSERVER(LWM2M_OBJECT_DEVICE_ID, 0,
DEVICE_CURRENT_TIME_ID);
}
}
static struct lwm2m_engine_obj_inst *device_create(u16_t obj_inst_id)
{
int i = 0;
/* initialize instance resource data */
INIT_OBJ_RES_DATA(res, i, DEVICE_MANUFACTURER_ID,
manufacturer, DEVICE_STRING_LONG);
INIT_OBJ_RES_DATA(res, i, DEVICE_MODEL_NUMBER_ID,
model_no, DEVICE_STRING_LONG);
INIT_OBJ_RES_DATA(res, i, DEVICE_SERIAL_NUMBER_ID,
serial_no, DEVICE_STRING_LONG);
INIT_OBJ_RES_DATA(res, i, DEVICE_FIRMWARE_VERSION_ID,
firmware_version, DEVICE_STRING_SHORT);
INIT_OBJ_RES_EXECUTE(res, i, DEVICE_REBOOT_ID, reboot_cb);
INIT_OBJ_RES_EXECUTE(res, i, DEVICE_FACTORY_DEFAULT_ID,
factory_default_cb);
INIT_OBJ_RES_MULTI_DATA(res, i, DEVICE_AVAILABLE_POWER_SOURCES_ID,
&pwrsrc_count, pwrsrc_available,
sizeof(*pwrsrc_available));
INIT_OBJ_RES_MULTI_DATA(res, i, DEVICE_POWER_SOURCE_VOLTAGE_ID,
&pwrsrc_count, pwrsrc_voltage_mv,
sizeof(*pwrsrc_voltage_mv));
INIT_OBJ_RES_MULTI_DATA(res, i, DEVICE_POWER_SOURCE_CURRENT_ID,
&pwrsrc_count, pwrsrc_current_ma,
sizeof(*pwrsrc_current_ma));
INIT_OBJ_RES_DATA(res, i, DEVICE_BATTERY_LEVEL_ID,
&battery_level, sizeof(battery_level));
INIT_OBJ_RES_DATA(res, i, DEVICE_MEMORY_FREE_ID,
&mem_free_kb, sizeof(mem_free_kb));
INIT_OBJ_RES_MULTI_DATA(res, i, DEVICE_ERROR_CODE_ID,
&error_code_count, error_code_list,
sizeof(*error_code_list));
INIT_OBJ_RES_EXECUTE(res, i, DEVICE_RESET_ERROR_CODE_ID,
reset_error_list_cb);
INIT_OBJ_RES(res, i, DEVICE_CURRENT_TIME_ID, 0, NULL, 0,
current_time_read_cb, current_time_pre_write_cb,
current_time_post_write_cb, NULL);
INIT_OBJ_RES_DATA(res, i, DEVICE_SUPPORTED_BINDING_MODES_ID,
binding_mode, DEVICE_STRING_SHORT);
INIT_OBJ_RES_DATA(res, i, DEVICE_TYPE_ID,
device_type, DEVICE_STRING_LONG);
INIT_OBJ_RES_DATA(res, i, DEVICE_HARDWARE_VERSION_ID,
hardware_version, DEVICE_STRING_LONG);
INIT_OBJ_RES_DATA(res, i, DEVICE_SOFTWARE_VERSION_ID,
software_version, DEVICE_STRING_LONG);
INIT_OBJ_RES_DATA(res, i, DEVICE_BATTERY_STATUS_ID,
&battery_status, sizeof(battery_status));
INIT_OBJ_RES_DATA(res, i, DEVICE_MEMORY_TOTAL_ID,
&mem_total_kb, sizeof(mem_total_kb));
inst.resources = res;
inst.resource_count = i;
SYS_LOG_DBG("Create LWM2M device instance: %d", obj_inst_id);
return &inst;
}
static int lwm2m_device_init(struct device *dev)
{
struct lwm2m_engine_obj_inst *obj_inst = NULL;
int ret = 0, i;
/* Set default values */
time_offset = 0;
mem_total_kb = 0;
mem_free_kb = -1;
pwrsrc_count = 0;
error_code_count = 0;
/* currently only support UDP binding mode (no SMS or Queue mode) */
strcpy(binding_mode, "U");
for (i = 0; i < DEVICE_PWRSRC_MAX; i++) {
pwrsrc_available[i] = -1;
}
/* initialize the device field data */
device.obj_id = LWM2M_OBJECT_DEVICE_ID;
device.fields = fields;
device.field_count = sizeof(fields) / sizeof(*fields);
device.max_instance_count = 1;
device.create_cb = device_create;
lwm2m_register_obj(&device);
/* auto create the only instance */
ret = lwm2m_create_obj_inst(LWM2M_OBJECT_DEVICE_ID, 0, &obj_inst);
if (ret < 0) {
SYS_LOG_DBG("Create LWM2M instance 0 error: %d", ret);
}
/* start thread to handle OBSERVER / NOTIFY events */
k_thread_create(&device_thread_data,
&device_thread_stack[0],
K_THREAD_STACK_SIZEOF(device_thread_stack),
(k_thread_entry_t) device_periodic_service,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
return ret;
}
SYS_INIT(lwm2m_device_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* TODO:
* Support PUSH transfer method (from server)
*/
#define SYS_LOG_DOMAIN "lwm2m_obj_firmware"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <net/zoap.h>
#include <string.h>
#include <init.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
/* Firmware resource IDs */
#define FIRMWARE_PACKAGE_ID 0
#define FIRMWARE_PACKAGE_URI_ID 1 /* TODO */
#define FIRMWARE_UPDATE_ID 2
#define FIRMWARE_STATE_ID 3
#define FIRMWARE_UPDATE_RESULT_ID 5
#define FIRMWARE_PACKAGE_NAME_ID 6 /* TODO */
#define FIRMWARE_PACKAGE_VERSION_ID 7 /* TODO */
#define FIRMWARE_UPDATE_PROTO_SUPPORT_ID 8 /* TODO */
#define FIRMWARE_UPDATE_DELIV_METHOD_ID 9
#define FIRMWARE_MAX_ID 10
#define DELIVERY_METHOD_PULL_ONLY 0
#define DELIVERY_METHOD_PUSH_ONLY 1
#define DELIVERY_METHOD_BOTH 2
#define PACKAGE_URI_LEN 255
/* resource state variables */
static u8_t update_state;
static u8_t update_result;
static u8_t delivery_method;
static char package_uri[PACKAGE_URI_LEN];
/* only 1 instance of firmware object exists */
static struct lwm2m_engine_obj firmware;
static struct lwm2m_engine_obj_field fields[] = {
OBJ_FIELD(FIRMWARE_PACKAGE_ID, W, OPAQUE, 0),
OBJ_FIELD(FIRMWARE_PACKAGE_URI_ID, RW, STRING, 0),
OBJ_FIELD_EXECUTE(FIRMWARE_UPDATE_ID),
OBJ_FIELD_DATA(FIRMWARE_STATE_ID, R, U8),
OBJ_FIELD_DATA(FIRMWARE_UPDATE_RESULT_ID, R, U8),
OBJ_FIELD_DATA(FIRMWARE_UPDATE_DELIV_METHOD_ID, R, U8)
};
static struct lwm2m_engine_obj_inst inst;
static struct lwm2m_engine_res_inst res[FIRMWARE_MAX_ID];
static lwm2m_engine_set_data_cb_t write_cb;
#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
extern int lwm2m_firmware_start_transfer(char *package_uri);
#endif
static int package_write_cb(u16_t obj_inst_id,
u8_t *data, u16_t data_len,
bool last_block, size_t total_size)
{
SYS_LOG_DBG("PACKAGE WRITE");
if (write_cb) {
write_cb(obj_inst_id, data, data_len, last_block, total_size);
return 1;
}
return 0;
}
static int package_uri_write_cb(u16_t obj_inst_id,
u8_t *data, u16_t data_len,
bool last_block, size_t total_size)
{
SYS_LOG_DBG("PACKAGE_URI WRITE: %s", package_uri);
#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
lwm2m_firmware_start_transfer(package_uri);
return 1;
#endif
return 0;
}
void lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb)
{
write_cb = cb;
}
lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void)
{
return write_cb;
}
static struct lwm2m_engine_obj_inst *firmware_create(u16_t obj_inst_id)
{
int i = 0;
/* initialize instance resource data */
INIT_OBJ_RES(res, i, FIRMWARE_PACKAGE_ID, 0, NULL, 0,
NULL, NULL, package_write_cb, NULL);
INIT_OBJ_RES(res, i, FIRMWARE_PACKAGE_URI_ID, 0,
package_uri, PACKAGE_URI_LEN,
NULL, NULL, package_uri_write_cb, NULL);
INIT_OBJ_RES_DUMMY(res, i, FIRMWARE_UPDATE_ID);
INIT_OBJ_RES_DATA(res, i, FIRMWARE_STATE_ID,
&update_state, sizeof(update_state));
INIT_OBJ_RES_DATA(res, i, FIRMWARE_UPDATE_RESULT_ID,
&update_result, sizeof(update_result));
INIT_OBJ_RES_DATA(res, i, FIRMWARE_UPDATE_DELIV_METHOD_ID,
&delivery_method, sizeof(delivery_method));
inst.resources = res;
inst.resource_count = i;
SYS_LOG_DBG("Create LWM2M firmware instance: %d", obj_inst_id);
return &inst;
}
static int lwm2m_firmware_init(struct device *dev)
{
struct lwm2m_engine_obj_inst *obj_inst = NULL;
int ret = 0;
/* Set default values */
package_uri[0] = '\0';
update_state = STATE_IDLE;
update_result = RESULT_DEFAULT;
#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
delivery_method = DELIVERY_METHOD_BOTH;
#else
delivery_method = DELIVERY_METHOD_PUSH;
#endif
firmware.obj_id = LWM2M_OBJECT_FIRMWARE_ID;
firmware.fields = fields;
firmware.field_count = sizeof(fields) / sizeof(*fields);
firmware.max_instance_count = 1;
firmware.create_cb = firmware_create;
lwm2m_register_obj(&firmware);
/* auto create the only instance */
ret = lwm2m_create_obj_inst(LWM2M_OBJECT_FIRMWARE_ID, 0, &obj_inst);
if (ret < 0) {
SYS_LOG_DBG("Create LWM2M instance 0 error: %d", ret);
}
return ret;
}
SYS_INIT(lwm2m_firmware_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,322 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* TODO:
* Support PULL transfer method (from server)
*/
#define SYS_LOG_DOMAIN "lwm2m_obj_firmware_pull"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <stdio.h>
#include <net/zoap.h>
#include <net/net_core.h>
#include <net/net_pkt.h>
#include <net/udp.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
#define STATE_IDLE 0
#define STATE_CONNECTING 1
#define PACKAGE_URI_LEN 255
#define BUF_ALLOC_TIMEOUT K_SECONDS(1)
static u8_t transfer_state;
static struct k_work firmware_work;
static char firmware_uri[PACKAGE_URI_LEN];
static struct sockaddr firmware_addr;
static struct net_context *firmware_net_ctx;
static struct k_delayed_work retransmit_work;
#define NUM_PENDINGS CONFIG_LWM2M_ENGINE_MAX_PENDING
#define NUM_REPLIES CONFIG_LWM2M_ENGINE_MAX_REPLIES
static struct zoap_pending pendings[NUM_PENDINGS];
static struct zoap_reply replies[NUM_REPLIES];
static struct zoap_block_context firmware_block_ctx;
static void
firmware_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, NULL);
}
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 = net_context_sendto(pending->pkt, &pending->addr,
NET_SOCKADDR_MAX_SIZE,
NULL, K_NO_WAIT, NULL, NULL);
if (r < 0) {
return;
}
if (!zoap_pending_cycle(pending)) {
zoap_pending_clear(pending);
return;
}
k_delayed_work_submit(&retransmit_work, pending->timeout);
}
static int transfer_request(struct zoap_block_context *ctx,
const u8_t *token, u8_t tkl,
zoap_reply_t reply_cb)
{
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
int ret;
ret = lwm2m_init_message(firmware_net_ctx, &request, &pkt,
ZOAP_TYPE_CON, ZOAP_METHOD_GET,
0, token, tkl);
if (ret) {
goto cleanup;
}
/* hard code URI path here -- should be pulled from package_uri */
ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
"large-create", sizeof("large-create") - 1);
ret = zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
"1", sizeof("1") - 1);
if (ret < 0) {
SYS_LOG_ERR("Error adding URI_QUERY 'large'");
goto cleanup;
}
ret = zoap_add_block2_option(&request, ctx);
if (ret) {
SYS_LOG_ERR("Unable to add block2 option.");
goto cleanup;
}
pending = lwm2m_init_message_pending(&request, &firmware_addr,
pendings, NUM_PENDINGS);
if (!pending) {
ret = -ENOMEM;
goto cleanup;
}
/* set the reply handler */
if (reply_cb) {
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 = reply_cb;
}
/* send request */
ret = net_context_sendto(pkt, &firmware_addr, NET_SOCKADDR_MAX_SIZE,
NULL, 0, NULL, NULL);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&retransmit_work, pending->timeout);
return 0;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
return ret;
}
static int
do_firmware_transfer_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const struct sockaddr *from)
{
int ret;
size_t transfer_offset = 0;
const u8_t *token;
u8_t tkl;
u16_t payload_len;
u8_t *payload;
struct zoap_packet *check_response = (struct zoap_packet *)response;
lwm2m_engine_set_data_cb_t callback;
SYS_LOG_DBG("TRANSFER REPLY");
ret = zoap_update_from_block(check_response, &firmware_block_ctx);
if (ret < 0) {
SYS_LOG_ERR("Error from block update: %d", ret);
return ret;
}
/* TODO: Process incoming data */
payload = zoap_packet_get_payload(check_response, &payload_len);
if (payload_len > 0) {
/* TODO: Determine when to actually advance to next block */
transfer_offset = zoap_next_block(response,
&firmware_block_ctx);
SYS_LOG_DBG("total: %zd, current: %zd",
firmware_block_ctx.total_size,
firmware_block_ctx.current);
/* callback */
callback = lwm2m_firmware_get_write_cb();
if (callback) {
callback(0, payload, payload_len,
transfer_offset == 0,
firmware_block_ctx.total_size);
}
}
/* TODO: Determine actual completion criteria */
if (transfer_offset > 0) {
token = zoap_header_get_token(check_response, &tkl);
ret = transfer_request(&firmware_block_ctx, token, tkl,
do_firmware_transfer_reply_cb);
}
return ret;
}
static void firmware_transfer(struct k_work *work)
{
#if defined(CONFIG_NET_IPV6)
static struct sockaddr_in6 any_addr6 = { .sin6_addr = IN6ADDR_ANY_INIT,
.sin6_family = AF_INET6 };
#endif
#if defined(CONFIG_NET_IPV4)
static struct sockaddr_in any_addr4 = { .sin_addr = INADDR_ANY_INIT,
.sin_family = AF_INET };
#endif
struct net_if *iface;
int ret, port, family;
/* Server Peer IP information */
/* TODO: use parser on firmware_uri to determine IP version + port */
/* TODO: hard code IPv4 + port for now */
port = 5685;
family = AF_INET;
#if defined(CONFIG_NET_IPV6)
if (family == AF_INET6) {
firmware_addr.family = family;
/* HACK: use firmware_uri directly as IP address */
net_addr_pton(firmware_addr.family, firmware_uri,
&net_sin6(&firmware_addr)->sin6_addr);
net_sin6(&firmware_addr)->sin6_port = htons(5685);
}
#endif
#if defined(CONFIG_NET_IPV4)
if (family == AF_INET) {
firmware_addr.family = family;
net_addr_pton(firmware_addr.family, firmware_uri,
&net_sin(&firmware_addr)->sin_addr);
net_sin(&firmware_addr)->sin_port = htons(5685);
}
#endif
ret = net_context_get(firmware_addr.family, SOCK_DGRAM, IPPROTO_UDP,
&firmware_net_ctx);
if (ret) {
NET_ERR("Could not get an UDP context (err:%d)", ret);
return;
}
iface = net_if_get_default();
if (!iface) {
NET_ERR("Could not find default interface");
goto cleanup;
}
#if defined(CONFIG_NET_IPV6)
if (firmware_addr.family == AF_INET6) {
ret = net_context_bind(firmware_net_ctx,
(struct sockaddr *)&any_addr6,
sizeof(any_addr6));
}
#endif
#if defined(CONFIG_NET_IPV4)
if (firmware_addr.family == AF_INET) {
ret = net_context_bind(firmware_net_ctx,
(struct sockaddr *)&any_addr4,
sizeof(any_addr4));
}
#endif
if (ret) {
NET_ERR("Could not bind the UDP context (err:%d)", ret);
goto cleanup;
}
SYS_LOG_DBG("Attached to port: %d", port);
ret = net_context_recv(firmware_net_ctx, firmware_udp_receive, 0, NULL);
if (ret) {
SYS_LOG_ERR("Could not set receive for net context (err:%d)",
ret);
goto cleanup;
}
/* reset block transfer context */
#if defined(CONFIG_NET_L2_BLUETOOTH)
zoap_block_transfer_init(&firmware_block_ctx, ZOAP_BLOCK_64, 0);
#else
zoap_block_transfer_init(&firmware_block_ctx, ZOAP_BLOCK_256, 0);
#endif
transfer_request(&firmware_block_ctx, NULL, 0,
do_firmware_transfer_reply_cb);
return;
cleanup:
if (firmware_net_ctx) {
net_context_put(firmware_net_ctx);
}
}
/* TODO: */
int lwm2m_firmware_cancel_transfer(void)
{
return 0;
}
int lwm2m_firmware_start_transfer(char *package_uri)
{
/* free up old context */
if (firmware_net_ctx) {
net_context_put(firmware_net_ctx);
}
if (transfer_state == STATE_IDLE) {
k_work_init(&firmware_work, firmware_transfer);
k_delayed_work_init(&retransmit_work, retransmit_request);
/* start file transfer work */
strncpy(firmware_uri, package_uri, PACKAGE_URI_LEN - 1);
k_work_submit(&firmware_work);
return 0;
}
return -1;
}

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#define SYS_LOG_DOMAIN "lwm2m_obj_security"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <stdint.h>
#include <init.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
/* Security resource IDs */
#define SECURITY_SERVER_URI_ID 0
#define SECURITY_BOOTSTRAP_FLAG_ID 1
#define SECURITY_MODE_ID 2
#define SECURITY_CLIENT_PK_ID 3
#define SECURITY_SERVER_PK_ID 4
#define SECURITY_SECRET_KEY_ID 5
#define SECURITY_SHORT_SERVER_ID 10
#define SECURITY_MAX_ID 7
#define MAX_INSTANCE_COUNT CONFIG_LWM2M_SECURITY_INSTANCE_COUNT
#define SECURITY_URI_LEN 255
/* resource state variables */
static char security_uri[MAX_INSTANCE_COUNT][SECURITY_URI_LEN];
static bool bootstrap_flag[MAX_INSTANCE_COUNT];
static u8_t security_mode[MAX_INSTANCE_COUNT];
static u16_t short_server_id[MAX_INSTANCE_COUNT];
static struct lwm2m_engine_obj security;
static struct lwm2m_engine_obj_field fields[] = {
OBJ_FIELD_DATA(SECURITY_SERVER_URI_ID, RW, STRING),
OBJ_FIELD_DATA(SECURITY_BOOTSTRAP_FLAG_ID, W, BOOL),
OBJ_FIELD_DATA(SECURITY_MODE_ID, W, U8),
OBJ_FIELD_DATA(SECURITY_CLIENT_PK_ID, W, OPAQUE), /* TODO */
OBJ_FIELD_DATA(SECURITY_SERVER_PK_ID, W, OPAQUE), /* TODO */
OBJ_FIELD_DATA(SECURITY_SECRET_KEY_ID, W, OPAQUE), /* TODO */
OBJ_FIELD_DATA(SECURITY_SHORT_SERVER_ID, W, U16) /* TODO */
};
static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
static struct lwm2m_engine_res_inst res[MAX_INSTANCE_COUNT][SECURITY_MAX_ID];
static struct lwm2m_engine_obj_inst *security_create(u16_t obj_inst_id)
{
int index, i = 0;
/* Check that there is no other instance with this ID */
for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
SYS_LOG_ERR("Can not create instance - "
"already existing: %u", obj_inst_id);
return NULL;
}
}
for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
if (!inst[index].obj) {
break;
}
}
if (index >= MAX_INSTANCE_COUNT) {
SYS_LOG_ERR("Can not create instance - "
"no more room: %u", obj_inst_id);
return NULL;
}
/* default values */
security_uri[index][0] = '\0';
bootstrap_flag[index] = 0;
security_mode[index] = 0;
short_server_id[index] = 0;
/* initialize instance resource data */
INIT_OBJ_RES_DATA(res[index], i, SECURITY_SERVER_URI_ID,
security_uri[index], SECURITY_URI_LEN);
INIT_OBJ_RES_DATA(res[index], i, SECURITY_BOOTSTRAP_FLAG_ID,
&bootstrap_flag[index], sizeof(*bootstrap_flag));
INIT_OBJ_RES_DATA(res[index], i, SECURITY_MODE_ID,
&security_mode[index], sizeof(*security_mode));
/* TODO: */
INIT_OBJ_RES_DUMMY(res[index], i, SECURITY_CLIENT_PK_ID);
INIT_OBJ_RES_DUMMY(res[index], i, SECURITY_SERVER_PK_ID),
INIT_OBJ_RES_DUMMY(res[index], i, SECURITY_SECRET_KEY_ID),
INIT_OBJ_RES_DATA(res[index], i, SECURITY_SHORT_SERVER_ID,
&short_server_id[index], sizeof(*short_server_id));
inst[index].resources = res[index];
inst[index].resource_count = i;
SYS_LOG_DBG("Create LWM2M security instance: %d", obj_inst_id);
return &inst[index];
}
static int lwm2m_security_init(struct device *dev)
{
struct lwm2m_engine_obj_inst *obj_inst = NULL;
int ret = 0;
/* Set default values */
memset(inst, 0, sizeof(*inst) * MAX_INSTANCE_COUNT);
memset(res, 0, sizeof(struct lwm2m_engine_res_inst) *
MAX_INSTANCE_COUNT * SECURITY_MAX_ID);
security.obj_id = LWM2M_OBJECT_SECURITY_ID;
security.fields = fields;
security.field_count = sizeof(fields) / sizeof(*fields);
security.max_instance_count = MAX_INSTANCE_COUNT;
security.create_cb = security_create;
lwm2m_register_obj(&security);
/* auto create the first instance */
ret = lwm2m_create_obj_inst(LWM2M_OBJECT_SECURITY_ID, 0, &obj_inst);
if (ret < 0) {
SYS_LOG_ERR("Create LWM2M security instance 0 error: %d", ret);
}
return ret;
}
SYS_INIT(lwm2m_security_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,184 @@
/*
* Copyright (c) 2017 Linaro Limited
*
* SPDX-License-Identifier: Apache-2.0
*/
#define SYS_LOG_DOMAIN "lwm2m_obj_server"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <stdint.h>
#include <init.h>
#include <net/lwm2m.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
#include "lwm2m_rd_client.h"
#endif
/* Server resource IDs */
#define SERVER_SHORT_SERVER_ID 0
#define SERVER_LIFETIME_ID 1
#define SERVER_DEFAULT_MIN_PERIOD_ID 2
#define SERVER_DEFAULT_MAX_PERIOD_ID 3
#define SERVER_DISABLE_ID 4
#define SERVER_DISABLE_TIMEOUT_ID 5
#define SERVER_STORE_NOTIFY_ID 6
#define SERVER_TRANSPORT_BINDING_ID 7
#define SERVER_REG_UPDATE_TRIGGER_ID 8
#define SERVER_MAX_ID 9
/* Server flags */
#define SERVER_FLAG_DISABLED 1
#define SERVER_FLAG_STORE_NOTIFY 2
#define MAX_INSTANCE_COUNT CONFIG_LWM2M_SERVER_INSTANCE_COUNT
#define TRANSPORT_BINDING_LEN 4
/* resource state variables */
static u16_t server_id[MAX_INSTANCE_COUNT];
static u32_t lifetime[MAX_INSTANCE_COUNT];
static u32_t default_min_period[MAX_INSTANCE_COUNT];
static u32_t default_max_period[MAX_INSTANCE_COUNT];
static u8_t server_flag_disabled[MAX_INSTANCE_COUNT];
static u32_t disabled_timeout[MAX_INSTANCE_COUNT];
static u8_t server_flag_store_notify[MAX_INSTANCE_COUNT];
static char transport_binding[MAX_INSTANCE_COUNT][TRANSPORT_BINDING_LEN];
static struct lwm2m_engine_obj server;
static struct lwm2m_engine_obj_field fields[] = {
OBJ_FIELD_DATA(SERVER_SHORT_SERVER_ID, R, U16),
OBJ_FIELD_DATA(SERVER_LIFETIME_ID, RW, U32),
OBJ_FIELD_DATA(SERVER_DEFAULT_MIN_PERIOD_ID, RW, U32),
OBJ_FIELD_DATA(SERVER_DEFAULT_MAX_PERIOD_ID, RW, U32),
OBJ_FIELD_EXECUTE(SERVER_DISABLE_ID),
OBJ_FIELD_DATA(SERVER_DISABLE_TIMEOUT_ID, RW, U32),
OBJ_FIELD_DATA(SERVER_STORE_NOTIFY_ID, RW, U8),
/* Mark Transport Binding RO as we only support UDP atm */
OBJ_FIELD_DATA(SERVER_TRANSPORT_BINDING_ID, R, STRING),
OBJ_FIELD_EXECUTE(SERVER_REG_UPDATE_TRIGGER_ID),
};
static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
static struct lwm2m_engine_res_inst res[MAX_INSTANCE_COUNT][SERVER_MAX_ID];
static int disable_cb(u16_t obj_inst_id)
{
int i;
SYS_LOG_DBG("DISABLE %d", obj_inst_id);
for (i = 0; i < MAX_INSTANCE_COUNT; i++) {
if (inst[i].obj && inst[i].obj_inst_id == obj_inst_id) {
server_flag_disabled[i] = 1;
return 0;
}
}
return -ENOENT;
}
static int update_trigger_cb(u16_t obj_inst_id)
{
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
engine_trigger_update();
return 0;
#else
return -EPERM;
#endif
}
static struct lwm2m_engine_obj_inst *server_create(u16_t obj_inst_id)
{
int index, i = 0;
/* Check that there is no other instance with this ID */
for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
SYS_LOG_ERR("Can not create instance - "
"already existing: %u", obj_inst_id);
return NULL;
}
}
for (index = 0; index < MAX_INSTANCE_COUNT; index++) {
if (!inst[index].obj) {
break;
}
}
if (index >= MAX_INSTANCE_COUNT) {
SYS_LOG_ERR("Can not create instance - "
"no more room: %u", obj_inst_id);
return NULL;
}
/* Set default values */
server_flag_disabled[index] = 0;
server_flag_store_notify[index] = 0;
server_id[index] = index + 1;
lifetime[index] = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
default_min_period[index] = 0;
default_max_period[index] = 0;
disabled_timeout[index] = 86400;
strcpy(transport_binding[index], "U");
/* initialize instance resource data */
INIT_OBJ_RES_DATA(res[index], i, SERVER_SHORT_SERVER_ID,
&server_id[index], sizeof(*server_id));
INIT_OBJ_RES_DATA(res[index], i, SERVER_LIFETIME_ID,
&lifetime[index], sizeof(*lifetime));
INIT_OBJ_RES_DATA(res[index], i, SERVER_DEFAULT_MIN_PERIOD_ID,
&default_min_period[index],
sizeof(*default_min_period));
INIT_OBJ_RES_DATA(res[index], i, SERVER_DEFAULT_MAX_PERIOD_ID,
&default_max_period[index],
sizeof(*default_max_period));
INIT_OBJ_RES_EXECUTE(res[index], i, SERVER_DISABLE_ID, disable_cb);
INIT_OBJ_RES_DATA(res[index], i, SERVER_DISABLE_TIMEOUT_ID,
&disabled_timeout[index],
sizeof(*disabled_timeout));
INIT_OBJ_RES_DATA(res[index], i, SERVER_STORE_NOTIFY_ID,
&server_flag_store_notify[index],
sizeof(*server_flag_store_notify));
/* Mark Transport Binding RO as we only support UDP atm */
INIT_OBJ_RES_DATA(res[index], i, SERVER_TRANSPORT_BINDING_ID,
transport_binding[index], TRANSPORT_BINDING_LEN);
INIT_OBJ_RES_EXECUTE(res[index], i, SERVER_REG_UPDATE_TRIGGER_ID,
update_trigger_cb);
inst[index].resources = res[index];
inst[index].resource_count = i;
SYS_LOG_DBG("Create LWM2M server instance: %d", obj_inst_id);
return &inst[index];
}
static int lwm2m_server_init(struct device *dev)
{
struct lwm2m_engine_obj_inst *obj_inst = NULL;
int ret = 0;
/* Set default values */
memset(inst, 0, sizeof(*inst) * MAX_INSTANCE_COUNT);
memset(res, 0, sizeof(struct lwm2m_engine_res_inst) *
MAX_INSTANCE_COUNT * SERVER_MAX_ID);
server.obj_id = LWM2M_OBJECT_SERVER_ID;
server.fields = fields;
server.field_count = sizeof(fields) / sizeof(*fields);
server.max_instance_count = MAX_INSTANCE_COUNT;
server.create_cb = server_create;
lwm2m_register_obj(&server);
/* auto create the first instance */
ret = lwm2m_create_obj_inst(LWM2M_OBJECT_SERVER_ID, 0, &obj_inst);
if (ret < 0) {
SYS_LOG_ERR("Create LWM2M server instance 0 error: %d", ret);
}
return ret;
}
SYS_INIT(lwm2m_server_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,408 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Authors:
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef LWM2M_OBJECT_H_
#define LWM2M_OBJECT_H_
/* stdint conversions */
#include <zephyr/types.h>
#include <stddef.h>
#include <net/net_ip.h>
#include <net/zoap.h>
#include <net/lwm2m.h>
#include <misc/printk.h>
#include <kernel.h>
/* #####/###/#####/### + NULL */
#define MAX_RESOURCE_LEN 20
/* operations */
#define LWM2M_OP_NONE 0
#define LWM2M_OP_READ 1
#define LWM2M_OP_DISCOVER 2
#define LWM2M_OP_WRITE 3
#define LWM2M_OP_WRITE_ATTR 4
#define LWM2M_OP_EXECUTE 5
#define LWM2M_OP_DELETE 6
/* operation permission bits */
#define LWM2M_OP_BIT(op) (1 << (op - 1))
/* resource permissions */
#define LWM2M_PERM_R LWM2M_OP_BIT(LWM2M_OP_READ)
#define LWM2M_PERM_W LWM2M_OP_BIT(LWM2M_OP_WRITE)
#define LWM2M_PERM_X LWM2M_OP_BIT(LWM2M_OP_EXECUTE)
#define LWM2M_PERM_RW (LWM2M_OP_BIT(LWM2M_OP_READ) | \
LWM2M_OP_BIT(LWM2M_OP_WRITE))
#define LWM2M_PERM_RWX (LWM2M_OP_BIT(LWM2M_OP_READ) | \
LWM2M_OP_BIT(LWM2M_OP_WRITE) | \
LWM2M_OP_BIT(LWM2M_OP_EXECUTE))
/* resource types */
#define LWM2M_RES_TYPE_NONE 0
#define LWM2M_RES_TYPE_OPAQUE 1
#define LWM2M_RES_TYPE_STRING 2
#define LWM2M_RES_TYPE_UINT64 3
#define LWM2M_RES_TYPE_U64 3
#define LWM2M_RES_TYPE_UINT 4
#define LWM2M_RES_TYPE_U32 4
#define LWM2M_RES_TYPE_U16 5
#define LWM2M_RES_TYPE_U8 6
#define LWM2M_RES_TYPE_INT64 7
#define LWM2M_RES_TYPE_S64 7
#define LWM2M_RES_TYPE_INT 8
#define LWM2M_RES_TYPE_S32 8
#define LWM2M_RES_TYPE_S16 9
#define LWM2M_RES_TYPE_S8 10
#define LWM2M_RES_TYPE_BOOL 11
#define LWM2M_RES_TYPE_TIME 12
#define LWM2M_RES_TYPE_FLOAT32 13
#define LWM2M_RES_TYPE_FLOAT64 14
/* remember that we have already output a value - can be between two block's */
#define WRITER_OUTPUT_VALUE 1
#define WRITER_RESOURCE_INSTANCE 2
struct lwm2m_engine_obj;
struct lwm2m_engine_context;
/* path representing object instances */
struct lwm2m_obj_path {
u16_t obj_id;
u16_t obj_inst_id;
u16_t res_id;
u16_t res_inst_id;
u8_t level; /* 0/1/2/3 = 3 = resource */
};
#define OBJ_FIELD(res_id, perm, type, multi_max) \
{ res_id, LWM2M_PERM_ ## perm, LWM2M_RES_TYPE_ ## type, multi_max }
#define OBJ_FIELD_DATA(res_id, perm, type) \
OBJ_FIELD(res_id, perm, type, 1)
#define OBJ_FIELD_MULTI_DATA(res_id, perm, type, multi_max) \
OBJ_FIELD(res_id, perm, type, multi_max)
#define OBJ_FIELD_EXECUTE(res_id) \
OBJ_FIELD(res_id, X, NONE, 0)
struct lwm2m_engine_obj_field {
u16_t res_id;
u8_t permissions;
u8_t data_type;
u8_t multi_max_count;
};
typedef struct lwm2m_engine_obj_inst *
(*lwm2m_engine_obj_create_cb_t)(u16_t obj_inst_id);
typedef int (*lwm2m_engine_obj_delete_cb_t)(u16_t obj_inst_id);
struct lwm2m_engine_obj {
sys_snode_t node;
u16_t obj_id;
struct lwm2m_engine_obj_field *fields;
u16_t field_count;
u16_t instance_count;
u16_t max_instance_count;
lwm2m_engine_obj_create_cb_t create_cb;
lwm2m_engine_obj_delete_cb_t delete_cb;
};
#define INIT_OBJ_RES(res_var, index_var, id_val, multi_var, \
data_val, data_val_len, r_cb, pre_w_cb, post_w_cb, ex_cb) \
res_var[index_var].res_id = id_val; \
res_var[index_var].multi_count_var = multi_var; \
res_var[index_var].data_ptr = data_val; \
res_var[index_var].data_len = data_val_len; \
res_var[index_var].read_cb = r_cb; \
res_var[index_var].pre_write_cb = pre_w_cb; \
res_var[index_var].post_write_cb = post_w_cb; \
res_var[index_var++].execute_cb = ex_cb
#define INIT_OBJ_RES_MULTI_DATA(res_var, index_var, id_val, multi_var, \
data_val, data_val_len) \
INIT_OBJ_RES(res_var, index_var, id_val, multi_var, data_val, \
data_val_len, NULL, NULL, NULL, NULL)
#define INIT_OBJ_RES_DATA(res_var, index_var, id_val, data_val, data_val_len) \
INIT_OBJ_RES_MULTI_DATA(res_var, index_var, id_val, NULL, \
data_val, data_val_len)
#define INIT_OBJ_RES_DUMMY(res_var, index_var, id_val) \
INIT_OBJ_RES_MULTI_DATA(res_var, index_var, id_val, NULL, NULL, 0)
#define INIT_OBJ_RES_EXECUTE(res_var, index_var, id_val, ex_cb) \
INIT_OBJ_RES(res_var, index_var, id_val, NULL, NULL, 0, \
NULL, NULL, NULL, ex_cb)
struct lwm2m_engine_res_inst {
char path[MAX_RESOURCE_LEN]; /* 3/0/0 */
u16_t res_id;
u8_t *multi_count_var;
void *data_ptr;
size_t data_len;
/* runtime field attributes (WRITE_ATTR) */
/* callbacks set by user code on obj instance */
lwm2m_engine_get_data_cb_t read_cb;
lwm2m_engine_get_data_cb_t pre_write_cb;
lwm2m_engine_set_data_cb_t post_write_cb;
lwm2m_engine_exec_cb_t execute_cb;
};
struct lwm2m_engine_obj_inst {
sys_snode_t node;
char path[MAX_RESOURCE_LEN]; /* 3/0 */
struct lwm2m_engine_obj *obj;
u16_t obj_inst_id;
struct lwm2m_engine_res_inst *resources;
u16_t resource_count;
};
struct lwm2m_output_context {
struct zoap_packet *out_zpkt;
u8_t writer_flags; /* flags for reader/writer */
u8_t *outbuf;
u16_t outsize;
u32_t outlen;
u8_t mark_pos_ri; /* mark pos for last resource instance */
const struct lwm2m_writer *writer;
};
struct lwm2m_input_context {
struct zoap_packet *in_zpkt;
u8_t *inbuf;
u16_t insize;
s32_t inpos;
u16_t last_value_len;
const struct lwm2m_reader *reader;
};
/* LWM2M format writer for the various formats supported */
struct lwm2m_writer {
size_t (*put_begin)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path);
size_t (*put_end)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path);
size_t (*put_begin_ri)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path);
size_t (*put_end_ri)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path);
size_t (*put_s8)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s8_t value);
size_t (*put_s16)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s16_t value);
size_t (*put_s32)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s32_t value);
size_t (*put_s64)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s64_t value);
size_t (*put_string)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
const char *value, size_t strlen);
size_t (*put_float32fix)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float32_value_t *value);
size_t (*put_float64fix)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float64_value_t *value);
size_t (*put_bool)(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
bool value);
};
struct lwm2m_reader {
size_t (*get_s32)(struct lwm2m_input_context *in,
s32_t *value);
size_t (*get_s64)(struct lwm2m_input_context *in,
s64_t *value);
size_t (*get_string)(struct lwm2m_input_context *in,
u8_t *value, size_t strlen);
size_t (*get_float32fix)(struct lwm2m_input_context *in,
float32_value_t *value);
size_t (*get_float64fix)(struct lwm2m_input_context *in,
float64_value_t *value);
size_t (*get_bool)(struct lwm2m_input_context *in,
bool *value);
};
/* LWM2M engine context */
struct lwm2m_engine_context {
struct lwm2m_input_context *in;
struct lwm2m_output_context *out;
struct lwm2m_obj_path *path;
u8_t operation;
};
/* inline multi-format write / read functions */
static inline size_t engine_put_begin(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
if (out->writer->put_begin) {
return out->writer->put_begin(out, path);
}
return 0;
}
static inline size_t engine_put_end(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
if (out->writer->put_end) {
return out->writer->put_end(out, path);
}
return 0;
}
static inline size_t engine_put_begin_ri(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
if (out->writer->put_begin_ri) {
return out->writer->put_begin_ri(out, path);
}
return 0;
}
static inline size_t engine_put_end_ri(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
if (out->writer->put_end_ri) {
return out->writer->put_end_ri(out, path);
}
return 0;
}
static inline size_t engine_put_s8(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s8_t value)
{
return out->writer->put_s8(out, path, value);
}
static inline size_t engine_put_s16(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s16_t value)
{
return out->writer->put_s16(out, path, value);
}
static inline size_t engine_put_s32(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s32_t value)
{
return out->writer->put_s32(out, path, value);
}
static inline size_t engine_put_s64(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
s64_t value)
{
return out->writer->put_s64(out, path, value);
}
static inline size_t engine_put_string(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
const char *value, size_t strlen)
{
return out->writer->put_string(out, path, value, strlen);
}
static inline size_t engine_put_float32fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float32_value_t *value)
{
return out->writer->put_float32fix(out, path, value);
}
static inline size_t engine_put_float64fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float64_value_t *value)
{
return out->writer->put_float64fix(out, path, value);
}
static inline size_t engine_put_bool(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
bool value)
{
return out->writer->put_bool(out, path, value);
}
static inline size_t engine_get_s32(struct lwm2m_input_context *in,
s32_t *value)
{
return in->reader->get_s32(in, value);
}
static inline size_t engine_get_s64(struct lwm2m_input_context *in,
s64_t *value)
{
return in->reader->get_s64(in, value);
}
static inline size_t engine_get_string(struct lwm2m_input_context *in,
u8_t *value, size_t strlen)
{
return in->reader->get_string(in, value, strlen);
}
static inline size_t engine_get_float32fix(struct lwm2m_input_context *in,
float32_value_t *value)
{
return in->reader->get_float32fix(in, value);
}
static inline size_t engine_get_float64fix(struct lwm2m_input_context *in,
float64_value_t *value)
{
return in->reader->get_float64fix(in, value);
}
static inline size_t engine_get_bool(struct lwm2m_input_context *in,
bool *value)
{
return in->reader->get_bool(in, value);
}
#endif /* LWM2M_OBJECT_H_ */

View file

@ -0,0 +1,877 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original authors:
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
* Joel Hoglund <joel@sics.se>
*/
#define SYS_LOG_DOMAIN "lib/lwm2m_rd_client"
#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 <errno.h>
#include <init.h>
#include <misc/printk.h>
#include <net/net_pkt.h>
#include <net/zoap.h>
#include <net/lwm2m.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
#define LWM2M_RD_CLIENT_URI "rd"
#define SECONDS_TO_UPDATE_EARLY 2
#define STATE_MACHINE_UPDATE_INTERVAL 500
#define LWM2M_PEER_PORT CONFIG_LWM2M_PEER_PORT
#define LWM2M_BOOTSTRAP_PORT CONFIG_LWM2M_BOOTSTRAP_PORT
/* Leave room for 32 hexadeciaml digits (UUID) + NULL */
#define CLIENT_EP_LEN 33
/* The states for the RD client state machine */
/*
* When node is unregistered it ends up in UNREGISTERED
* and this is going to be there until use X or Y kicks it
* back into INIT again
*/
enum sm_engine_state {
ENGINE_INIT,
ENGINE_DO_BOOTSTRAP,
ENGINE_BOOTSTRAP_SENT,
ENGINE_BOOTSTRAP_DONE,
ENGINE_DO_REGISTRATION,
ENGINE_REGISTRATION_SENT,
ENGINE_REGISTRATION_DONE,
ENGINE_UPDATE_SENT,
ENGINE_DEREGISTER,
ENGINE_DEREGISTER_SENT,
ENGINE_DEREGISTER_FAILED,
ENGINE_DEREGISTERED
};
struct lwm2m_rd_client_info {
u16_t lifetime;
struct net_context *net_ctx;
struct sockaddr bs_server;
struct sockaddr reg_server;
u8_t engine_state;
u8_t use_bootstrap;
u8_t has_bs_server_info;
u8_t use_registration;
u8_t has_registration_info;
u8_t registered;
u8_t bootstrapped; /* bootstrap done */
u8_t trigger_update;
s64_t last_update;
char ep_name[CLIENT_EP_LEN];
char server_ep[CLIENT_EP_LEN];
};
static K_THREAD_STACK_DEFINE(lwm2m_rd_client_thread_stack,
CONFIG_LWM2M_RD_CLIENT_STACK_SIZE);
struct k_thread lwm2m_rd_client_thread_data;
/* HACK: remove when engine transactions are ready */
#define NUM_PENDINGS CONFIG_LWM2M_ENGINE_MAX_PENDING
#define NUM_REPLIES CONFIG_LWM2M_ENGINE_MAX_REPLIES
extern struct zoap_pending pendings[NUM_PENDINGS];
extern struct zoap_reply replies[NUM_REPLIES];
extern struct k_delayed_work retransmit_work;
/* buffers */
static char query_buffer[64]; /* allocate some data for queries and updates */
static u8_t client_data[256]; /* allocate some data for the RD */
#define CLIENT_INSTANCE_COUNT CONFIG_LWM2M_RD_CLIENT_INSTANCE_COUNT
static struct lwm2m_rd_client_info clients[CLIENT_INSTANCE_COUNT];
static int client_count;
static void set_sm_state(int index, u8_t state)
{
/* TODO: add locking? */
clients[index].engine_state = state;
}
static u8_t get_sm_state(int index)
{
/* TODO: add locking? */
return clients[index].engine_state;
}
static int find_clients_index(const struct sockaddr *addr,
bool check_bs_server)
{
int index = -1, i;
for (i = 0; i < client_count; i++) {
if (check_bs_server) {
if (memcmp(addr, &clients[i].bs_server,
sizeof(addr)) == 0) {
index = i;
break;
}
} else {
if (memcmp(addr, &clients[i].reg_server,
sizeof(addr)) == 0) {
index = i;
break;
}
}
}
return index;
}
/* force re-update with remote peer(s) */
void engine_trigger_update(void)
{
int index;
for (index = 0; index < client_count; index++) {
/* TODO: add locking? */
clients[index].trigger_update = 1;
}
}
/* state machine reply callbacks */
static int do_bootstrap_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const struct sockaddr *from)
{
u8_t code;
int index;
code = zoap_header_get_code(response);
SYS_LOG_DBG("Bootstrap callback (code:%u.%u)",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
index = find_clients_index(from, true);
if (index < 0) {
SYS_LOG_ERR("Bootstrap client index not found.");
return 0;
}
if (code == ZOAP_RESPONSE_CODE_CHANGED) {
SYS_LOG_DBG("Considered done!");
set_sm_state(index, ENGINE_BOOTSTRAP_DONE);
} else if (code == ZOAP_RESPONSE_CODE_NOT_FOUND) {
SYS_LOG_ERR("Failed: NOT_FOUND. Not Retrying.");
set_sm_state(index, ENGINE_DO_REGISTRATION);
} else {
/* TODO: Read payload for error message? */
SYS_LOG_ERR("Failed with code %u.%u. Retrying ...",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
set_sm_state(index, ENGINE_INIT);
}
return 0;
}
static int do_registration_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const struct sockaddr *from)
{
struct zoap_option options[2];
u8_t code;
int ret, index;
code = zoap_header_get_code(response);
SYS_LOG_DBG("Registration callback (code:%u.%u)",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
index = find_clients_index(from, false);
if (index < 0) {
SYS_LOG_ERR("Registration client index not found.");
return 0;
}
/* check state and possibly set registration to done */
if (code == ZOAP_RESPONSE_CODE_CREATED) {
ret = zoap_find_options(response, ZOAP_OPTION_LOCATION_PATH,
options, 2);
if (ret < 0) {
return ret;
}
if (ret < 2) {
SYS_LOG_ERR("Unexpected endpoint data returned.");
return -EINVAL;
}
/* option[0] should be "rd" */
if (options[1].len + 1 > sizeof(clients[index].server_ep)) {
SYS_LOG_ERR("Unexpected length of query: "
"%u (expected %zu)\n",
options[1].len,
sizeof(clients[index].server_ep));
return -EINVAL;
}
memcpy(clients[index].server_ep, options[1].value,
options[1].len);
clients[index].server_ep[options[1].len] = '\0';
set_sm_state(index, ENGINE_REGISTRATION_DONE);
clients[index].registered = 1;
SYS_LOG_INF("Registration Done (EP='%s')",
clients[index].server_ep);
return 0;
} else if (code == ZOAP_RESPONSE_CODE_NOT_FOUND) {
SYS_LOG_ERR("Failed: NOT_FOUND. Not Retrying.");
set_sm_state(index, ENGINE_REGISTRATION_DONE);
return 0;
}
/* TODO: Read payload for error message? */
/* Possible error response codes: 4.00 Bad request & 4.03 Forbidden */
SYS_LOG_ERR("failed with code %u.%u. Re-init network",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
set_sm_state(index, ENGINE_INIT);
return 0;
}
static int do_update_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const struct sockaddr *from)
{
u8_t code;
int index;
code = zoap_header_get_code(response);
SYS_LOG_DBG("Update callback (code:%u.%u)",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
index = find_clients_index(from, false);
if (index < 0) {
SYS_LOG_ERR("Registration client index not found.");
return 0;
}
/* If NOT_FOUND just continue on */
if ((code == ZOAP_RESPONSE_CODE_CHANGED) ||
(code == ZOAP_RESPONSE_CODE_CREATED)) {
set_sm_state(index, ENGINE_REGISTRATION_DONE);
SYS_LOG_DBG("Update Done");
return 0;
}
/* TODO: Read payload for error message? */
/* Possible error response codes: 4.00 Bad request & 4.04 Not Found */
SYS_LOG_ERR("Failed with code %u.%u. Retrying registration",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
clients[index].registered = 0;
set_sm_state(index, ENGINE_DO_REGISTRATION);
return 0;
}
static int do_deregister_reply_cb(const struct zoap_packet *response,
struct zoap_reply *reply,
const struct sockaddr *from)
{
u8_t code;
int index;
code = zoap_header_get_code(response);
SYS_LOG_DBG("Deregister callback (code:%u.%u)",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
index = find_clients_index(from, false);
if (index < 0) {
SYS_LOG_ERR("Registration clients index not found.");
return 0;
}
if (code == ZOAP_RESPONSE_CODE_DELETED) {
clients[index].registered = 0;
SYS_LOG_DBG("Deregistration success");
set_sm_state(index, ENGINE_DEREGISTERED);
} else {
SYS_LOG_ERR("failed with code %u.%u",
ZOAP_RESPONSE_CODE_CLASS(code),
ZOAP_RESPONSE_CODE_DETAIL(code));
if (get_sm_state(index) == ENGINE_DEREGISTER_SENT) {
set_sm_state(index, ENGINE_DEREGISTER_FAILED);
}
}
return 0;
}
/* state machine step functions */
static int sm_do_init(int index)
{
SYS_LOG_DBG("RD Client started with endpoint "
"'%s' and client lifetime %d",
clients[index].ep_name,
clients[index].lifetime);
/* Zephyr has joined network already */
clients[index].has_registration_info = 1;
clients[index].registered = 0;
clients[index].bootstrapped = 0;
clients[index].trigger_update = 0;
#if defined(CONFIG_LWM2M_BOOTSTRAP_SERVER)
clients[index].use_bootstrap = 1;
#else
clients[index].use_registration = 1;
#endif
if (clients[index].lifetime == 0) {
clients[index].lifetime = CONFIG_LWM2M_ENGINE_DEFAULT_LIFETIME;
}
/* Do bootstrap or registration */
if (clients[index].use_bootstrap) {
set_sm_state(index, ENGINE_DO_BOOTSTRAP);
} else {
set_sm_state(index, ENGINE_DO_REGISTRATION);
}
return 0;
}
static int sm_do_bootstrap(int index)
{
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
int ret = 0;
if (clients[index].use_bootstrap &&
clients[index].bootstrapped == 0 &&
clients[index].has_bs_server_info) {
ret = lwm2m_init_message(clients[index].net_ctx,
&request, &pkt, ZOAP_TYPE_CON,
ZOAP_METHOD_POST, 0, NULL, 0);
if (ret) {
goto cleanup;
}
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
"bs", strlen("bs"));
snprintf(query_buffer, sizeof(query_buffer) - 1,
"ep=%s", clients[index].ep_name);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
pending = lwm2m_init_message_pending(&request,
&clients[index].bs_server,
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 = do_bootstrap_reply_cb;
/* log the bootstrap attempt */
SYS_LOG_DBG("Register ID with bootstrap server [%s] as '%s'",
lwm2m_sprint_ip_addr(&clients[index].bs_server),
query_buffer);
ret = net_context_sendto(pkt, &clients[index].bs_server,
NET_SOCKADDR_MAX_SIZE,
NULL, 0, NULL, NULL);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&retransmit_work, pending->timeout);
set_sm_state(index, ENGINE_BOOTSTRAP_SENT);
}
return ret;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
return ret;
}
static int sm_bootstrap_done(int index)
{
/* TODO: Fix this */
/* check that we should still use bootstrap */
if (clients[index].use_bootstrap) {
#ifdef CONFIG_LWM2M_SECURITY_OBJ_SUPPORT
int i;
SYS_LOG_DBG("*** Bootstrap - checking for server info ...");
/* get the server URI */
if (sec_data->server_uri_len > 0) {
/* TODO: Write endpoint parsing function */
#if 0
if (!parse_endpoint(sec_data->server_uri,
sec_data->server_uri_len,
&clients[index].reg_server)) {
#else
if (true) {
#endif
SYS_LOG_ERR("Failed to parse URI!");
} else {
clients[index].has_registration_info = 1;
clients[index].registered = 0;
clients[index].bootstrapped++;
}
} else {
SYS_LOG_ERR("** failed to parse URI");
}
/* if we did not register above - then fail this and restart */
if (clients[index].bootstrapped == 0) {
/* Not ready - Retry with the bootstrap server again */
set_sm_state(index, ENGINE_DO_BOOTSTRAP);
} else {
set_sm_state(index, ENGINE_DO_REGISTRATION);
}
} else {
#endif
set_sm_state(index, ENGINE_DO_REGISTRATION);
}
return 0;
}
static int sm_send_registration(int index, bool send_obj_support_data,
zoap_reply_t reply_cb)
{
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
u8_t *payload;
u16_t client_data_len, len;
int ret = 0;
/* remember the last reg time */
clients[index].last_update = k_uptime_get();
ret = lwm2m_init_message(clients[index].net_ctx,
&request, &pkt, ZOAP_TYPE_CON,
ZOAP_METHOD_POST, 0, NULL, 0);
if (ret) {
goto cleanup;
}
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
LWM2M_RD_CLIENT_URI,
strlen(LWM2M_RD_CLIENT_URI));
if (!clients[index].registered) {
/* include client endpoint in URI QUERY on 1st registration */
snprintf(query_buffer, sizeof(query_buffer) - 1,
"ep=%s", clients[index].ep_name);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
} else {
/* include server endpoint in URI PATH otherwise */
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
clients[index].server_ep,
strlen(clients[index].server_ep));
}
snprintf(query_buffer, sizeof(query_buffer) - 1,
"lt=%d", clients[index].lifetime);
zoap_add_option(&request, ZOAP_OPTION_URI_QUERY,
query_buffer, strlen(query_buffer));
/* TODO: add supported binding query string */
if (send_obj_support_data) {
/* generate the rd data */
client_data_len = lwm2m_get_rd_data(client_data,
sizeof(client_data));
payload = zoap_packet_get_payload(&request, &len);
if (!payload) {
ret = -EINVAL;
goto cleanup;
}
memcpy(payload, client_data, client_data_len);
ret = zoap_packet_set_used(&request, client_data_len);
if (ret) {
goto cleanup;
}
}
pending = lwm2m_init_message_pending(&request,
&clients[index].reg_server,
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 = reply_cb;
/* log the registration attempt */
SYS_LOG_DBG("registration sent [%s]",
lwm2m_sprint_ip_addr(&clients[index].reg_server));
ret = net_context_sendto(pkt, &clients[index].reg_server,
NET_SOCKADDR_MAX_SIZE,
NULL, 0, NULL, NULL);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&retransmit_work, pending->timeout);
return ret;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
return ret;
}
static int sm_do_registration(int index)
{
int ret = 0;
if (clients[index].use_registration &&
!clients[index].registered &&
clients[index].has_registration_info) {
ret = sm_send_registration(index, true,
do_registration_reply_cb);
if (!ret) {
set_sm_state(index, ENGINE_REGISTRATION_SENT);
} else {
SYS_LOG_ERR("Registration err: %d", ret);
}
}
return ret;
}
static int sm_registration_done(int index)
{
int ret = 0;
bool forced_update;
/* check for lifetime seconds - 1 so that we can update early */
if (clients[index].registered &&
(clients[index].trigger_update ||
((clients[index].lifetime - SECONDS_TO_UPDATE_EARLY) <=
(k_uptime_get() - clients[index].last_update) / 1000))) {
forced_update = clients[index].trigger_update;
clients[index].trigger_update = 0;
ret = sm_send_registration(index, forced_update,
do_update_reply_cb);
if (!ret) {
set_sm_state(index, ENGINE_UPDATE_SENT);
} else {
SYS_LOG_ERR("Registration update err: %d", ret);
}
}
return ret;
}
static int sm_do_deregister(int index)
{
struct zoap_packet request;
struct net_pkt *pkt = NULL;
struct zoap_pending *pending = NULL;
struct zoap_reply *reply = NULL;
int ret;
ret = lwm2m_init_message(clients[index].net_ctx,
&request, &pkt, ZOAP_TYPE_CON,
ZOAP_METHOD_DELETE, 0, NULL, 0);
if (ret) {
goto cleanup;
}
zoap_add_option(&request, ZOAP_OPTION_URI_PATH,
clients[index].server_ep,
strlen(clients[index].server_ep));
pending = lwm2m_init_message_pending(&request,
&clients[index].reg_server,
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 = do_deregister_reply_cb;
SYS_LOG_INF("Deregister from '%s'", clients[index].server_ep);
ret = net_context_sendto(pkt, &clients[index].reg_server,
NET_SOCKADDR_MAX_SIZE,
NULL, 0, NULL, NULL);
if (ret < 0) {
SYS_LOG_ERR("Error sending LWM2M packet (err:%d).",
ret);
goto cleanup;
}
zoap_pending_cycle(pending);
k_delayed_work_submit(&retransmit_work, pending->timeout);
set_sm_state(index, ENGINE_DEREGISTER_SENT);
return ret;
cleanup:
lwm2m_init_message_cleanup(pkt, pending, reply);
return ret;
}
static void lwm2m_rd_client_service(void)
{
int index;
while (true) {
for (index = 0; index < client_count; index++) {
switch (get_sm_state(index)) {
case ENGINE_INIT:
sm_do_init(index);
break;
case ENGINE_DO_BOOTSTRAP:
sm_do_bootstrap(index);
break;
case ENGINE_BOOTSTRAP_SENT:
/* wait for bootstrap to be done */
break;
case ENGINE_BOOTSTRAP_DONE:
sm_bootstrap_done(index);
break;
case ENGINE_DO_REGISTRATION:
sm_do_registration(index);
break;
case ENGINE_REGISTRATION_SENT:
/* wait registration to be done */
break;
case ENGINE_REGISTRATION_DONE:
sm_registration_done(index);
break;
case ENGINE_UPDATE_SENT:
/* wait update to be done */
break;
case ENGINE_DEREGISTER:
sm_do_deregister(index);
break;
case ENGINE_DEREGISTER_SENT:
break;
case ENGINE_DEREGISTER_FAILED:
break;
case ENGINE_DEREGISTERED:
break;
default:
SYS_LOG_ERR("Unhandled state: %d",
get_sm_state(index));
}
k_yield();
}
/*
* TODO: calculate the diff between the start of the loop
* and subtract that from the update interval
*/
k_sleep(K_MSEC(STATE_MACHINE_UPDATE_INTERVAL));
}
}
static bool peer_addr_exist(struct sockaddr *peer_addr)
{
bool ret = false;
int i;
/* look for duplicate peer_addr */
for (i = 0; i < client_count; i++) {
#if defined(CONFIG_NET_IPV6)
if (peer_addr->family == AF_INET6 && net_ipv6_addr_cmp(
&net_sin6(&clients[i].bs_server)->sin6_addr,
&net_sin6(peer_addr)->sin6_addr)) {
ret = true;
break;
}
if (peer_addr->family == AF_INET6 && net_ipv6_addr_cmp(
&net_sin6(&clients[i].reg_server)->sin6_addr,
&net_sin6(peer_addr)->sin6_addr)) {
ret = true;
break;
}
#endif
#if defined(CONFIG_NET_IPV4)
if (peer_addr->family == AF_INET && net_ipv4_addr_cmp(
&net_sin(&clients[i].bs_server)->sin_addr,
&net_sin(peer_addr)->sin_addr)) {
ret = true;
break;
}
if (peer_addr->family == AF_INET && net_ipv4_addr_cmp(
&net_sin(&clients[i].reg_server)->sin_addr,
&net_sin(peer_addr)->sin_addr)) {
ret = true;
break;
}
#endif
}
return ret;
}
static void set_ep_ports(int index)
{
#if defined(CONFIG_NET_IPV6)
if (clients[index].bs_server.family == AF_INET6) {
net_sin6(&clients[index].bs_server)->sin6_port =
htons(LWM2M_BOOTSTRAP_PORT);
}
if (clients[index].reg_server.family == AF_INET6) {
net_sin6(&clients[index].reg_server)->sin6_port =
htons(LWM2M_PEER_PORT);
}
#endif
#if defined(CONFIG_NET_IPV4)
if (clients[index].bs_server.family == AF_INET) {
net_sin(&clients[index].bs_server)->sin_port =
htons(LWM2M_BOOTSTRAP_PORT);
}
if (clients[index].reg_server.family == AF_INET) {
net_sin(&clients[index].reg_server)->sin_port =
htons(LWM2M_PEER_PORT);
}
#endif
}
int lwm2m_rd_client_start(struct net_context *net_ctx,
struct sockaddr *peer_addr,
const char *ep_name)
{
int index;
if (client_count + 1 > CLIENT_INSTANCE_COUNT) {
return -ENOMEM;
}
if (peer_addr_exist(peer_addr)) {
return -EEXIST;
}
/* Server Peer IP information */
/* TODO: use server URI data from security */
index = client_count;
client_count++;
clients[index].net_ctx = net_ctx;
memcpy(&clients[index].reg_server, peer_addr, sizeof(struct sockaddr));
memcpy(&clients[index].bs_server, peer_addr, sizeof(struct sockaddr));
set_ep_ports(index);
set_sm_state(index, ENGINE_INIT);
strncpy(clients[index].ep_name, ep_name, CLIENT_EP_LEN - 1);
SYS_LOG_INF("LWM2M Client: %s", clients[index].ep_name);
return 0;
}
static int lwm2m_rd_client_init(struct device *dev)
{
k_thread_create(&lwm2m_rd_client_thread_data,
&lwm2m_rd_client_thread_stack[0],
K_THREAD_STACK_SIZEOF(lwm2m_rd_client_thread_stack),
(k_thread_entry_t) lwm2m_rd_client_service,
NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
SYS_LOG_DBG("LWM2M RD client thread started");
return 0;
}
SYS_INIT(lwm2m_rd_client_init, APPLICATION,
CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2016, SICS Swedish ICT AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LWM2M_RD_CLIENT_H
#define LWM2M_RD_CLIENT_H
void engine_trigger_update(void);
#endif /* LWM2M_RD_CLIENT_H */

View file

@ -0,0 +1,639 @@
/*
* Copyright (c) 2016, Eistec AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Authors:
* Joakim Nohlgård <joakim.nohlgard@eistec.se>
* Joakim Eriksson <joakime@sics.se> added JSON reader parts
*/
/*
* Zephyr Contribution by Michael Scott <michael.scott@linaro.org>
* - Zephyr code style changes / code cleanup
* - Move to Zephyr APIs where possible
* - Convert to Zephyr int/uint types
* - Remove engine dependency (replace with writer/reader context)
* - Add write / read int64 functions
*/
/*
* TODO:
* - Debug formatting errors in Leshan
* - Replace magic #'s with defines
* - Research using Zephyr JSON lib for json_next_token()
*/
#define SYS_LOG_DOMAIN "lib/lwm2m_json"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <inttypes.h>
#include "lwm2m_object.h"
#include "lwm2m_rw_json.h"
#include "lwm2m_rw_plain_text.h"
#include "lwm2m_engine.h"
#define T_NONE 0
#define T_STRING_B 1
#define T_STRING 2
#define T_NAME 4
#define T_VNUM 5
#define T_OBJ 6
#define T_VAL 7
#define SEPARATOR(f) ((f & WRITER_OUTPUT_VALUE) ? "," : "")
/* writer modes */
#define MODE_NONE 0
#define MODE_INSTANCE 1
#define MODE_VALUE 2
#define MODE_READY 3
/* Simlified JSON style reader for reading in values from a LWM2M JSON string */
int json_next_token(struct lwm2m_input_context *in, struct json_data *json)
{
int pos;
u8_t type = T_NONE;
u8_t vpos_start = 0;
u8_t vpos_end = 0;
u8_t cont;
u8_t wscount = 0;
u8_t c;
json->name_len = 0;
json->value_len = 0;
cont = 1;
pos = in->inpos;
/* We will be either at start, or at a specific position */
while (pos < in->insize && cont) {
c = in->inbuf[pos++];
switch (c) {
case '{':
type = T_OBJ;
break;
case '}':
case ',':
if (type == T_VAL || type == T_STRING) {
json->value = &in->inbuf[vpos_start];
json->value_len = vpos_end - vpos_start -
wscount;
type = T_NONE;
cont = 0;
}
wscount = 0;
break;
case '\\':
/* stuffing */
if (pos < in->insize) {
pos++;
vpos_end = pos;
}
break;
case '"':
if (type == T_STRING_B) {
type = T_STRING;
vpos_end = pos - 1;
wscount = 0;
} else {
type = T_STRING_B;
vpos_start = pos;
}
break;
case ':':
if (type == T_STRING) {
json->name = &in->inbuf[vpos_start];
json->name_len = vpos_end - vpos_start;
vpos_start = vpos_end = pos;
type = T_VAL;
} else {
/* Could be in string or at illegal pos */
if (type != T_STRING_B) {
SYS_LOG_ERR("ERROR - illegal ':'");
}
}
break;
/* ignore whitespace */
case ' ':
case '\n':
case '\t':
if (type != T_STRING_B) {
if (vpos_start == pos - 1) {
vpos_start = pos;
} else {
wscount++;
}
}
/* fallthrough */
default:
vpos_end = pos;
}
}
if (cont == 0 && pos < in->insize) {
in->inpos = pos;
}
/* OK if cont == 0 othewise we failed */
return (cont == 0 && pos < in->insize);
}
static size_t put_begin(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
int len;
len = snprintf(&out->outbuf[out->outlen],
out->outsize - out->outlen,
"{\"bn\":\"/%u/%u/\",\"e\":[",
path->obj_id, path->obj_inst_id);
out->writer_flags = 0; /* set flags to zero */
if (len < 0 || len >= out->outsize) {
return 0;
}
out->outlen += len;
return (size_t)len;
}
static size_t put_end(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
int len;
len = snprintf(&out->outbuf[out->outlen],
out->outsize - out->outlen, "]}");
if (len < 0 || len >= (out->outsize - out->outlen)) {
return 0;
}
out->outlen += len;
return (size_t)len;
}
static size_t put_begin_ri(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
out->writer_flags |= WRITER_RESOURCE_INSTANCE;
return 0;
}
static size_t put_end_ri(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
out->writer_flags &= ~WRITER_RESOURCE_INSTANCE;
return 0;
}
static size_t put_s32(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s32_t value)
{
u8_t *outbuf;
size_t outlen;
char *sep;
int len;
outbuf = &out->outbuf[out->outlen];
outlen = out->outsize - out->outlen;
sep = SEPARATOR(out->writer_flags);
if (out->writer_flags & WRITER_RESOURCE_INSTANCE) {
len = snprintf(outbuf, outlen, "%s{\"n\":\"%u/%u\",\"v\":%d}",
sep, path->res_id, path->res_inst_id, value);
} else {
len = snprintf(outbuf, outlen, "%s{\"n\":\"%u\",\"v\":%d}",
sep, path->res_id, value);
}
if (len < 0 || len >= outlen) {
return 0;
}
SYS_LOG_DBG("JSON: Write int:%s", outbuf);
out->writer_flags |= WRITER_OUTPUT_VALUE;
out->outlen += len;
return (size_t)len;
}
static size_t put_s16(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s16_t value)
{
return put_s32(out, path, (s32_t)value);
}
static size_t put_s8(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s8_t value)
{
return put_s32(out, path, (s32_t)value);
}
static size_t put_s64(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s64_t value)
{
u8_t *outbuf;
size_t outlen;
char *sep;
int len;
outbuf = &out->outbuf[out->outlen];
outlen = out->outsize - out->outlen;
sep = SEPARATOR(out->writer_flags);
if (out->writer_flags & WRITER_RESOURCE_INSTANCE) {
len = snprintf(outbuf, outlen, "%s{\"n\":\"%u/%u\",\"v\":%lld}",
sep, path->res_id, path->res_inst_id, value);
} else {
len = snprintf(outbuf, outlen, "%s{\"n\":\"%u\",\"v\":%lld}",
sep, path->res_id, value);
}
if (len < 0 || len >= outlen) {
return 0;
}
SYS_LOG_DBG("JSON: Write int:%s", outbuf);
out->writer_flags |= WRITER_OUTPUT_VALUE;
out->outlen += len;
return (size_t)len;
}
static size_t put_string(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
const char *value, size_t strlen)
{
u8_t *outbuf;
size_t outlen;
char *sep;
size_t i;
size_t len = 0;
int res;
outbuf = &out->outbuf[out->outlen];
outlen = out->outsize - out->outlen;
sep = SEPARATOR(out->writer_flags);
if (out->writer_flags & WRITER_RESOURCE_INSTANCE) {
res = snprintf(outbuf, outlen, "%s{\"n\":\"%u/%u\",\"sv\":\"",
sep, path->res_id, path->res_inst_id);
} else {
res = snprintf(outbuf, outlen, "%s{\"n\":\"%u\",\"sv\":\"",
sep, path->res_id);
}
if (res < 0 || res >= outlen) {
return 0;
}
len += res;
for (i = 0; i < strlen && len < outlen; ++i) {
/* Escape special characters */
/* TODO: Handle UTF-8 strings */
if (value[i] < '\x20') {
res = snprintf(&outbuf[len], outlen - len, "\\x%x",
value[i]);
if (res < 0 || res >= (outlen - len)) {
return 0;
}
len += res;
continue;
} else if (value[i] == '"' || value[i] == '\\') {
outbuf[len] = '\\';
++len;
if (len >= outlen) {
return 0;
}
}
outbuf[len] = value[i];
++len;
if (len >= outlen) {
return 0;
}
}
res = snprintf(&outbuf[len], outlen - len, "\"}");
if (res < 0 || res >= (outlen - len)) {
return 0;
}
SYS_LOG_DBG("JSON: Write string:%s", outbuf);
len += res;
out->writer_flags |= WRITER_OUTPUT_VALUE;
out->outlen += len;
return len;
}
static size_t put_float32fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float32_value_t *value)
{
u8_t *outbuf;
size_t outlen;
char *sep;
size_t len = 0;
int res;
outbuf = &out->outbuf[out->outlen];
outlen = out->outsize - out->outlen;
sep = SEPARATOR(out->writer_flags);
if (out->writer_flags & WRITER_RESOURCE_INSTANCE) {
res = snprintf(outbuf, outlen, "%s{\"n\":\"%u/%u\",\"v\":",
sep, path->res_id, path->res_inst_id);
} else {
res = snprintf(outbuf, outlen, "%s{\"n\":\"%u\",\"v\":",
sep, path->res_id);
}
if (res <= 0 || res >= outlen) {
return 0;
}
len += res;
outlen -= res;
res = plain_text_put_float32fix(&outbuf[len], outlen, value);
if (res <= 0 || res >= outlen) {
return 0;
}
len += res;
outlen -= res;
res = snprintf(&outbuf[len], outlen, "}");
if (res <= 0 || res >= outlen) {
return 0;
}
len += res;
out->writer_flags |= WRITER_OUTPUT_VALUE;
out->outlen += len;
return len;
}
static size_t put_float64fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float64_value_t *value)
{
u8_t *outbuf;
size_t outlen;
char *sep;
size_t len = 0;
int res;
outbuf = &out->outbuf[out->outlen];
outlen = out->outsize - out->outlen;
sep = SEPARATOR(out->writer_flags);
if (out->writer_flags & WRITER_RESOURCE_INSTANCE) {
res = snprintf(outbuf, outlen, "%s{\"n\":\"%u/%u\",\"v\":",
sep, path->res_id, path->res_inst_id);
} else {
res = snprintf(outbuf, outlen, "%s{\"n\":\"%u\",\"v\":",
sep, path->res_id);
}
if (res <= 0 || res >= outlen) {
return 0;
}
len += res;
outlen -= res;
res = plain_text_put_float64fix(&outbuf[len], outlen, value);
if (res <= 0 || res >= outlen) {
return 0;
}
len += res;
outlen -= res;
res = snprintf(&outbuf[len], outlen, "}");
if (res <= 0 || res >= outlen) {
return 0;
}
len += res;
out->writer_flags |= WRITER_OUTPUT_VALUE;
out->outlen += len;
return len;
}
static size_t put_bool(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
bool value)
{
u8_t *outbuf;
size_t outlen;
char *sep;
int len;
outbuf = &out->outbuf[out->outlen];
outlen = out->outsize - out->outlen;
sep = SEPARATOR(out->writer_flags);
if (out->writer_flags & WRITER_RESOURCE_INSTANCE) {
len = snprintf(outbuf, outlen, "%s{\"n\":\"%u/%u\",\"bv\":%s}",
sep, path->res_id, path->res_inst_id,
value ? "true" : "false");
} else {
len = snprintf(outbuf, outlen, "%s{\"n\":\"%u\",\"bv\":%s}",
sep, path->res_id, value ? "true" : "false");
}
if (len < 0 || len >= outlen) {
return 0;
}
SYS_LOG_DBG("JSON: Write bool:%s", outbuf);
out->writer_flags |= WRITER_OUTPUT_VALUE;
out->outlen += len;
return (size_t)len;
}
const struct lwm2m_writer json_writer = {
put_begin,
put_end,
put_begin_ri,
put_end_ri,
put_s8,
put_s16,
put_s32,
put_s64,
put_string,
put_float32fix,
put_float64fix,
put_bool
};
static int parse_path(const u8_t *strpath, u16_t strlen,
struct lwm2m_obj_path *path)
{
int ret = 0;
int pos = 0;
u16_t val;
u8_t c = 0;
do {
val = 0;
c = strpath[pos];
/* we should get a value first - consume all numbers */
while (pos < strlen && c >= '0' && c <= '9') {
val = val * 10 + (c - '0');
c = strpath[++pos];
}
/*
* Slash will mote thing forward
* and the end will be when pos == pl
*/
if (c == '/' || pos == strlen) {
SYS_LOG_DBG("Setting %u = %u", ret, val);
if (ret == 0) {
path->obj_id = val;
} else if (ret == 1) {
path->obj_inst_id = val;
} else if (ret == 2) {
path->res_id = val;
}
ret++;
pos++;
} else {
SYS_LOG_ERR("Error: illegal char '%c' at pos:%d",
c, pos);
return -1;
}
} while (pos < strlen);
return ret;
}
int do_write_op_json(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
struct lwm2m_input_context *in = context->in;
struct lwm2m_obj_path *path = context->path;
struct lwm2m_engine_obj_field *obj_field;
struct lwm2m_engine_obj_inst *obj_inst = NULL;
struct lwm2m_engine_res_inst *res = NULL;
struct json_data json;
u8_t olv = 0;
u8_t *inbuf, created;
int inpos, i, r;
size_t insize;
u8_t mode = MODE_NONE;
olv = path->level;
inbuf = in->inbuf;
inpos = in->inpos;
insize = in->insize;
while (json_next_token(in, &json)) {
i = 0;
created = 0;
if (json.name[0] == 'n') {
path->level = parse_path(json.value, json.value_len,
path);
if (i > 0) {
r = lwm2m_get_or_create_engine_obj(context,
&obj_inst,
&created);
if (r < 0) {
return r;
}
mode |= MODE_INSTANCE;
}
} else {
/* HACK: assume value node: can it be anything else? */
mode |= MODE_VALUE;
/* update values */
inbuf = in->inbuf;
inpos = in->inpos;
in->inbuf = json.value;
in->inpos = 0;
in->insize = json.value_len;
}
if (mode == MODE_READY) {
if (!obj_inst) {
return -EINVAL;
}
obj_field = lwm2m_get_engine_obj_field(obj,
path->res_id);
if (!obj_field) {
return -EINVAL;
}
if ((obj_field->permissions & LWM2M_PERM_W) !=
LWM2M_PERM_W) {
return -EPERM;
}
if (!obj_inst->resources ||
obj_inst->resource_count == 0) {
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) {
return -ENOENT;
}
lwm2m_write_handler(obj_inst, res, obj_field, context);
mode = MODE_NONE;
in->inbuf = inbuf;
in->inpos = inpos;
in->insize = insize;
path->level = olv;
}
}
return 0;
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016, Eistec AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Author:
* Joakim Nohlgård <joakim.nohlgard@eistec.se>
*/
#ifndef LWM2M_RW_JSON_H_
#define LWM2M_RW_JSON_H_
#include "lwm2m_object.h"
struct json_data {
u8_t type; /* S,B,V */
u8_t *name;
u8_t *value;
u8_t name_len;
u8_t value_len;
};
extern const struct lwm2m_writer json_writer;
int json_next_token(struct lwm2m_input_context *in, struct json_data *json);
int do_write_op_json(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context);
#endif /* LWM2M_RW_JSON_H_ */

View file

@ -0,0 +1,707 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Authors:
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
/*
* Zephyr Contribution by Michael Scott <michael.scott@linaro.org>
* - Zephyr code style changes / code cleanup
* - Move to Zephyr APIs where possible
* - Convert to Zephyr int/uint types
* - Remove engine dependency (replace with writer context)
* - Add write int64 function
*/
/*
* TODO:
* - Lots of byte-order API clean up
* - Var / parameter type cleanup
* - Replace magic #'s with defines
*/
#define SYS_LOG_DOMAIN "lib/lwm2m_oma_tlv"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <string.h>
#include <stdint.h>
#include <misc/byteorder.h>
#include "lwm2m_rw_oma_tlv.h"
#include "lwm2m_engine.h"
static u8_t get_len_type(const struct oma_tlv *tlv)
{
if (tlv->length < 8) {
return 0;
} else if (tlv->length < 0x100) {
return 1;
} else if (tlv->length < 0x10000) {
return 2;
}
return 3;
}
static u8_t tlv_calc_type(u8_t flags)
{
return flags & WRITER_RESOURCE_INSTANCE ?
OMA_TLV_TYPE_RESOURCE_INSTANCE : OMA_TLV_TYPE_RESOURCE;
}
static u16_t tlv_calc_id(u8_t flags, struct lwm2m_obj_path *path)
{
return flags & WRITER_RESOURCE_INSTANCE ?
path->res_inst_id : path->res_id;
}
static void tlv_setup(struct oma_tlv *tlv, u8_t type, u16_t id,
u32_t len, const u8_t *value)
{
if (tlv) {
tlv->type = type;
tlv->id = id;
tlv->length = len;
tlv->value = value;
}
}
static size_t oma_tlv_put(const struct oma_tlv *tlv, u8_t *buffer, size_t len)
{
int pos;
u8_t len_type;
/* len type is the same as number of bytes required for length */
len_type = get_len_type(tlv);
pos = 1 + len_type;
/* ensure that we do not write too much */
if (len < tlv->length + pos) {
SYS_LOG_ERR("OMA-TLV: Could not write the TLV - buffer overflow"
" (len:%zd < tlv->length:%d + pos:%d)",
len, tlv->length, pos);
return 0;
}
/* first type byte in TLV header */
buffer[0] = (tlv->type << 6) |
(tlv->id > 255 ? (1 << 5) : 0) |
(len_type << 3) |
(len_type == 0 ? tlv->length : 0);
pos = 1;
/* The ID */
if (tlv->id > 255) {
buffer[pos++] = (tlv->id >> 8) & 0xff;
}
buffer[pos++] = tlv->id & 0xff;
/* Add length if needed - unrolled loop ? */
if (len_type > 2) {
buffer[pos++] = (tlv->length >> 16) & 0xff;
}
if (len_type > 1) {
buffer[pos++] = (tlv->length >> 8) & 0xff;
}
if (len_type > 0) {
buffer[pos++] = tlv->length & 0xff;
}
/* finally add the value */
if (tlv->value != NULL && tlv->length > 0) {
memcpy(&buffer[pos], tlv->value, tlv->length);
}
/* TODO: Add debug print of TLV */
return pos + tlv->length;
}
size_t oma_tlv_get(struct oma_tlv *tlv, const u8_t *buffer, size_t len)
{
u8_t len_type;
u8_t len_pos = 1;
size_t tlv_len;
tlv->type = (buffer[0] >> 6) & 3;
len_type = (buffer[0] >> 3) & 3;
len_pos = 1 + (((buffer[0] & (1 << 5)) != 0) ? 2 : 1);
tlv->id = buffer[1];
/* if len_pos > 2 it means that there are more ID to read */
if (len_pos > 2) {
tlv->id = (tlv->id << 8) + buffer[2];
}
if (len_type == 0) {
tlv_len = buffer[0] & 7;
} else {
/* read the length */
tlv_len = 0;
while (len_type > 0) {
tlv_len = tlv_len << 8 | buffer[len_pos++];
len_type--;
}
}
/* and read out the data??? */
tlv->length = tlv_len;
tlv->value = &buffer[len_pos];
return len_pos + tlv_len;
}
static size_t put_begin_ri(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
/* set some flags in state */
struct oma_tlv tlv;
size_t len;
out->writer_flags |= WRITER_RESOURCE_INSTANCE;
tlv_setup(&tlv, OMA_TLV_TYPE_MULTI_RESOURCE, path->res_id, 8, NULL);
/* we remove the nonsense payload here (len = 8) */
len = oma_tlv_put(&tlv, &out->outbuf[out->outlen],
out->outsize - out->outlen) - 8;
/*
* store position for deciding where to re-write the TLV when we
* know the length - NOTE: either this or memmov of buffer later...
*/
out->mark_pos_ri = out->outlen;
out->outlen += len;
return len;
}
static size_t put_end_ri(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path)
{
/* clear out state info */
int pos = 2; /* this is the length pos */
size_t len;
out->writer_flags &= ~WRITER_RESOURCE_INSTANCE;
if (path->res_id > 0xff) {
pos++;
}
len = out->outlen - out->mark_pos_ri;
/* update the length byte... Assume TLV header is pos + 1 bytes. */
out->outbuf[pos + out->mark_pos_ri] = len - (pos + 1);
return 0;
}
static size_t put_s8(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s8_t value)
{
size_t len;
struct oma_tlv tlv;
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
tlv_calc_id(out->writer_flags, path), sizeof(value),
(u8_t *)&value);
len = oma_tlv_put(&tlv, &out->outbuf[out->outlen],
out->outsize - out->outlen);
out->outlen += len;
return len;
}
static size_t put_s16(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s16_t value)
{
size_t len;
struct oma_tlv tlv;
s16_t net_value;
net_value = sys_cpu_to_be16(value);
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
tlv_calc_id(out->writer_flags, path), sizeof(net_value),
(u8_t *)&net_value);
len = oma_tlv_put(&tlv, &out->outbuf[out->outlen],
out->outsize - out->outlen);
out->outlen += len;
return len;
}
static size_t put_s32(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s32_t value)
{
size_t len;
struct oma_tlv tlv;
s32_t net_value;
net_value = sys_cpu_to_be32(value);
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
tlv_calc_id(out->writer_flags, path), sizeof(net_value),
(u8_t *)&net_value);
len = oma_tlv_put(&tlv, &out->outbuf[out->outlen],
out->outsize - out->outlen);
out->outlen += len;
return len;
}
static size_t put_s64(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s64_t value)
{
size_t len;
struct oma_tlv tlv;
s64_t net_value;
net_value = sys_cpu_to_be64(value);
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
tlv_calc_id(out->writer_flags, path), sizeof(net_value),
(u8_t *)&net_value);
len = oma_tlv_put(&tlv, &out->outbuf[out->outlen],
out->outsize - out->outlen);
out->outlen += len;
return len;
}
static size_t put_string(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
const char *value, size_t strlen)
{
size_t len;
struct oma_tlv tlv;
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
path->res_id, (u32_t)strlen,
(u8_t *)value);
len = oma_tlv_put(&tlv, &out->outbuf[out->outlen],
out->outsize - out->outlen);
out->outlen += len;
return len;
}
/* use binary32 format */
static size_t put_float32fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float32_value_t *value)
{
size_t len;
struct oma_tlv tlv;
u8_t *buffer = &out->outbuf[out->outlen];
int e = 0;
s32_t val = 0;
s32_t v;
u8_t b[4];
/*
* TODO: Currently, there is no standard API for handling the decimal
* portion of the float32_value structure. In the future, we should use
* the value->val2 (decimal portion) to set the decimal mask and in the
* following binary float calculations.
*
* HACK BELOW: hard code the decimal mask to 0 (whole number)
*/
int bits = 0;
v = value->val1;
if (v < 0) {
v = -v;
}
while (v > 1) {
val = (val >> 1);
if (v & 1) {
val = val | (1L << 22);
}
v = (v >> 1);
e++;
}
/* convert to the thing we should have */
e = e - bits + 127;
if (value->val1 == 0) {
e = 0;
}
/* is this the right byte order? */
b[0] = (value->val1 < 0 ? 0x80 : 0) | (e >> 1);
b[1] = ((e & 1) << 7) | ((val >> 16) & 0x7f);
b[2] = (val >> 8) & 0xff;
b[3] = val & 0xff;
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
tlv_calc_id(out->writer_flags, path),
4, b);
len = oma_tlv_put(&tlv, buffer, out->outsize - out->outlen);
out->outlen += len;
return len;
}
static size_t put_float64fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float64_value_t *value)
{
size_t len;
s64_t binary64 = 0, net_binary64 = 0;
struct oma_tlv tlv;
u8_t *buffer = &out->outbuf[out->outlen];
/* TODO */
net_binary64 = sys_cpu_to_be64(binary64);
tlv_setup(&tlv, tlv_calc_type(out->writer_flags),
tlv_calc_id(out->writer_flags, path),
sizeof(net_binary64), (u8_t *)&net_binary64);
len = oma_tlv_put(&tlv, buffer, out->outsize - out->outlen);
out->outlen += len;
return len;
}
static size_t put_bool(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, bool value)
{
return put_s8(out, path, value != 0 ? 1 : 0);
}
static size_t get_s32(struct lwm2m_input_context *in, s32_t *value)
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in->inbuf, in->insize);
*value = 0;
if (size > 0) {
switch (tlv.length) {
case 1:
*value = *(s8_t *)tlv.value;
break;
case 2:
*value = sys_cpu_to_be16(*(s16_t *)tlv.value);
break;
case 4:
*value = sys_cpu_to_be32(*(s32_t *)tlv.value);
break;
default:
SYS_LOG_ERR("invalid length: %u", tlv.length);
size = 0;
tlv.length = 0;
}
in->last_value_len = tlv.length;
}
return size;
}
/* TODO: research to make sure this is correct */
static size_t get_s64(struct lwm2m_input_context *in, s64_t *value)
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in->inbuf, in->insize);
*value = 0;
if (size > 0) {
switch (tlv.length) {
case 1:
*value = *tlv.value;
break;
case 2:
*value = sys_cpu_to_be16(*(s16_t *)tlv.value);
break;
case 4:
*value = sys_cpu_to_be32(*(s32_t *)tlv.value);
break;
case 8:
*value = sys_cpu_to_be64(*(s64_t *)tlv.value);
break;
default:
SYS_LOG_ERR("invalid length: %u", tlv.length);
size = 0;
tlv.length = 0;
}
in->last_value_len = tlv.length;
}
return size;
}
static size_t get_string(struct lwm2m_input_context *in,
u8_t *value, size_t strlen)
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in->inbuf, in->insize);
if (size > 0) {
if (strlen <= tlv.length) {
/*
* The outbuffer can not contain the
* full string including ending zero
*/
return 0;
}
memcpy(value, tlv.value, tlv.length);
value[tlv.length] = '\0';
in->last_value_len = tlv.length;
}
return size;
}
/* convert float to fixpoint */
static size_t get_float32fix(struct lwm2m_input_context *in,
float32_value_t *value)
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in->inbuf, in->insize);
int e;
s32_t val;
int sign = (tlv.value[0] & 0x80) != 0;
/* */
int bits = 0;
if (size > 0) {
/* TLV needs to be 4 bytes */
e = ((tlv.value[0] << 1) & 0xff) | (tlv.value[1] >> 7);
val = (((long)tlv.value[1] & 0x7f) << 16) |
(tlv.value[2] << 8) |
tlv.value[3];
e = e - 127 + bits;
/* e is the number of times we need to roll the number */
SYS_LOG_DBG("Actual e=%d", e);
e = e - 23;
SYS_LOG_DBG("E after sub %d", e);
val = val | 1L << 23;
if (e > 0) {
val = val << e;
} else {
val = val >> -e;
}
value->val1 = sign ? -val : val;
/*
* TODO: Currently, there is no standard API for handling the
* decimal portion of the float32_value structure. In the
* future, once that is settled, we should calculate
* value->val2 in the above float calculations.
*
* HACK BELOW: hard code the decimal value 0
*/
value->val2 = 0;
in->last_value_len = tlv.length;
}
return size;
}
static size_t get_float64fix(struct lwm2m_input_context *in,
float64_value_t *value)
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in->inbuf, in->insize);
if (size > 0) {
if (tlv.length != 8) {
SYS_LOG_ERR("invalid length: %u (not 8)", tlv.length);
return 0;
}
/* TODO */
in->last_value_len = tlv.length;
}
return size;
}
static size_t get_bool(struct lwm2m_input_context *in, bool *value)
{
struct oma_tlv tlv;
size_t size = oma_tlv_get(&tlv, in->inbuf, in->insize);
int i;
s32_t temp = 0;
if (size > 0) {
/* will probably need to handle MSB as a sign bit? */
for (i = 0; i < tlv.length; i++) {
temp = (temp << 8) | tlv.value[i];
}
*value = (temp != 0);
in->last_value_len = tlv.length;
}
return size;
}
const struct lwm2m_writer oma_tlv_writer = {
NULL,
NULL,
put_begin_ri,
put_end_ri,
put_s8,
put_s16,
put_s32,
put_s64,
put_string,
put_float32fix,
put_float64fix,
put_bool
};
const struct lwm2m_reader oma_tlv_reader = {
get_s32,
get_s64,
get_string,
get_float32fix,
get_float64fix,
get_bool
};
static int do_write_op_tlv_item(struct lwm2m_engine_context *context,
u8_t *data, int len)
{
struct lwm2m_input_context *in = context->in;
struct lwm2m_engine_obj_inst *obj_inst = NULL;
struct lwm2m_engine_res_inst *res = NULL;
struct lwm2m_engine_obj_field *obj_field = NULL;
u8_t created = 0;
int ret, i;
in->inbuf = data;
in->inpos = 0;
in->insize = len;
ret = lwm2m_get_or_create_engine_obj(context, &obj_inst, &created);
if (ret < 0) {
return ret;
}
obj_field = lwm2m_get_engine_obj_field(obj_inst->obj,
context->path->res_id);
if (!obj_field) {
return -EINVAL;
}
if ((obj_field->permissions & LWM2M_PERM_W) != LWM2M_PERM_W) {
return -EPERM;
}
if (!obj_inst->resources || obj_inst->resource_count == 0) {
return -EINVAL;
}
for (i = 0; i < obj_inst->resource_count; i++) {
if (obj_inst->resources[i].res_id == context->path->res_id) {
res = &obj_inst->resources[i];
break;
}
}
if (!res) {
return -ENOENT;
}
return lwm2m_write_handler(obj_inst, res, obj_field, context);
}
int do_write_op_tlv(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
struct lwm2m_input_context *in = context->in;
struct lwm2m_obj_path *path = context->path;
struct lwm2m_engine_obj_inst *obj_inst = NULL;
size_t len;
struct oma_tlv tlv;
int tlvpos = 0, ret;
while (tlvpos < in->insize) {
len = oma_tlv_get(&tlv, &in->inbuf[tlvpos],
in->insize - tlvpos);
SYS_LOG_DBG("Got TLV format First is: type:%d id:%d "
"len:%d (p:%d len:%d/%d)",
tlv.type, tlv.id, (int) tlv.length,
(int) tlvpos, (int) len, (int) in->insize);
if (tlv.type == OMA_TLV_TYPE_OBJECT_INSTANCE) {
struct oma_tlv tlv2;
int len2;
int pos = 0;
path->obj_inst_id = tlv.id;
if (tlv.length == 0) {
/* Create only - no data */
ret = lwm2m_create_obj_inst(path->obj_id,
path->obj_inst_id,
&obj_inst);
if (ret < 0) {
return ret;
}
}
while (pos < tlv.length &&
(len2 = oma_tlv_get(&tlv2, &tlv.value[pos],
tlv.length - pos))) {
if (tlv2.type == OMA_TLV_TYPE_RESOURCE) {
path->res_id = tlv2.id;
path->level = 3;
/* ignore errors */
do_write_op_tlv_item(
context,
(u8_t *)&tlv.value[pos],
len2);
}
pos += len2;
}
zoap_header_set_code(context->out->out_zpkt,
ZOAP_RESPONSE_CODE_CREATED);
} else if (tlv.type == OMA_TLV_TYPE_RESOURCE) {
path->res_id = tlv.id;
path->level = 3;
ret = do_write_op_tlv_item(context,
&in->inbuf[tlvpos], len);
if (ret < 0) {
return ret;
}
}
tlvpos += len;
}
return 0;
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Authors:
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef LWM2M_RW_OMA_TLV_H_
#define LWM2M_RW_OMA_TLV_H_
#include <stdint.h>
#include <stddef.h>
#include "lwm2m_object.h"
enum {
OMA_TLV_TYPE_OBJECT_INSTANCE = 0,
OMA_TLV_TYPE_RESOURCE_INSTANCE = 1,
OMA_TLV_TYPE_MULTI_RESOURCE = 2,
OMA_TLV_TYPE_RESOURCE = 3
};
enum {
OMA_TLV_LEN_TYPE_NO_LEN = 0,
OMA_TLV_LEN_TYPE_8BIT_LEN = 1,
OMA_TLV_LEN_TYPE_16BIT_LEN = 2,
OMA_TLV_LEN_TYPE_24BIT_LEN = 3
};
struct oma_tlv {
u8_t type;
u16_t id; /* can be 8-bit or 16-bit when serialized */
u32_t length;
const u8_t *value;
};
extern const struct lwm2m_writer oma_tlv_writer;
extern const struct lwm2m_reader oma_tlv_reader;
int do_write_op_tlv(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context);
#endif /* LWM2M_RW_OMA_TLV_H_ */

View file

@ -0,0 +1,434 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Authors:
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
/*
* Zephyr Contribution by Michael Scott <michael.scott@linaro.org>
* - Zephyr code style changes / code cleanup
* - Move to Zephyr APIs where possible
* - Convert to Zephyr int/uint types
* - Remove engine dependency (replace with writer context)
* - Add write int64 function
*/
/*
* TODO:
* - Type cleanups
* - Cleanup integer parsing
*/
#define SYS_LOG_DOMAIN "lib/lwm2m_plain_text"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_LWM2M_LEVEL
#include <logging/sys_log.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "lwm2m_object.h"
#include "lwm2m_rw_plain_text.h"
#include "lwm2m_engine.h"
size_t plain_text_put_float32fix(u8_t *outbuf, size_t outlen,
float32_value_t *value)
{
int n, o = 0;
if (outlen == 0) {
return 0;
}
if (value->val1 < 0) {
*outbuf++ = '-';
outlen--;
o = 1;
value->val1 = -value->val1;
}
n = snprintf(outbuf, outlen, "%d.%d", value->val1, value->val2);
if (n < 0 || n >= outlen) {
return 0;
}
return n + o;
}
size_t plain_text_put_float64fix(u8_t *outbuf, size_t outlen,
float64_value_t *value)
{
int n, o = 0;
if (outlen == 0) {
return 0;
}
if (value->val1 < 0) {
*outbuf++ = '-';
outlen--;
o = 1;
value->val1 = -value->val1;
}
n = snprintf(outbuf, outlen, "%lld.%lld", value->val1, value->val2);
if (n < 0 || n >= outlen) {
return 0;
}
return n + o;
}
static size_t put_s32(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s32_t value)
{
int len;
len = snprintf(&out->outbuf[out->outlen], out->outsize - out->outlen,
"%d", value);
if (len < 0 || len >= (out->outsize - out->outlen)) {
return 0;
}
out->outlen += len;
return (size_t)len;
}
static size_t put_s8(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s8_t value)
{
return put_s32(out, path, (s32_t)value);
}
static size_t put_s16(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s16_t value)
{
return put_s32(out, path, (s32_t)value);
}
static size_t put_s64(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path, s64_t value)
{
int len;
len = snprintf(&out->outbuf[out->outlen], out->outsize - out->outlen,
"%lld", value);
if (len < 0 || len >= (out->outsize - out->outlen)) {
return 0;
}
out->outlen += len;
return (size_t)len;
}
static size_t put_float32fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float32_value_t *value)
{
size_t len;
len = plain_text_put_float32fix(&out->outbuf[out->outlen],
out->outsize - out->outlen,
value);
out->outlen += len;
return len;
}
static size_t put_float64fix(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
float64_value_t *value)
{
size_t len;
len = plain_text_put_float64fix(&out->outbuf[out->outlen],
out->outsize - out->outlen,
value);
out->outlen += len;
return len;
}
static size_t put_string(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
const char *value, size_t strlen)
{
if (strlen >= (out->outsize - out->outlen)) {
return 0;
}
memmove(&out->outbuf[out->outlen], value, strlen);
out->outbuf[strlen] = '\0';
out->outlen += strlen;
return strlen;
}
static size_t put_bool(struct lwm2m_output_context *out,
struct lwm2m_obj_path *path,
bool value)
{
if ((out->outsize - out->outlen) > 0) {
if (value) {
out->outbuf[out->outlen] = '1';
} else {
out->outbuf[out->outlen] = '0';
}
out->outlen += 1;
return 1;
}
return 0;
}
static size_t get_s32(struct lwm2m_input_context *in, s32_t *value)
{
int i;
bool neg = false;
*value = 0;
for (i = 0; i < in->insize; i++) {
if (isdigit(in->inbuf[i])) {
*value = *value * 10 + (in->inbuf[i] - '0');
} else if (in->inbuf[i] == '-' && i == 0) {
neg = true;
} else {
break;
}
}
if (neg) {
*value = -*value;
}
in->last_value_len = i;
return i;
}
static size_t get_s64(struct lwm2m_input_context *in, s64_t *value)
{
int i;
bool neg = false;
*value = 0;
for (i = 0; i < in->insize; i++) {
if (isdigit(in->inbuf[i])) {
*value = *value * 10 + (in->inbuf[i] - '0');
} else if (in->inbuf[i] == '-' && i == 0) {
neg = true;
} else {
break;
}
}
if (neg) {
*value = -*value;
}
in->last_value_len = i;
return i;
}
static size_t get_string(struct lwm2m_input_context *in,
u8_t *value, size_t strlen)
{
/* The outbuffer can't contain the full string including ending zero */
if (strlen <= in->insize) {
return 0;
}
memcpy(value, in->inbuf, in->insize);
value[in->insize] = '\0';
in->last_value_len = in->insize;
return in->insize;
}
static size_t get_float32fix(struct lwm2m_input_context *in,
float32_value_t *value)
{
int i;
bool dot = false, neg = false;
s32_t counter = 0;
value->val1 = 0;
value->val2 = 0;
for (i = 0; i < in->insize; i++) {
if (isdigit(in->inbuf[i])) {
counter = counter * 10 + (in->inbuf[i] - '0');
} else if (in->inbuf[i] == '-' && i == 0) {
neg = true;
} else if (in->inbuf[i] == '.' && !dot) {
value->val1 = counter;
counter = 0;
dot = true;
} else {
break;
}
}
if (!dot) {
value->val1 = counter;
} else {
value->val2 = counter;
}
SYS_LOG_DBG("READ FLOATFIX: '%s'(%d) => %d.%d",
in->inbuf, in->insize, value->val1, value->val2);
if (neg) {
value->val1 = -value->val1;
}
in->last_value_len = i;
return i;
}
static size_t get_float64fix(struct lwm2m_input_context *in,
float64_value_t *value)
{
int i;
bool dot = false, neg = false;
s64_t counter = 0;
value->val1 = 0;
value->val2 = 0;
for (i = 0; i < in->insize; i++) {
if (isdigit(in->inbuf[i])) {
counter = counter * 10 + (in->inbuf[i] - '0');
} else if (in->inbuf[i] == '-' && i == 0) {
neg = true;
} else if (in->inbuf[i] == '.' && !dot) {
value->val1 = counter;
counter = 0;
dot = true;
} else {
break;
}
}
if (!dot) {
value->val1 = counter;
} else {
value->val2 = counter;
}
SYS_LOG_DBG("READ FLOATFIX: '%s'(%d) => %lld.%lld",
in->inbuf, in->insize, value->val1, value->val2);
if (neg) {
value->val1 = -value->val1;
}
in->last_value_len = i;
return i;
}
static size_t get_bool(struct lwm2m_input_context *in,
bool *value)
{
if (in->insize > 0) {
if (*in->inbuf == '1' || *in->inbuf == '0') {
*value = (*in->inbuf == '1') ? true : false;
in->last_value_len = 1;
return 1;
}
}
return 0;
}
const struct lwm2m_writer plain_text_writer = {
NULL,
NULL,
NULL,
NULL,
put_s8,
put_s16,
put_s32,
put_s64,
put_string,
put_float32fix,
put_float64fix,
put_bool
};
const struct lwm2m_reader plain_text_reader = {
get_s32,
get_s64,
get_string,
get_float32fix,
get_float64fix,
get_bool
};
int do_write_op_plain_text(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
struct lwm2m_obj_path *path = context->path;
struct lwm2m_engine_obj_inst *obj_inst = NULL;
struct lwm2m_engine_obj_field *obj_field;
struct lwm2m_engine_res_inst *res = NULL;
int ret, i;
u8_t created = 0;
ret = lwm2m_get_or_create_engine_obj(context, &obj_inst, &created);
if (ret < 0) {
return ret;
}
obj_field = lwm2m_get_engine_obj_field(obj, path->res_id);
if (!obj_field) {
return -EINVAL;
}
if ((obj_field->permissions & LWM2M_PERM_W) != LWM2M_PERM_W) {
return -EPERM;
}
if (!obj_inst->resources || obj_inst->resource_count == 0) {
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) {
return -ENOENT;
}
context->path->level = 3;
context->in->inpos = 0;
return lwm2m_write_handler(obj_inst, res, obj_field, context);
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2015, Yanzi Networks AB.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Original Authors:
* Joakim Eriksson <joakime@sics.se>
* Niclas Finne <nfi@sics.se>
*/
#ifndef LWM2M_RW_PLAIN_TEXT_H_
#define LWM2M_RW_PLAIN_TEXT_H_
#include "lwm2m_object.h"
extern const struct lwm2m_writer plain_text_writer;
extern const struct lwm2m_reader plain_text_reader;
size_t plain_text_put_float32fix(u8_t *outbuf, size_t outlen,
float32_value_t *value);
size_t plain_text_put_float64fix(u8_t *outbuf, size_t outlen,
float64_value_t *value);
int do_write_op_plain_text(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context);
#endif /* LWM2M_RW_PLAIN_TEXT_H_ */