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:
parent
ff22595c31
commit
09fcd83b98
3 changed files with 381 additions and 3 deletions
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue