net: lwm2m: Add data validation callback

Add a data validation callback to the resource structure, which can be
registered by an application. It allows to verify the data before
actually modifying the resource data.

If the callback is registered for a resource, the data is decoded into a
temporary buffer first, and only copied into the actual resource buffer
if the validation is successfull. If no validation is required (and thus
no callback registered) the resource value is decoded directly into the
resource buffer, as it used to be.

Signed-off-by: Robert Lubos <robert.lubos@nordicsemi.no>
This commit is contained in:
Robert Lubos 2021-02-17 15:47:28 +01:00 committed by Anas Nashif
commit 31e5ec79b8
16 changed files with 192 additions and 59 deletions

View file

@ -124,6 +124,12 @@ struct lwm2m_ctx {
* callback in case of socket errors on receive. * callback in case of socket errors on receive.
*/ */
lwm2m_socket_fault_cb_t fault_cb; lwm2m_socket_fault_cb_t fault_cb;
/** Validation buffer. Used as a temporary buffer to decode the resource
* value before validation. On successful validation, its content is
* copied into the actual resource buffer.
*/
uint8_t validate_buf[CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE];
}; };
@ -159,6 +165,7 @@ typedef void *(*lwm2m_engine_get_data_cb_t)(uint16_t obj_inst_id,
* objects. * objects.
* *
* A function of this type can be registered via: * A function of this type can be registered via:
* lwm2m_engine_register_validate_callback()
* lwm2m_engine_register_post_write_callback() * lwm2m_engine_register_post_write_callback()
* *
* @param[in] obj_inst_id Object instance ID generating the callback. * @param[in] obj_inst_id Object instance ID generating the callback.
@ -746,6 +753,29 @@ int lwm2m_engine_register_read_callback(char *pathstr,
int lwm2m_engine_register_pre_write_callback(char *pathstr, int lwm2m_engine_register_pre_write_callback(char *pathstr,
lwm2m_engine_get_data_cb_t cb); lwm2m_engine_get_data_cb_t cb);
/**
* @brief Set resource (instance) validation callback
*
* This callback is triggered before setting the value of a resource to the
* resource data buffer.
*
* The callback allows an LwM2M client or object to validate the data before
* writing and notify an error if the data should be discarded for any reason
* (by returning a negative error code).
*
* @note All resources that have a validation callback registered are initially
* decoded into a temporary validation buffer. Make sure that
* ``CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE`` is large enough to
* store each of the validated resources (individually).
*
* @param[in] pathstr LwM2M path string "obj/obj-inst/res(/res-inst)"
* @param[in] cb Validate resource data callback
*
* @return 0 for success or negative in case of error.
*/
int lwm2m_engine_register_validate_callback(char *pathstr,
lwm2m_engine_set_data_cb_t cb);
/** /**
* @brief Set resource (instance) post-write callback * @brief Set resource (instance) post-write callback
* *

View file

@ -56,6 +56,16 @@ config LWM2M_ENGINE_MESSAGE_HEADER_SIZE
help help
Extra room allocated to handle CoAP header data Extra room allocated to handle CoAP header data
config LWM2M_ENGINE_VALIDATION_BUFFER_SIZE
int "Size of the validation buffer for the incoming data"
default 64
help
LwM2M will use the validation buffer during the write operation, to
decode the resource value before validating it (applies for resources
for which validation callback has been registered). Set this value to
the maximum expected size of the resources that need to be validated
(and thus have validation callback registered).
config LWM2M_ENGINE_MAX_PENDING config LWM2M_ENGINE_MAX_PENDING
int "LWM2M engine max. pending objects" int "LWM2M engine max. pending objects"
default 5 default 5

View file

@ -216,7 +216,7 @@ static struct lwm2m_engine_obj_inst *buzzer_create(uint16_t obj_inst_id)
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&buzzer_data[avail].onoff, &buzzer_data[avail].onoff,
sizeof(buzzer_data[avail].onoff), sizeof(buzzer_data[avail].onoff),
NULL, NULL, onoff_post_write_cb, NULL); NULL, NULL, NULL, onoff_post_write_cb, NULL);
INIT_OBJ_RES_DATA(BUZZER_LEVEL_ID, res[avail], i, res_inst[avail], j, INIT_OBJ_RES_DATA(BUZZER_LEVEL_ID, res[avail], i, res_inst[avail], j,
&buzzer_data[avail].level, &buzzer_data[avail].level,
sizeof(buzzer_data[avail].level)); sizeof(buzzer_data[avail].level));

View file

@ -207,7 +207,7 @@ static struct lwm2m_engine_obj_inst *generic_sensor_create(uint16_t obj_inst_id)
/* initialize instance resource data */ /* initialize instance resource data */
INIT_OBJ_RES(SENSOR_VALUE_RID, res[index], i, res_inst[index], j, 1, INIT_OBJ_RES(SENSOR_VALUE_RID, res[index], i, res_inst[index], j, 1,
false, true, &sensor_value[index], sizeof(*sensor_value), false, true, &sensor_value[index], sizeof(*sensor_value),
NULL, NULL, sensor_value_write_cb, NULL); NULL, NULL, NULL, sensor_value_write_cb, NULL);
INIT_OBJ_RES_DATA(SENSOR_UNITS_RID, res[index], i, res_inst[index], j, INIT_OBJ_RES_DATA(SENSOR_UNITS_RID, res[index], i, res_inst[index], j,
units[index], UNIT_STR_MAX_SIZE); units[index], UNIT_STR_MAX_SIZE);
INIT_OBJ_RES_DATA(MIN_MEASURED_VALUE_RID, res[index], i, INIT_OBJ_RES_DATA(MIN_MEASURED_VALUE_RID, res[index], i,

View file

@ -190,7 +190,7 @@ humidity_sensor_create(uint16_t obj_inst_id)
/* initialize instance resource data */ /* initialize instance resource data */
INIT_OBJ_RES(SENSOR_VALUE_RID, res[index], i, res_inst[index], j, 1, INIT_OBJ_RES(SENSOR_VALUE_RID, res[index], i, res_inst[index], j, 1,
false, true, &sensor_value[index], sizeof(*sensor_value), false, true, &sensor_value[index], sizeof(*sensor_value),
NULL, NULL, sensor_value_write_cb, NULL); NULL, NULL, NULL, sensor_value_write_cb, NULL);
INIT_OBJ_RES_DATA(SENSOR_UNITS_RID, res[index], i, res_inst[index], j, INIT_OBJ_RES_DATA(SENSOR_UNITS_RID, res[index], i, res_inst[index], j,
units[index], UNIT_STR_MAX_SIZE); units[index], UNIT_STR_MAX_SIZE);
INIT_OBJ_RES_DATA(MIN_MEASURED_VALUE_RID, res[index], i, INIT_OBJ_RES_DATA(MIN_MEASURED_VALUE_RID, res[index], i,

View file

@ -173,7 +173,7 @@ static struct lwm2m_engine_obj_inst *light_control_create(uint16_t obj_inst_id)
INIT_OBJ_RES(LIGHT_ON_TIME_ID, res[avail], i, INIT_OBJ_RES(LIGHT_ON_TIME_ID, res[avail], i,
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&on_time_value[avail], sizeof(*on_time_value), &on_time_value[avail], sizeof(*on_time_value),
on_time_read_cb, NULL, on_time_post_write_cb, NULL); on_time_read_cb, NULL, NULL, on_time_post_write_cb, NULL);
INIT_OBJ_RES_DATA(LIGHT_CUMULATIVE_ACTIVE_POWER_ID, res[avail], i, INIT_OBJ_RES_DATA(LIGHT_CUMULATIVE_ACTIVE_POWER_ID, res[avail], i,
res_inst[avail], j, res_inst[avail], j,
&cumulative_active_value[avail], &cumulative_active_value[avail],

View file

@ -214,17 +214,17 @@ static struct lwm2m_engine_obj_inst *switch_create(uint16_t obj_inst_id)
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&switch_data[avail].state, &switch_data[avail].state,
sizeof(switch_data[avail].state), sizeof(switch_data[avail].state),
NULL, NULL, state_post_write_cb, NULL); NULL, NULL, NULL, state_post_write_cb, NULL);
INIT_OBJ_RES_DATA(SWITCH_DIGITAL_INPUT_COUNTER_ID, res[avail], i, INIT_OBJ_RES_DATA(SWITCH_DIGITAL_INPUT_COUNTER_ID, res[avail], i,
res_inst[avail], j, res_inst[avail], j,
&switch_data[avail].counter, &switch_data[avail].counter,
sizeof(switch_data[avail].counter)); sizeof(switch_data[avail].counter));
INIT_OBJ_RES_OPT(SWITCH_ON_TIME_ID, res[avail], i, INIT_OBJ_RES_OPT(SWITCH_ON_TIME_ID, res[avail], i,
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
on_time_read_cb, NULL, time_post_write_cb, NULL); on_time_read_cb, NULL, NULL, time_post_write_cb, NULL);
INIT_OBJ_RES_OPT(SWITCH_OFF_TIME_ID, res[avail], i, INIT_OBJ_RES_OPT(SWITCH_OFF_TIME_ID, res[avail], i,
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
off_time_read_cb, NULL, time_post_write_cb, NULL); off_time_read_cb, NULL, NULL, time_post_write_cb, NULL);
INIT_OBJ_RES_OPTDATA(SWITCH_APPLICATION_TYPE_ID, res[avail], i, INIT_OBJ_RES_OPTDATA(SWITCH_APPLICATION_TYPE_ID, res[avail], i,
res_inst[avail], j); res_inst[avail], j);
#if ADD_TIMESTAMPS #if ADD_TIMESTAMPS

View file

@ -190,7 +190,7 @@ pressure_sensor_create(uint16_t obj_inst_id)
/* initialize instance resource data */ /* initialize instance resource data */
INIT_OBJ_RES(SENSOR_VALUE_RID, res[index], i, res_inst[index], j, 1, INIT_OBJ_RES(SENSOR_VALUE_RID, res[index], i, res_inst[index], j, 1,
false, true, &sensor_value[index], sizeof(*sensor_value), false, true, &sensor_value[index], sizeof(*sensor_value),
NULL, NULL, sensor_value_write_cb, NULL); NULL, NULL, NULL, sensor_value_write_cb, NULL);
INIT_OBJ_RES_DATA(SENSOR_UNITS_RID, res[index], i, res_inst[index], j, INIT_OBJ_RES_DATA(SENSOR_UNITS_RID, res[index], i, res_inst[index], j,
units[index], UNIT_STR_MAX_SIZE); units[index], UNIT_STR_MAX_SIZE);
INIT_OBJ_RES_DATA(MIN_MEASURED_VALUE_RID, res[index], i, INIT_OBJ_RES_DATA(MIN_MEASURED_VALUE_RID, res[index], i,

View file

@ -146,7 +146,7 @@ static struct lwm2m_engine_obj_inst *button_create(uint16_t obj_inst_id)
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&button_data[avail].state, &button_data[avail].state,
sizeof(button_data[avail].state), sizeof(button_data[avail].state),
NULL, NULL, state_post_write_cb, NULL); NULL, NULL, NULL, state_post_write_cb, NULL);
INIT_OBJ_RES_DATA(BUTTON_DIGITAL_INPUT_COUNTER_ID, res[avail], i, INIT_OBJ_RES_DATA(BUTTON_DIGITAL_INPUT_COUNTER_ID, res[avail], i,
res_inst[avail], j, res_inst[avail], j,
&button_data[avail].counter, &button_data[avail].counter,

View file

@ -205,7 +205,7 @@ static struct lwm2m_engine_obj_inst *temp_sensor_create(uint16_t obj_inst_id)
INIT_OBJ_RES(TEMP_SENSOR_VALUE_ID, res[index], i, INIT_OBJ_RES(TEMP_SENSOR_VALUE_ID, res[index], i,
res_inst[index], j, 1, false, true, res_inst[index], j, 1, false, true,
&sensor_value[index], sizeof(*sensor_value), &sensor_value[index], sizeof(*sensor_value),
NULL, NULL, sensor_value_write_cb, NULL); NULL, NULL, NULL, sensor_value_write_cb, NULL);
INIT_OBJ_RES_DATA(TEMP_UNITS_ID, res[index], i, res_inst[index], j, INIT_OBJ_RES_DATA(TEMP_UNITS_ID, res[index], i, res_inst[index], j,
units[index], TEMP_STRING_SHORT); units[index], TEMP_STRING_SHORT);
INIT_OBJ_RES_DATA(TEMP_MIN_MEASURED_VALUE_ID, res[index], i, INIT_OBJ_RES_DATA(TEMP_MIN_MEASURED_VALUE_ID, res[index], i,

View file

@ -344,7 +344,7 @@ static struct lwm2m_engine_obj_inst *timer_create(uint16_t obj_inst_id)
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&timer_data[avail].remaining_time, &timer_data[avail].remaining_time,
sizeof(timer_data[avail].remaining_time), sizeof(timer_data[avail].remaining_time),
remaining_time_read_cb, NULL, NULL, NULL); remaining_time_read_cb, NULL, NULL, NULL, NULL);
INIT_OBJ_RES_DATA(TIMER_MINIMUM_OFF_TIME_ID, res[avail], i, INIT_OBJ_RES_DATA(TIMER_MINIMUM_OFF_TIME_ID, res[avail], i,
res_inst[avail], j, &timer_data[avail].min_off_time, res_inst[avail], j, &timer_data[avail].min_off_time,
sizeof(timer_data[avail].min_off_time)); sizeof(timer_data[avail].min_off_time));
@ -354,12 +354,12 @@ static struct lwm2m_engine_obj_inst *timer_create(uint16_t obj_inst_id)
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&timer_data[avail].enabled, &timer_data[avail].enabled,
sizeof(timer_data[avail].enabled), sizeof(timer_data[avail].enabled),
NULL, NULL, enabled_post_write_cb, NULL); NULL, NULL, NULL, enabled_post_write_cb, NULL);
INIT_OBJ_RES(TIMER_CUMULATIVE_TIME_ID, res[avail], i, INIT_OBJ_RES(TIMER_CUMULATIVE_TIME_ID, res[avail], i,
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&timer_data[avail].cumulative_time, &timer_data[avail].cumulative_time,
sizeof(timer_data[avail].cumulative_time), sizeof(timer_data[avail].cumulative_time),
cumulative_time_read_cb, NULL, cumulative_time_read_cb, NULL, NULL,
cumulative_time_post_write_cb, NULL); cumulative_time_post_write_cb, NULL);
INIT_OBJ_RES_DATA(TIMER_DIGITAL_STATE_ID, res[avail], i, INIT_OBJ_RES_DATA(TIMER_DIGITAL_STATE_ID, res[avail], i,
res_inst[avail], j, &timer_data[avail].active, res_inst[avail], j, &timer_data[avail].active,
@ -368,7 +368,7 @@ static struct lwm2m_engine_obj_inst *timer_create(uint16_t obj_inst_id)
res_inst[avail], j, 1, false, true, res_inst[avail], j, 1, false, true,
&timer_data[avail].trigger_counter, &timer_data[avail].trigger_counter,
sizeof(timer_data[avail].trigger_counter), sizeof(timer_data[avail].trigger_counter),
NULL, NULL, trigger_counter_post_write_cb, NULL); NULL, NULL, NULL, trigger_counter_post_write_cb, NULL);
INIT_OBJ_RES_DATA(TIMER_MODE_ID, res[avail], i, res_inst[avail], j, INIT_OBJ_RES_DATA(TIMER_MODE_ID, res[avail], i, res_inst[avail], j,
&timer_data[avail].timer_mode, &timer_data[avail].timer_mode,
sizeof(timer_data[avail].timer_mode)); sizeof(timer_data[avail].timer_mode));

View file

@ -1500,6 +1500,15 @@ static int lwm2m_engine_set(char *pathstr, void *value, uint16_t len)
changed = true; changed = true;
} }
if (res->validate_cb) {
ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
res_inst->res_inst_id, value,
len, false, 0);
if (ret < 0) {
return -EINVAL;
}
}
switch (obj_field->data_type) { switch (obj_field->data_type) {
case LWM2M_RES_TYPE_OPAQUE: case LWM2M_RES_TYPE_OPAQUE:
@ -2103,6 +2112,21 @@ int lwm2m_engine_register_pre_write_callback(char *pathstr,
return 0; return 0;
} }
int lwm2m_engine_register_validate_callback(char *pathstr,
lwm2m_engine_set_data_cb_t cb)
{
int ret;
struct lwm2m_engine_res *res = NULL;
ret = lwm2m_engine_get_resource(pathstr, &res);
if (ret < 0) {
return ret;
}
res->validate_cb = cb;
return 0;
}
int lwm2m_engine_register_post_write_callback(char *pathstr, int lwm2m_engine_register_post_write_callback(char *pathstr,
lwm2m_engine_set_data_cb_t cb) lwm2m_engine_set_data_cb_t cb)
{ {
@ -2350,7 +2374,7 @@ size_t lwm2m_engine_get_opaque_more(struct lwm2m_input_context *in,
static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst, static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
struct lwm2m_engine_res *res, struct lwm2m_engine_res *res,
struct lwm2m_engine_res_inst *res_inst, struct lwm2m_engine_res_inst *res_inst,
struct lwm2m_input_context *in, struct lwm2m_message *msg,
void *data_ptr, size_t data_len) void *data_ptr, size_t data_len)
{ {
size_t len = 1; size_t len = 1;
@ -2358,22 +2382,51 @@ static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
int ret = 0; int ret = 0;
bool last_block = true; bool last_block = true;
struct lwm2m_opaque_context opaque_ctx = { 0 }; struct lwm2m_opaque_context opaque_ctx = { 0 };
void *write_buf;
size_t write_buf_len;
if (in->block_ctx != NULL) { if (msg->in.block_ctx != NULL) {
last_block = in->block_ctx->last_block; last_block = msg->in.block_ctx->last_block;
/* Restore the opaque context from the block context, if used. /* Restore the opaque context from the block context, if used.
*/ */
opaque_ctx = in->block_ctx->opaque; opaque_ctx = msg->in.block_ctx->opaque;
}
/* In case validation callback is present, write data to the temporary
* buffer first, for validation. Otherwise, write to the data buffer
* directly.
*/
if (res->validate_cb) {
write_buf = msg->ctx->validate_buf;
write_buf_len = sizeof(msg->ctx->validate_buf);
} else {
write_buf = data_ptr;
write_buf_len = data_len;
} }
while (!last_pkt_block && len > 0) { while (!last_pkt_block && len > 0) {
len = engine_get_opaque(in, (uint8_t *)data_ptr, len = engine_get_opaque(&msg->in, write_buf,
data_len, &opaque_ctx, &last_pkt_block); MIN(data_len, write_buf_len),
&opaque_ctx, &last_pkt_block);
if (len == 0) { if (len == 0) {
/* ignore empty content and continue */ /* ignore empty content and continue */
return 0; return 0;
} }
if (res->validate_cb) {
ret = res->validate_cb(
obj_inst->obj_inst_id, res->res_id,
res_inst->res_inst_id, write_buf, len,
last_pkt_block && last_block, opaque_ctx.len);
if (ret < 0) {
/* -EEXIST will generate Bad Request LWM2M response. */
return -EEXIST;
}
memcpy(data_ptr, write_buf, len);
}
if (res->post_write_cb) { if (res->post_write_cb) {
ret = res->post_write_cb( ret = res->post_write_cb(
obj_inst->obj_inst_id, res->res_id, obj_inst->obj_inst_id, res->res_id,
@ -2385,8 +2438,8 @@ static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
} }
} }
if (in->block_ctx != NULL) { if (msg->in.block_ctx != NULL) {
in->block_ctx->opaque = opaque_ctx; msg->in.block_ctx->opaque = opaque_ctx;
} }
return opaque_ctx.len; return opaque_ctx.len;
@ -2407,6 +2460,8 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
int32_t temp32 = 0; int32_t temp32 = 0;
int ret = 0; int ret = 0;
bool last_block = true; bool last_block = true;
void *write_buf;
size_t write_buf_len;
if (!obj_inst || !res || !res_inst || !obj_field || !msg) { if (!obj_inst || !res || !res_inst || !obj_field || !msg) {
return -EINVAL; return -EINVAL;
@ -2427,7 +2482,7 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
&data_len); &data_len);
} }
if (res->post_write_cb) { if (res->post_write_cb || res->validate_cb) {
if (msg->in.block_ctx != NULL) { if (msg->in.block_ctx != NULL) {
/* Get block_ctx for total_size (might be zero) */ /* Get block_ctx for total_size (might be zero) */
total_size = msg->in.block_ctx->ctx.total_size; total_size = msg->in.block_ctx->ctx.total_size;
@ -2439,12 +2494,24 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
} }
} }
/* In case validation callback is present, write data to the temporary
* buffer first, for validation. Otherwise, write to the data buffer
* directly.
*/
if (res->validate_cb) {
write_buf = msg->ctx->validate_buf;
write_buf_len = sizeof(msg->ctx->validate_buf);
} else {
write_buf = data_ptr;
write_buf_len = data_len;
}
if (data_ptr && data_len > 0) { if (data_ptr && data_len > 0) {
switch (obj_field->data_type) { switch (obj_field->data_type) {
case LWM2M_RES_TYPE_OPAQUE: case LWM2M_RES_TYPE_OPAQUE:
ret = lwm2m_write_handler_opaque(obj_inst, res, ret = lwm2m_write_handler_opaque(obj_inst, res,
res_inst, &msg->in, res_inst, msg,
data_ptr, data_len); data_ptr, data_len);
if (ret < 0) { if (ret < 0) {
return ret; return ret;
@ -2454,77 +2521,77 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
break; break;
case LWM2M_RES_TYPE_STRING: case LWM2M_RES_TYPE_STRING:
engine_get_string(&msg->in, (uint8_t *)data_ptr, data_len); engine_get_string(&msg->in, write_buf, write_buf_len);
len = strlen((char *)data_ptr); len = strlen((char *)write_buf);
break; break;
case LWM2M_RES_TYPE_U64: case LWM2M_RES_TYPE_U64:
engine_get_s64(&msg->in, &temp64); engine_get_s64(&msg->in, &temp64);
*(uint64_t *)data_ptr = temp64; *(uint64_t *)write_buf = temp64;
len = 8; len = 8;
break; break;
case LWM2M_RES_TYPE_U32: case LWM2M_RES_TYPE_U32:
case LWM2M_RES_TYPE_TIME: case LWM2M_RES_TYPE_TIME:
engine_get_s32(&msg->in, &temp32); engine_get_s32(&msg->in, &temp32);
*(uint32_t *)data_ptr = temp32; *(uint32_t *)write_buf = temp32;
len = 4; len = 4;
break; break;
case LWM2M_RES_TYPE_U16: case LWM2M_RES_TYPE_U16:
engine_get_s32(&msg->in, &temp32); engine_get_s32(&msg->in, &temp32);
*(uint16_t *)data_ptr = temp32; *(uint16_t *)write_buf = temp32;
len = 2; len = 2;
break; break;
case LWM2M_RES_TYPE_U8: case LWM2M_RES_TYPE_U8:
engine_get_s32(&msg->in, &temp32); engine_get_s32(&msg->in, &temp32);
*(uint8_t *)data_ptr = temp32; *(uint8_t *)write_buf = temp32;
len = 1; len = 1;
break; break;
case LWM2M_RES_TYPE_S64: case LWM2M_RES_TYPE_S64:
engine_get_s64(&msg->in, (int64_t *)data_ptr); engine_get_s64(&msg->in, (int64_t *)write_buf);
len = 8; len = 8;
break; break;
case LWM2M_RES_TYPE_S32: case LWM2M_RES_TYPE_S32:
engine_get_s32(&msg->in, (int32_t *)data_ptr); engine_get_s32(&msg->in, (int32_t *)write_buf);
len = 4; len = 4;
break; break;
case LWM2M_RES_TYPE_S16: case LWM2M_RES_TYPE_S16:
engine_get_s32(&msg->in, &temp32); engine_get_s32(&msg->in, &temp32);
*(int16_t *)data_ptr = temp32; *(int16_t *)write_buf = temp32;
len = 2; len = 2;
break; break;
case LWM2M_RES_TYPE_S8: case LWM2M_RES_TYPE_S8:
engine_get_s32(&msg->in, &temp32); engine_get_s32(&msg->in, &temp32);
*(int8_t *)data_ptr = temp32; *(int8_t *)write_buf = temp32;
len = 1; len = 1;
break; break;
case LWM2M_RES_TYPE_BOOL: case LWM2M_RES_TYPE_BOOL:
engine_get_bool(&msg->in, (bool *)data_ptr); engine_get_bool(&msg->in, (bool *)write_buf);
len = 1; len = 1;
break; break;
case LWM2M_RES_TYPE_FLOAT32: case LWM2M_RES_TYPE_FLOAT32:
engine_get_float32fix(&msg->in, engine_get_float32fix(&msg->in,
(float32_value_t *)data_ptr); (float32_value_t *)write_buf);
len = sizeof(float32_value_t); len = sizeof(float32_value_t);
break; break;
case LWM2M_RES_TYPE_FLOAT64: case LWM2M_RES_TYPE_FLOAT64:
engine_get_float64fix(&msg->in, engine_get_float64fix(&msg->in,
(float64_value_t *)data_ptr); (float64_value_t *)write_buf);
len = sizeof(float64_value_t); len = sizeof(float64_value_t);
break; break;
case LWM2M_RES_TYPE_OBJLNK: case LWM2M_RES_TYPE_OBJLNK:
engine_get_objlnk(&msg->in, engine_get_objlnk(&msg->in,
(struct lwm2m_objlnk *)data_ptr); (struct lwm2m_objlnk *)write_buf);
len = sizeof(struct lwm2m_objlnk); len = sizeof(struct lwm2m_objlnk);
break; break;
@ -2538,16 +2605,40 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
return -ENOENT; return -ENOENT;
} }
res_inst->data_len = len; if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) {
if (res->validate_cb) {
ret = res->validate_cb(
obj_inst->obj_inst_id, res->res_id,
res_inst->res_inst_id, write_buf, len,
last_block, total_size);
if (ret < 0) {
/* -EEXIST will generate Bad Request LWM2M response. */
return -EEXIST;
}
if (res->post_write_cb && if (len > data_len) {
obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) { LOG_ERR("Received data won't fit into provided "
ret = res->post_write_cb(obj_inst->obj_inst_id, "bufffer");
res->res_id, res_inst->res_inst_id, return -ENOMEM;
data_ptr, len, }
last_block, total_size);
if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
strncpy(data_ptr, write_buf, data_len);
} else {
memcpy(data_ptr, write_buf, len);
}
}
if (res->post_write_cb) {
ret = res->post_write_cb(
obj_inst->obj_inst_id, res->res_id,
res_inst->res_inst_id, data_ptr, len,
last_block, total_size);
}
} }
res_inst->data_len = len;
NOTIFY_OBSERVER_PATH(&msg->path); NOTIFY_OBSERVER_PATH(&msg->path);
return ret; return ret;

View file

@ -229,7 +229,7 @@ static struct lwm2m_engine_obj_inst *device_create(uint16_t obj_inst_id)
reset_error_list_cb); reset_error_list_cb);
INIT_OBJ_RES_OPT(DEVICE_CURRENT_TIME_ID, res, i, res_inst, j, 1, false, INIT_OBJ_RES_OPT(DEVICE_CURRENT_TIME_ID, res, i, res_inst, j, 1, false,
true, current_time_read_cb, current_time_pre_write_cb, true, current_time_read_cb, current_time_pre_write_cb,
current_time_post_write_cb, NULL); NULL, current_time_post_write_cb, NULL);
INIT_OBJ_RES_OPTDATA(DEVICE_UTC_OFFSET_ID, res, i, res_inst, j); INIT_OBJ_RES_OPTDATA(DEVICE_UTC_OFFSET_ID, res, i, res_inst, j);
INIT_OBJ_RES_OPTDATA(DEVICE_TIMEZONE_ID, res, i, res_inst, j); INIT_OBJ_RES_OPTDATA(DEVICE_TIMEZONE_ID, res, i, res_inst, j);
INIT_OBJ_RES_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, res, i, INIT_OBJ_RES_DATA(DEVICE_SUPPORTED_BINDING_MODES_ID, res, i,

View file

@ -320,10 +320,10 @@ static struct lwm2m_engine_obj_inst *firmware_create(uint16_t obj_inst_id)
/* initialize instance resource data */ /* initialize instance resource data */
INIT_OBJ_RES_OPT(FIRMWARE_PACKAGE_ID, res, i, res_inst, j, 1, false, INIT_OBJ_RES_OPT(FIRMWARE_PACKAGE_ID, res, i, res_inst, j, 1, false,
true, NULL, NULL, package_write_cb, NULL); true, NULL, NULL, NULL, package_write_cb, NULL);
INIT_OBJ_RES(FIRMWARE_PACKAGE_URI_ID, res, i, res_inst, j, 1, false, INIT_OBJ_RES(FIRMWARE_PACKAGE_URI_ID, res, i, res_inst, j, 1, false,
true, package_uri, PACKAGE_URI_LEN, true, package_uri, PACKAGE_URI_LEN,
NULL, NULL, package_uri_write_cb, NULL); NULL, NULL, NULL, package_uri_write_cb, NULL);
INIT_OBJ_RES_EXECUTE(FIRMWARE_UPDATE_ID, res, i, firmware_update_cb); INIT_OBJ_RES_EXECUTE(FIRMWARE_UPDATE_ID, res, i, firmware_update_cb);
INIT_OBJ_RES_DATA(FIRMWARE_STATE_ID, res, i, res_inst, j, INIT_OBJ_RES_DATA(FIRMWARE_STATE_ID, res, i, res_inst, j,
&update_state, sizeof(update_state)); &update_state, sizeof(update_state));

View file

@ -213,7 +213,7 @@ static struct lwm2m_engine_obj_inst *server_create(uint16_t obj_inst_id)
&server_id[index], sizeof(*server_id)); &server_id[index], sizeof(*server_id));
INIT_OBJ_RES(SERVER_LIFETIME_ID, res[index], i, res_inst[index], j, INIT_OBJ_RES(SERVER_LIFETIME_ID, res[index], i, res_inst[index], j,
1U, false, true, &lifetime[index], sizeof(*lifetime), 1U, false, true, &lifetime[index], sizeof(*lifetime),
NULL, NULL, lifetime_write_cb, NULL); NULL, NULL, NULL, lifetime_write_cb, NULL);
INIT_OBJ_RES_DATA(SERVER_DEFAULT_MIN_PERIOD_ID, res[index], i, INIT_OBJ_RES_DATA(SERVER_DEFAULT_MIN_PERIOD_ID, res[index], i,
res_inst[index], j, res_inst[index], j,
&default_min_period[index], &default_min_period[index],

View file

@ -198,13 +198,14 @@ struct lwm2m_engine_obj {
/* Resource macros */ /* Resource macros */
#define _INIT_OBJ_RES(_id, _r_ptr, _r_idx, _ri_ptr, _ri_count, _multi_ri, \ #define _INIT_OBJ_RES(_id, _r_ptr, _r_idx, _ri_ptr, _ri_count, _multi_ri, \
_r_cb, _pre_w_cb, _post_w_cb, _ex_cb) \ _r_cb, _pre_w_cb, _val_cb, _post_w_cb, _ex_cb) \
_r_ptr[_r_idx].res_id = _id; \ _r_ptr[_r_idx].res_id = _id; \
_r_ptr[_r_idx].res_instances = _ri_ptr; \ _r_ptr[_r_idx].res_instances = _ri_ptr; \
_r_ptr[_r_idx].res_inst_count = _ri_count; \ _r_ptr[_r_idx].res_inst_count = _ri_count; \
_r_ptr[_r_idx].multi_res_inst = _multi_ri; \ _r_ptr[_r_idx].multi_res_inst = _multi_ri; \
_r_ptr[_r_idx].read_cb = _r_cb; \ _r_ptr[_r_idx].read_cb = _r_cb; \
_r_ptr[_r_idx].pre_write_cb = _pre_w_cb; \ _r_ptr[_r_idx].pre_write_cb = _pre_w_cb; \
_r_ptr[_r_idx].validate_cb = _val_cb; \
_r_ptr[_r_idx].post_write_cb = _post_w_cb; \ _r_ptr[_r_idx].post_write_cb = _post_w_cb; \
_r_ptr[_r_idx].execute_cb = _ex_cb _r_ptr[_r_idx].execute_cb = _ex_cb
@ -253,11 +254,11 @@ struct lwm2m_engine_obj {
#define INIT_OBJ_RES(_id, _r_ptr, _r_idx, \ #define INIT_OBJ_RES(_id, _r_ptr, _r_idx, \
_ri_ptr, _ri_idx, _ri_count, _multi_ri, _ri_create, \ _ri_ptr, _ri_idx, _ri_count, _multi_ri, _ri_create, \
_data_ptr, _data_len, \ _data_ptr, _data_len, \
_r_cb, _pre_w_cb, _post_w_cb, _ex_cb) \ _r_cb, _pre_w_cb, _val_cb, _post_w_cb, _ex_cb) \
do { \ do { \
_INIT_OBJ_RES(_id, _r_ptr, _r_idx, \ _INIT_OBJ_RES(_id, _r_ptr, _r_idx, \
(_ri_ptr + _ri_idx), _ri_count, _multi_ri, \ (_ri_ptr + _ri_idx), _ri_count, _multi_ri, \
_r_cb, _pre_w_cb, _post_w_cb, _ex_cb); \ _r_cb, _pre_w_cb, _val_cb, _post_w_cb, _ex_cb); \
_INIT_OBJ_RES_INST(_ri_ptr, _ri_idx, _ri_count, _ri_create, \ _INIT_OBJ_RES_INST(_ri_ptr, _ri_idx, _ri_count, _ri_create, \
_data_ptr, _data_len); \ _data_ptr, _data_len); \
++_r_idx; \ ++_r_idx; \
@ -266,11 +267,11 @@ struct lwm2m_engine_obj {
#define INIT_OBJ_RES_OPT(_id, _r_ptr, _r_idx, \ #define INIT_OBJ_RES_OPT(_id, _r_ptr, _r_idx, \
_ri_ptr, _ri_idx, _ri_count, _multi_ri, _ri_create, \ _ri_ptr, _ri_idx, _ri_count, _multi_ri, _ri_create, \
_r_cb, _pre_w_cb, _post_w_cb, _ex_cb) \ _r_cb, _pre_w_cb, _val_cb, _post_w_cb, _ex_cb) \
do { \ do { \
_INIT_OBJ_RES(_id, _r_ptr, _r_idx, \ _INIT_OBJ_RES(_id, _r_ptr, _r_idx, \
(_ri_ptr + _ri_idx), _ri_count, _multi_ri, \ (_ri_ptr + _ri_idx), _ri_count, _multi_ri, \
_r_cb, _pre_w_cb, _post_w_cb, _ex_cb); \ _r_cb, _pre_w_cb, _val_cb, _post_w_cb, _ex_cb); \
_INIT_OBJ_RES_INST_OPT(_ri_ptr, _ri_idx, _ri_count, _ri_create); \ _INIT_OBJ_RES_INST_OPT(_ri_ptr, _ri_idx, _ri_count, _ri_create); \
++_r_idx; \ ++_r_idx; \
} while (false) } while (false)
@ -280,27 +281,27 @@ struct lwm2m_engine_obj {
_data_ptr, _data_len) \ _data_ptr, _data_len) \
INIT_OBJ_RES(_id, _r_ptr, _r_idx, \ INIT_OBJ_RES(_id, _r_ptr, _r_idx, \
_ri_ptr, _ri_idx, _ri_count, true, _ri_create, \ _ri_ptr, _ri_idx, _ri_count, true, _ri_create, \
_data_ptr, _data_len, NULL, NULL, NULL, NULL) _data_ptr, _data_len, NULL, NULL, NULL, NULL, NULL)
#define INIT_OBJ_RES_MULTI_OPTDATA(_id, _r_ptr, _r_idx, \ #define INIT_OBJ_RES_MULTI_OPTDATA(_id, _r_ptr, _r_idx, \
_ri_ptr, _ri_idx, _ri_count, _ri_create) \ _ri_ptr, _ri_idx, _ri_count, _ri_create) \
INIT_OBJ_RES_OPT(_id, _r_ptr, _r_idx, \ INIT_OBJ_RES_OPT(_id, _r_ptr, _r_idx, \
_ri_ptr, _ri_idx, _ri_count, true, _ri_create, \ _ri_ptr, _ri_idx, _ri_count, true, _ri_create, \
NULL, NULL, NULL, NULL) NULL, NULL, NULL, NULL, NULL)
#define INIT_OBJ_RES_DATA(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx, \ #define INIT_OBJ_RES_DATA(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx, \
_data_ptr, _data_len) \ _data_ptr, _data_len) \
INIT_OBJ_RES(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx, 1U, false, true, \ INIT_OBJ_RES(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx, 1U, false, true, \
_data_ptr, _data_len, NULL, NULL, NULL, NULL) _data_ptr, _data_len, NULL, NULL, NULL, NULL, NULL)
#define INIT_OBJ_RES_OPTDATA(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx) \ #define INIT_OBJ_RES_OPTDATA(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx) \
INIT_OBJ_RES_OPT(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx, 1U, false, \ INIT_OBJ_RES_OPT(_id, _r_ptr, _r_idx, _ri_ptr, _ri_idx, 1U, false, \
true, NULL, NULL, NULL, NULL) true, NULL, NULL, NULL, NULL, NULL)
#define INIT_OBJ_RES_EXECUTE(_id, _r_ptr, _r_idx, _ex_cb) \ #define INIT_OBJ_RES_EXECUTE(_id, _r_ptr, _r_idx, _ex_cb) \
do { \ do { \
_INIT_OBJ_RES(_id, _r_ptr, _r_idx, NULL, 0, false, \ _INIT_OBJ_RES(_id, _r_ptr, _r_idx, NULL, 0, false, \
NULL, NULL, NULL, _ex_cb); \ NULL, NULL, NULL, NULL, _ex_cb); \
++_r_idx; \ ++_r_idx; \
} while (false) } while (false)
@ -336,6 +337,7 @@ struct lwm2m_engine_res_inst {
struct lwm2m_engine_res { struct lwm2m_engine_res {
lwm2m_engine_get_data_cb_t read_cb; lwm2m_engine_get_data_cb_t read_cb;
lwm2m_engine_get_data_cb_t pre_write_cb; lwm2m_engine_get_data_cb_t pre_write_cb;
lwm2m_engine_set_data_cb_t validate_cb;
lwm2m_engine_set_data_cb_t post_write_cb; lwm2m_engine_set_data_cb_t post_write_cb;
lwm2m_engine_execute_cb_t execute_cb; lwm2m_engine_execute_cb_t execute_cb;