zephyr/subsys/net/lib/lwm2m/ipso_onoff_switch.c
Robert Lubos b6f69a6df1 net: lwm2m: Align float handling with specification
According to the specificaion, resources are not predefined to use 32 or
64 bit floating point numbers, but should rather accept any of them (as
indicated by the size of the TLV in the message). This lead to issues
for instance with Eclipse Leshan LWM2M server, where Leshan sent 64-bit
value, while Zephyr expected 32-bit, making it impossible to write
the resource.

Therefore, unify the float usage to 32-bit float representation and fix
the TLV parsing functions, to accept values sent as either 32 or 64 bit
float.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
2021-09-16 19:04:52 -04:00

253 lines
6.6 KiB
C

/*
* Copyright (c) 2019 Foundries.io
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* Source material for IPSO On/Off Switch object (3342):
* http://www.openmobilealliance.org/tech/profiles/lwm2m/3342.xml
*/
#define LOG_MODULE_NAME net_ipso_onoff_switch
#define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
#include <logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
#include <stdint.h>
#include <init.h>
#include "lwm2m_object.h"
#include "lwm2m_engine.h"
#include "lwm2m_resource_ids.h"
#define SWITCH_VERSION_MAJOR 1
#if defined(CONFIG_LWM2M_IPSO_ONOFF_SWITCH_VERSION_1_1)
#define SWITCH_VERSION_MINOR 1
#define SWITCH_MAX_ID 7
#else
#define SWITCH_VERSION_MINOR 0
#define SWITCH_MAX_ID 5
#endif /* defined(CONFIG_LWM2M_IPSO_ONOFF_SWITCH_VERSION_1_1) */
#define MAX_INSTANCE_COUNT CONFIG_LWM2M_IPSO_ONOFF_SWITCH_INSTANCE_COUNT
/*
* Calculate resource instances as follows:
* start with SWITCH_MAX_ID
*/
#define RESOURCE_INSTANCE_COUNT (SWITCH_MAX_ID)
/* resource state */
struct ipso_switch_data {
uint64_t trigger_offset;
int64_t on_time_sec;
int64_t off_time_sec;
int64_t counter;
uint16_t obj_inst_id;
bool last_state;
bool state;
};
static struct ipso_switch_data switch_data[MAX_INSTANCE_COUNT];
static struct lwm2m_engine_obj onoff_switch;
static struct lwm2m_engine_obj_field fields[] = {
OBJ_FIELD_DATA(DIGITAL_INPUT_STATE_RID, R, BOOL),
OBJ_FIELD_DATA(DIGITAL_INPUT_COUNTER_RID, R_OPT, S64),
OBJ_FIELD_DATA(ON_TIME_RID, RW_OPT, S64),
OBJ_FIELD_DATA(OFF_TIME_RID, RW_OPT, S64),
OBJ_FIELD_DATA(APPLICATION_TYPE_RID, RW_OPT, STRING),
#if defined(CONFIG_LWM2M_IPSO_ONOFF_SWITCH_VERSION_1_1)
OBJ_FIELD_DATA(TIMESTAMP_RID, R_OPT, TIME),
OBJ_FIELD_DATA(FRACTIONAL_TIMESTAMP_RID, R_OPT, FLOAT),
#endif
};
static struct lwm2m_engine_obj_inst inst[MAX_INSTANCE_COUNT];
static struct lwm2m_engine_res res[MAX_INSTANCE_COUNT][SWITCH_MAX_ID];
static struct lwm2m_engine_res_inst
res_inst[MAX_INSTANCE_COUNT][RESOURCE_INSTANCE_COUNT];
static int get_switch_index(uint16_t obj_inst_id)
{
int i, ret = -ENOENT;
for (i = 0; i < ARRAY_SIZE(inst); i++) {
if (!inst[i].obj || inst[i].obj_inst_id != obj_inst_id) {
continue;
}
ret = i;
break;
}
return ret;
}
static int state_post_write_cb(uint16_t obj_inst_id,
uint16_t res_id, uint16_t res_inst_id,
uint8_t *data, uint16_t data_len,
bool last_block, size_t total_size)
{
int i;
i = get_switch_index(obj_inst_id);
if (i < 0) {
return i;
}
if (switch_data[i].state) {
/* reset off time */
switch_data[i].off_time_sec = 0;
if (!switch_data[i].last_state) {
/* off to on transition */
switch_data[i].counter++;
if (switch_data[i].counter < 0) {
switch_data[i].counter = 0;
}
}
} else {
/* reset on time */
switch_data[i].on_time_sec = 0;
}
switch_data[i].last_state = switch_data[i].state;
switch_data[i].trigger_offset = k_uptime_get();
return 0;
}
static void *on_time_read_cb(uint16_t obj_inst_id,
uint16_t res_id, uint16_t res_inst_id,
size_t *data_len)
{
int i = get_switch_index(obj_inst_id);
if (i < 0) {
return NULL;
}
if (switch_data[i].state) {
switch_data[i].on_time_sec =
(int64_t)((k_uptime_get() - switch_data[i].trigger_offset) / 1000);
}
*data_len = sizeof(switch_data[i].on_time_sec);
return &switch_data[i].on_time_sec;
}
static void *off_time_read_cb(uint16_t obj_inst_id,
uint16_t res_id, uint16_t res_inst_id,
size_t *data_len)
{
int i = get_switch_index(obj_inst_id);
if (i < 0) {
return NULL;
}
if (!switch_data[i].state) {
switch_data[i].off_time_sec =
(int64_t)((k_uptime_get() - switch_data[i].trigger_offset) / 1000);
}
*data_len = sizeof(switch_data[i].off_time_sec);
return &switch_data[i].off_time_sec;
}
static int time_post_write_cb(uint16_t obj_inst_id,
uint16_t res_id, uint16_t res_inst_id,
uint8_t *data, uint16_t data_len,
bool last_block, size_t total_size)
{
int i = get_switch_index(obj_inst_id);
if (i < 0) {
return i;
}
switch_data[i].trigger_offset = 0U;
return 0;
}
static struct lwm2m_engine_obj_inst *switch_create(uint16_t obj_inst_id)
{
int index, avail = -1, i = 0, j = 0;
/* Check that there is no other instance with this ID */
for (index = 0; index < ARRAY_SIZE(inst); index++) {
if (inst[index].obj && inst[index].obj_inst_id == obj_inst_id) {
LOG_ERR("Can not create instance - "
"already existing: %u", obj_inst_id);
return NULL;
}
/* Save first available slot index */
if (avail < 0 && !inst[index].obj) {
avail = index;
}
}
if (avail < 0) {
LOG_ERR("Can not create instance - no more room: %u",
obj_inst_id);
return NULL;
}
/* Set default values */
(void)memset(&switch_data[avail], 0, sizeof(switch_data[avail]));
switch_data[avail].obj_inst_id = obj_inst_id;
(void)memset(res[avail], 0,
sizeof(res[avail][0]) * ARRAY_SIZE(res[avail]));
init_res_instance(res_inst[avail], ARRAY_SIZE(res_inst[avail]));
/* initialize instance resource data */
INIT_OBJ_RES(DIGITAL_INPUT_STATE_RID, res[avail], i, res_inst[avail],
j, 1, false, true, &switch_data[avail].state,
sizeof(switch_data[avail].state),
NULL, NULL, NULL, state_post_write_cb, NULL);
INIT_OBJ_RES_DATA(DIGITAL_INPUT_COUNTER_RID, res[avail], i,
res_inst[avail], j, &switch_data[avail].counter,
sizeof(switch_data[avail].counter));
INIT_OBJ_RES_OPT(ON_TIME_RID, res[avail], i,
res_inst[avail], j, 1, false, true,
on_time_read_cb, NULL, NULL, time_post_write_cb, NULL);
INIT_OBJ_RES_OPT(OFF_TIME_RID, res[avail], i,
res_inst[avail], j, 1, false, true,
off_time_read_cb, NULL, NULL, time_post_write_cb, NULL);
INIT_OBJ_RES_OPTDATA(APPLICATION_TYPE_RID, res[avail], i,
res_inst[avail], j);
#if defined(CONFIG_LWM2M_IPSO_ONOFF_SWITCH_VERSION_1_1)
INIT_OBJ_RES_OPTDATA(TIMESTAMP_RID, res[avail], i, res_inst[avail], j);
INIT_OBJ_RES_OPTDATA(FRACTIONAL_TIMESTAMP_RID, res[avail], i,
res_inst[avail], j);
#endif
inst[avail].resources = res[avail];
inst[avail].resource_count = i;
LOG_DBG("Create IPSO On/Off Switch instance: %d", obj_inst_id);
return &inst[avail];
}
static int ipso_switch_init(const struct device *dev)
{
onoff_switch.obj_id = IPSO_OBJECT_ONOFF_SWITCH_ID;
onoff_switch.version_major = SWITCH_VERSION_MAJOR;
onoff_switch.version_minor = SWITCH_VERSION_MINOR;
onoff_switch.is_core = false;
onoff_switch.fields = fields;
onoff_switch.field_count = ARRAY_SIZE(fields);
onoff_switch.max_instance_count = ARRAY_SIZE(inst);
onoff_switch.create_cb = switch_create;
lwm2m_register_obj(&onoff_switch);
return 0;
}
SYS_INIT(ipso_switch_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);