net: lwm2m: add write-attribute WRITE support

Implement write-attribute on obj/obj_inst/res according to LwM2M spec
20170704-A, sec 5.1.2. Support pmin/pmax/st/gt/lt parameters on WRITE
operation.

The basic idea is to add sys_slist_t to obj/obj_inst/res structure.
And attach struct lwm2m_attr to the list when attributes are written
from server side (implement lwm2m_write_attr_handler accordingly)

Signed-off-by: Robert Chou <robert.ch.chou@acer.com>
This commit is contained in:
Robert Chou 2017-11-24 18:47:00 +08:00 committed by Anas Nashif
commit 09fcd83b98
3 changed files with 381 additions and 3 deletions

View file

@ -179,6 +179,13 @@ config LWM2M_DEVICE_ERROR_CODE_MAX
This value sets the maximum number of error codes that the device
object will store before ignoring new values.
config LWM2M_NUM_ATTR
int "Maximum # of LWM2M attributes"
default 20
help
This value sets up the maximum number of LwM2M attributes that
we can handle at the same time.
menu "IPSO Alliance Smart Object Support"
source "subsys/net/lib/lwm2m/Kconfig.ipso"

View file

@ -28,6 +28,7 @@
#include <zephyr/types.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
@ -80,6 +81,12 @@
#define INSTANCE_INFO "Zephyr DTLS LwM2M-client"
#endif
#if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
#define COAP_OPTION_BUF_LEN (CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
#else
#define COAP_OPTION_BUF_LEN 13
#endif
#define MAX_TOKEN_LEN 8
struct observe_node {
@ -133,6 +140,13 @@ struct block_context {
static struct block_context block1_contexts[NUM_BLOCK1_CONTEXT];
/* write-attribute related definitons */
static const char * const LWM2M_ATTR_STR[] = { "pmin", "pmax",
"gt", "lt", "st" };
static const u8_t LWM2M_ATTR_LEN[] = { 4, 4, 2, 2, 2 };
static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR];
/* periodic / notify / observe handling stack */
static K_THREAD_STACK_DEFINE(engine_thread_stack,
CONFIG_LWM2M_ENGINE_STACK_SIZE);
@ -288,6 +302,50 @@ static void free_block_ctx(struct block_context *ctx)
/* observer functions */
struct notification_attrs {
/* use to determine which value is set */
u8_t flags;
float32_value_t gt;
float32_value_t lt;
float32_value_t st;
s32_t pmin;
s32_t pmax;
};
static int update_attrs(sys_slist_t *list, struct notification_attrs *out)
{
struct lwm2m_attr *attr;
SYS_SLIST_FOR_EACH_CONTAINER(list, attr, node) {
switch (attr->type) {
case LWM2M_ATTR_PMIN:
out->pmin = attr->int_val;
break;
case LWM2M_ATTR_PMAX:
out->pmax = attr->int_val;
break;
case LWM2M_ATTR_LT:
out->lt = attr->float_val;
break;
case LWM2M_ATTR_GT:
out->gt = attr->float_val;
break;
case LWM2M_ATTR_STEP:
out->st = attr->float_val;
break;
default:
SYS_LOG_ERR("Unrecognize attr: %d",
attr->type);
return -EINVAL;
}
/* mark as set */
out->flags |= BIT(attr->type);
}
return 0;
}
int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id)
{
struct observe_node *obs;
@ -625,6 +683,8 @@ int lwm2m_delete_obj_inst(u16_t obj_id, u16_t obj_inst_id)
int i, ret = 0;
struct lwm2m_engine_obj *obj;
struct lwm2m_engine_obj_inst *obj_inst;
struct lwm2m_attr *attr, *tmp;
sys_snode_t *prev_node = NULL;
obj = get_engine_obj(obj_id);
if (!obj) {
@ -645,10 +705,25 @@ int lwm2m_delete_obj_inst(u16_t obj_id, u16_t obj_inst_id)
/* reset obj_inst and res_inst data structure */
for (i = 0; i < obj_inst->resource_count; i++) {
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(
&obj_inst->resources[i].attr_list, attr,
tmp, node) {
sys_slist_remove(&obj_inst->resources[i].attr_list,
prev_node, &attr->node);
memset(attr, 0, sizeof(*attr));
}
memset(obj_inst->resources + i, 0,
sizeof(struct lwm2m_engine_res_inst));
}
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(
&obj_inst->attr_list, attr, tmp, node) {
sys_slist_remove(&obj_inst->attr_list, prev_node,
&attr->node);
memset(attr, 0, sizeof(*attr));
}
memset(obj_inst, 0, sizeof(struct lwm2m_engine_obj_inst));
#ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
engine_trigger_update();
@ -704,6 +779,50 @@ static u16_t atou16(u8_t *buf, u16_t buflen, u16_t *len)
return val;
}
static int atof32(const char *input, float32_value_t *out)
{
char *pos, *end, buf[24];
long int val;
s32_t base = 1000000, sign = 1;
if (!input || !out) {
return -EINVAL;
}
strncpy(buf, input, sizeof(buf) - 1);
buf[sizeof(buf) - 1] = '\0';
if (strchr(buf, '-')) {
sign = -1;
}
pos = strchr(buf, '.');
if (pos) {
*pos = '\0';
}
errno = 0;
val = strtol(buf, &end, 10);
if (errno || *end || val > INT_MAX || val < INT_MIN) {
return -EINVAL;
}
out->val1 = (s32_t) val;
out->val2 = 0;
if (!pos) {
return 0;
}
while (*(++pos) && base > 1 && isdigit(*pos)) {
out->val2 = out->val2 * 10 + (*pos - '0');
base /= 10;
}
out->val2 *= sign * base;
return !*pos || base == 1 ? 0 : -EINVAL;
}
static int coap_options_to_path(struct coap_option *opt, int options_count,
struct lwm2m_obj_path *path)
{
@ -2072,13 +2191,237 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj,
struct lwm2m_engine_context *context)
{
char opt_buf[COAP_OPTION_BUF_LEN];
int nr_opt, ret = 0;
struct coap_option options[NR_LWM2M_ATTR];
struct lwm2m_engine_obj_inst *obj_inst = NULL;
struct lwm2m_engine_res_inst *res = NULL;
struct lwm2m_input_context *in;
struct lwm2m_obj_path *path;
struct lwm2m_attr *attr, *tmp;
struct notification_attrs nattrs = { 0 };
sys_slist_t *attr_list;
sys_snode_t *prev_node = NULL;
u8_t type = 0;
void *nattr_ptrs[NR_LWM2M_ATTR] = {
&nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt, &nattrs.st
};
if (!obj || !context) {
return -EINVAL;
}
/* TODO: set parameters on resource for notification */
/* do not expose security obj */
if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
return -ENOENT;
}
return -ENOTSUP;
in = context->in;
path = context->path;
nr_opt = coap_find_options(in->in_cpkt, COAP_OPTION_URI_QUERY,
options, NR_LWM2M_ATTR);
if (nr_opt <= 0) {
SYS_LOG_ERR("No attribute found!");
/* translate as bad request */
return -EEXIST;
}
/* get lwm2m_attr slist */
if (path->level == 3) {
ret = engine_get_resource(path, &res);
if (ret < 0) {
return ret;
}
attr_list = &res->attr_list;
} else if (path->level == 1) {
attr_list = &obj->attr_list;
} else if (path->level == 2) {
obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
if (!obj_inst) {
return -ENOENT;
}
attr_list = &obj_inst->attr_list;
} else {
/* bad request */
return -EEXIST;
}
/* retrieve existing attributes */
update_attrs(attr_list, &nattrs);
/* loop through options to parse attribute */
for (int i = 0; i < nr_opt; i++) {
int limit = min(options[i].len, 5), plen = 0, vlen;
float32_value_t val = { 0 };
type = 0;
/* search for '=' */
while (plen < limit && options[i].value[plen] != '=') {
plen += 1;
}
/* either length = 2(gt/lt/st) or = 4(pmin/pmax) */
if (plen != 2 && plen != 4) {
continue;
}
/* matching attribute name */
for (type = 0; type < NR_LWM2M_ATTR; type++) {
if (LWM2M_ATTR_LEN[type] == plen &&
!memcmp(options[i].value, LWM2M_ATTR_STR[type],
LWM2M_ATTR_LEN[type])) {
break;
}
}
/* unrecognized attribute */
if (type == NR_LWM2M_ATTR) {
continue;
}
/* unset attribute when no value's given */
if (options[i].len == plen) {
nattrs.flags &= ~BIT(type);
memset(nattr_ptrs[type], 0,
type <= LWM2M_ATTR_PMAX ?
sizeof(s32_t) : sizeof(float32_value_t));
continue;
}
/* gt/lt/st cannot be assigned to obj/obj_inst unless unset */
if (plen == 2 && path->level <= 2) {
return -EEXIST;
}
vlen = options[i].len - plen - 1;
memcpy(opt_buf, options[i].value + plen + 1, vlen);
opt_buf[vlen] = '\0';
/* convert value to integer or float */
if (plen == 4) {
char *end;
long int v;
/* pmin/pmax: integer (sec 5.1.2)
* however, negative is non-sense
*/
errno = 0;
v = strtol(opt_buf, &end, 10);
if (errno || *end || v > INT_MAX || v < 0) {
ret = -EINVAL;
}
val.val1 = v;
} else {
/* gt/lt/st: type float */
ret = atof32(opt_buf, &val);
}
if (ret < 0) {
SYS_LOG_ERR("invalid attr[%s] value",
LWM2M_ATTR_STR[type]);
/* bad request */
return -EEXIST;
}
if (type <= LWM2M_ATTR_PMAX) {
*(s32_t *)nattr_ptrs[type] = val.val1;
} else {
memcpy(nattr_ptrs[type], &val, sizeof(float32_value_t));
}
nattrs.flags |= BIT(type);
}
if ((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) &&
nattrs.pmin > nattrs.pmax) {
SYS_LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax);
return -EEXIST;
}
if (nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) {
if (!((nattrs.lt.val1 < nattrs.gt.val1) ||
(nattrs.lt.val2 < nattrs.gt.val2))) {
SYS_LOG_DBG("lt > gt");
return -EEXIST;
}
if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) {
s32_t st1 = nattrs.st.val1 * 2 +
nattrs.st.val2 * 2 / 1000000;
s32_t st2 = nattrs.st.val2 * 2 % 1000000;
if (!(((nattrs.lt.val1 + st1) < nattrs.gt.val1) ||
((nattrs.lt.val2 + st2) < nattrs.gt.val2))) {
SYS_LOG_DBG("lt + 2*st > gt");
return -EEXIST;
}
}
}
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(attr_list, attr, tmp, node) {
type = attr->type;
if (!(BIT(type) & nattrs.flags)) {
SYS_LOG_DBG("Unset attr %s", LWM2M_ATTR_STR[type]);
sys_slist_remove(attr_list, prev_node, &attr->node);
memset(attr, 0, sizeof(*attr));
continue;
}
prev_node = &attr->node;
nattrs.flags &= ~BIT(type);
if (type <= LWM2M_ATTR_PMAX) {
attr->int_val = *(s32_t *)nattr_ptrs[type];
} else {
memcpy(&attr->float_val, nattr_ptrs[type],
sizeof(float32_value_t));
}
SYS_LOG_DBG("Update %s to %d.%06d", LWM2M_ATTR_STR[type],
attr->float_val.val1, attr->float_val.val2);
}
/* add attribute to obj/obj_inst/res */
for (type = 0; nattrs.flags && type < NR_LWM2M_ATTR; type++) {
int i;
if (!(BIT(type) & nattrs.flags)) {
continue;
}
/* grab an entry for newly added attribute */
for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
if (!write_attr_pool[i].used) {
break;
}
}
if (i == CONFIG_LWM2M_NUM_ATTR) {
return -ENOMEM;
}
attr = write_attr_pool + i;
attr->type = type;
attr->used = true;
if (type <= LWM2M_ATTR_PMAX) {
attr->int_val = *(s32_t *)nattr_ptrs[type];
} else {
memcpy(&attr->float_val, nattr_ptrs[type],
sizeof(float32_value_t));
}
sys_slist_append(attr_list, &attr->node);
nattrs.flags &= ~BIT(type);
SYS_LOG_DBG("Add %s to %d.%06d", LWM2M_ATTR_STR[type],
attr->float_val.val1, attr->float_val.val2);
}
return 0;
}
static int lwm2m_exec_handler(struct lwm2m_engine_obj *obj,

View file

@ -150,6 +150,9 @@ struct lwm2m_engine_obj {
u16_t max_instance_count;
lwm2m_engine_obj_create_cb_t create_cb;
lwm2m_engine_obj_delete_cb_t delete_cb;
/* runtime field attributes (lwm2m_attr) */
sys_slist_t attr_list;
};
#define INIT_OBJ_RES(res_var, index_var, id_val, multi_var, \
@ -179,13 +182,35 @@ struct lwm2m_engine_obj {
INIT_OBJ_RES(res_var, index_var, id_val, NULL, NULL, 0, \
NULL, NULL, NULL, ex_cb)
#define LWM2M_ATTR_PMIN 0
#define LWM2M_ATTR_PMAX 1
#define LWM2M_ATTR_GT 2
#define LWM2M_ATTR_LT 3
#define LWM2M_ATTR_STEP 4
#define NR_LWM2M_ATTR 5
/* TODO: support multiple server (sec 5.4.2) */
struct lwm2m_attr {
union {
float32_value_t float_val;
s32_t int_val;
};
sys_snode_t node;
u8_t type;
bool used;
};
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) */
/* runtime field attributes (lwm2m_attr) */
sys_slist_t attr_list;
/* callbacks set by user code on obj instance */
lwm2m_engine_get_data_cb_t read_cb;
@ -201,6 +226,9 @@ struct lwm2m_engine_obj_inst {
u16_t obj_inst_id;
struct lwm2m_engine_res_inst *resources;
u16_t resource_count;
/* runtime field attributes (lwm2m_attr) */
sys_slist_t attr_list;
};
struct lwm2m_output_context {