net: lwm2m: add firmware push support

1. Add handling block1 option in handle_request(). The basic idea is
   to declare structure block_context at compiled time and use "token"
   as a key to pick up the on-going block cotext. It should be able to
   support multiple blockwise transfer concurrently
2. Use write callback implemented in lwm2m_obj_firmware to deal w/ the
   update state transition and than call the callback registered by the
   application
3. move default_block_size to lwm2m_engine.c to share between
   lwm2m_engine and lwm2m_obj_firmware_pull

Signed-off-by: Robert Chou <robert.ch.chou@acer.com>
[michael.scott@linaro.org: rebased on LwM2M net_app changes.]
Signed-off-by: Michael Scott <michael.scott@linaro.org>
This commit is contained in:
Robert Chou 2017-07-25 16:54:25 +08:00 committed by Anas Nashif
commit b93eb8dee1
7 changed files with 292 additions and 84 deletions

View file

@ -123,10 +123,10 @@ int lwm2m_device_add_err(u8_t error_code);
#define RESULT_UNSUP_PROTO 9
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT)
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT)
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);
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT)
void lwm2m_firmware_set_update_cb(lwm2m_engine_exec_cb_t cb);
lwm2m_engine_exec_cb_t lwm2m_firmware_get_update_cb(void);
#endif

View file

@ -147,6 +147,7 @@ static int device_factory_default_cb(u16_t obj_inst_id)
return 1;
}
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT)
static int firmware_update_cb(u16_t obj_inst_id)
{
SYS_LOG_DBG("UPDATE");
@ -160,7 +161,9 @@ static int firmware_update_cb(u16_t obj_inst_id)
lwm2m_engine_set_u8("5/0/5", RESULT_SUCCESS);
return 1;
}
#endif
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT)
static int firmware_block_received_cb(u16_t obj_inst_id,
u8_t *data, u16_t data_len,
bool last_block, size_t total_size)
@ -169,6 +172,7 @@ static int firmware_block_received_cb(u16_t obj_inst_id,
data_len, last_block);
return 1;
}
#endif
static int lwm2m_setup(void)
{
@ -212,10 +216,12 @@ static int lwm2m_setup(void)
/* setup FIRMWARE object */
lwm2m_engine_register_post_write_callback("5/0/0",
firmware_block_received_cb);
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT)
lwm2m_firmware_set_write_cb(firmware_block_received_cb);
#endif
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT)
lwm2m_firmware_set_update_cb(firmware_update_cb);
#endif
/* setup TEMP SENSOR object */

View file

@ -137,17 +137,23 @@ config LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
block transfer and "FIRMWARE PACKAGE URI" resource. This option
adds another UDP context and packet handling.
config LWM2M_FIRMWARE_UPDATE_PULL_COAP_BLOCK_SIZE
int "Firmware Update object pull CoAP block size"
depends on LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
config LWM2M_COAP_BLOCK_SIZE
int "LWM2M CoAP block-wise transfer size"
default 256
default 64 if NET_L2_BT
default 64 if NET_L2_IEEE802154
range 16 1024
help
CoAP block size used by firmware pull when performing block-wise
CoAP block size used by LWM2M when performing block-wise
transfers. Possible values: 16, 32, 64, 128, 256, 512 and 1024.
config LWM2M_NUM_BLOCK1_CONTEXT
int "Maximum # of LWM2M block1 contexts"
default 3
help
This value sets up the maximum number of block1 contexts for
CoAP block-wise transfer we can handle at the same time.
config LWM2M_RW_JSON_SUPPORT
bool "support for JSON writer"
default y

View file

@ -16,8 +16,6 @@
*
* - Use server / security object instance 0 for initial connection
* - Add DNS support for security uri parsing
* - Block-transfer support / Large response messages
* (use Block2 to limit message size to 64 bytes for 6LOWPAN compat.)
* - BOOTSTRAP/DTLS cleanup
* - Handle WRITE_ATTRIBUTES (pmin=10&pmax=60)
* - Handle Resource ObjLink type
@ -102,6 +100,24 @@ static sys_slist_t engine_obj_list;
static sys_slist_t engine_obj_inst_list;
static sys_slist_t engine_observer_list;
#define NUM_BLOCK1_CONTEXT CONFIG_LWM2M_NUM_BLOCK1_CONTEXT
/* TODO: figure out what's correct value */
#define TIMEOUT_BLOCKWISE_TRANSFER K_SECONDS(30)
#define GET_BLOCK_NUM(v) ((v) >> 4)
#define GET_BLOCK_SIZE(v) (((v) & 0x7))
#define GET_MORE(v) (!!((v) & 0x08))
struct block_context {
struct zoap_block_context ctx;
s64_t timestamp;
u8_t token[8];
u8_t tkl;
};
static struct block_context block1_contexts[NUM_BLOCK1_CONTEXT];
/* periodic / notify / observe handling stack */
static K_THREAD_STACK_DEFINE(engine_thread_stack,
CONFIG_LWM2M_ENGINE_STACK_SIZE);
@ -160,6 +176,101 @@ static char *sprint_token(const u8_t *token, u8_t tkl)
}
#endif
/* block-wise transfer functions */
enum zoap_block_size lwm2m_default_block_size(void)
{
switch (CONFIG_LWM2M_COAP_BLOCK_SIZE) {
case 16:
return ZOAP_BLOCK_16;
case 32:
return ZOAP_BLOCK_32;
case 64:
return ZOAP_BLOCK_64;
case 128:
return ZOAP_BLOCK_128;
case 256:
return ZOAP_BLOCK_256;
case 512:
return ZOAP_BLOCK_512;
case 1024:
return ZOAP_BLOCK_1024;
}
return ZOAP_BLOCK_256;
}
static int
init_block_ctx(const u8_t *token, u8_t tkl, struct block_context **ctx)
{
int i;
s64_t timestamp;
*ctx = NULL;
timestamp = k_uptime_get();
for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
if (block1_contexts[i].tkl == 0) {
*ctx = &block1_contexts[i];
break;
}
if (timestamp - block1_contexts[i].timestamp >
TIMEOUT_BLOCKWISE_TRANSFER) {
*ctx = &block1_contexts[i];
/* TODO: notify application for block
* transfer timeout
*/
break;
}
}
if (*ctx == NULL) {
SYS_LOG_ERR("Cannot find free block context");
return -ENOMEM;
}
(*ctx)->tkl = tkl;
memcpy((*ctx)->token, token, tkl);
zoap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0);
(*ctx)->timestamp = timestamp;
return 0;
}
static int
get_block_ctx(const u8_t *token, u8_t tkl, struct block_context **ctx)
{
int i;
*ctx = NULL;
for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
if (block1_contexts[i].tkl == tkl &&
memcmp(token, block1_contexts[i].token, tkl) == 0) {
*ctx = &block1_contexts[i];
/* refresh timestmap */
(*ctx)->timestamp = k_uptime_get();
break;
}
}
if (*ctx == NULL) {
SYS_LOG_ERR("Cannot find block context");
return -ENOENT;
}
return 0;
}
static void free_block_ctx(struct block_context *ctx)
{
if (ctx == NULL) {
return;
}
ctx->tkl = 0;
}
/* observer functions */
int lwm2m_notify_observer(u16_t obj_id, u16_t obj_inst_id, u16_t res_id)
@ -530,6 +641,20 @@ int lwm2m_delete_obj_inst(u16_t obj_id, u16_t obj_inst_id)
/* utility functions */
static int get_option_int(const struct zoap_packet *zpkt, u8_t opt)
{
struct zoap_option option = {};
u16_t count = 1;
int r;
r = zoap_find_options(zpkt, opt, &option, count);
if (r <= 0) {
return -ENOENT;
}
return zoap_option_value_to_int(&option);
}
static void engine_clear_context(struct lwm2m_engine_context *context)
{
if (context->in) {
@ -1698,6 +1823,12 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
void *data_ptr = NULL;
size_t data_len = 0;
size_t len = 0;
size_t total_size = 0;
int ret = 0;
u8_t tkl = 0;
const u8_t *token;
bool last_block = true;
struct block_context *block_ctx = NULL;
if (!obj_inst || !res || !obj_field || !context) {
return -EINVAL;
@ -1719,8 +1850,6 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, &data_len);
}
/* TODO: check for block transfer fields here */
if (data_ptr && data_len > 0) {
switch (obj_field->data_type) {
@ -1808,14 +1937,30 @@ int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
}
if (res->post_write_cb) {
/* Get block1 option for checking MORE block flag */
ret = get_option_int(in->in_zpkt, ZOAP_OPTION_BLOCK1);
if (ret >= 0) {
last_block = !GET_MORE(ret);
/* Get block_ctx for total_size (might be zero) */
token = zoap_header_get_token(in->in_zpkt, &tkl);
if (token != NULL &&
!get_block_ctx(token, tkl, &block_ctx)) {
total_size = block_ctx->ctx.total_size;
}
}
/* ignore return value here */
res->post_write_cb(obj_inst->obj_inst_id, data_ptr, len,
false, 0);
ret = res->post_write_cb(obj_inst->obj_inst_id, data_ptr, len,
last_block, total_size);
if (ret >= 0) {
ret = 0;
}
}
NOTIFY_OBSERVER_PATH(path);
return 0;
return ret;
}
static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj,
@ -2103,20 +2248,6 @@ static int do_write_op(struct lwm2m_engine_obj *obj,
}
}
static int get_observe_option(const struct zoap_packet *zpkt)
{
struct zoap_option option = {};
u16_t count = 1;
int r;
r = zoap_find_options(zpkt, ZOAP_OPTION_OBSERVE, &option, count);
if (r <= 0) {
return -ENOENT;
}
return zoap_option_value_to_int(&option);
}
static int handle_request(struct zoap_packet *request,
struct lwm2m_message *msg)
{
@ -2133,6 +2264,9 @@ static int handle_request(struct zoap_packet *request,
struct lwm2m_engine_context context;
int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
bool discover = false;
struct block_context *block_ctx = NULL;
size_t block_offset = 0;
enum zoap_block_size block_size;
/* setup engine context */
memset(&context, 0, sizeof(struct lwm2m_engine_context));
@ -2208,7 +2342,7 @@ static int handle_request(struct zoap_packet *request,
context.operation = LWM2M_OP_READ;
}
/* check for observe */
observe = get_observe_option(in.in_zpkt);
observe = get_option_int(in.in_zpkt, ZOAP_OPTION_OBSERVE);
zoap_header_set_code(out.out_zpkt, ZOAP_RESPONSE_CODE_CONTENT);
break;
@ -2245,7 +2379,31 @@ static int handle_request(struct zoap_packet *request,
in.inpos = 0;
in.inbuf = zoap_packet_get_payload(in.in_zpkt, &in.insize);
/* TODO: check for block transfer? */
/* Check for block transfer */
r = get_option_int(in.in_zpkt, ZOAP_OPTION_BLOCK1);
if (r > 0) {
/* RFC7252: 4.6. Message Size */
block_size = GET_BLOCK_SIZE(r);
if (GET_MORE(r) &&
zoap_block_size_to_bytes(block_size) > in.insize) {
SYS_LOG_DBG("Trailing payload is discarded!");
r = -EFBIG;
goto error;
}
if (GET_BLOCK_NUM(r) == 0) {
r = init_block_ctx(token, tkl, &block_ctx);
} else {
r = get_block_ctx(token, tkl, &block_ctx);
}
if (r < 0) {
goto error;
}
/* 0 will be returned if it's the last block */
block_offset = zoap_next_block(in.in_zpkt, &block_ctx->ctx);
}
switch (context.operation) {
@ -2315,8 +2473,30 @@ static int handle_request(struct zoap_packet *request,
return -EINVAL;
}
if (r == 0) {
/* TODO: Handle blockwise 1 */
if (r) {
goto error;
}
/* Handle blockwise 1 */
if (block_ctx) {
if (block_offset > 0) {
/* More to come, ack with correspond block # */
r = zoap_add_block1_option(
out.out_zpkt, &block_ctx->ctx);
if (r) {
/* report as internal server error */
SYS_LOG_ERR("Fail adding block1 option: %d", r);
r = -EINVAL;
goto error;
} else {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_CONTINUE);
}
} else {
/* Free context when finished */
free_block_ctx(block_ctx);
}
}
if (out.outlen > 0) {
SYS_LOG_DBG("replying with %u bytes", out.outlen);
@ -2324,28 +2504,32 @@ static int handle_request(struct zoap_packet *request,
} else {
SYS_LOG_DBG("no data in reply");
}
} else {
return 0;
error:
if (r == -ENOENT) {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_NOT_FOUND);
r = 0;
} else if (r == -EPERM) {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_NOT_ALLOWED);
r = 0;
} else if (r == -EEXIST) {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_BAD_REQUEST);
r = 0;
} else if (r == -EFBIG) {
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_REQUEST_TOO_LARGE);
} else {
/* Failed to handle the request */
zoap_header_set_code(out.out_zpkt,
ZOAP_RESPONSE_CODE_INTERNAL_ERROR);
r = 0;
}
}
return r;
/* Free block context when error happened */
free_block_ctx(block_ctx);
return 0;
}
void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
@ -2788,6 +2972,9 @@ error_start:
static int lwm2m_engine_init(struct device *dev)
{
memset(block1_contexts, 0,
sizeof(struct block_context) * NUM_BLOCK1_CONTEXT);
/* start thread to handle OBSERVER / NOTIFY events */
k_thread_create(&engine_thread_data,
&engine_thread_stack[0],

View file

@ -103,6 +103,8 @@ void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, struct net_pkt *pkt,
bool handle_separate_response,
udp_request_handler_cb_t udp_request_handler);
enum zoap_block_size lwm2m_default_block_size(void);
#if defined(CONFIG_LWM2M_FIRMWARE_UPDATE_OBJ_SUPPORT)
u8_t lwm2m_firmware_get_update_state(void);
void lwm2m_firmware_set_update_state(u8_t state);

View file

@ -181,13 +181,41 @@ 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);
u8_t state;
int ret = 0;
state = lwm2m_firmware_get_update_state();
if (state == STATE_IDLE) {
/* TODO: setup timer to check download status,
* make sure it fail after timeout
*/
lwm2m_firmware_set_update_state(STATE_DOWNLOADING);
} else if (state != STATE_DOWNLOADING) {
if (data_len == 0 && state == STATE_DOWNLOADED) {
/* reset to state idle and result default */
lwm2m_firmware_set_update_result(RESULT_DEFAULT);
return 1;
}
return 0;
SYS_LOG_DBG("Cannot download: state = %d", state);
return -EPERM;
}
if (write_cb) {
ret = write_cb(obj_inst_id, data, data_len,
last_block, total_size);
if (ret < 0) {
SYS_LOG_ERR("Failed to store firmware: %d", ret);
lwm2m_firmware_set_update_result(
RESULT_INTEGRITY_FAILED);
}
}
if (last_block) {
lwm2m_firmware_set_update_state(STATE_DOWNLOADED);
}
return 1;
}
static int package_uri_write_cb(u16_t obj_inst_id,

View file

@ -225,28 +225,6 @@ do_firmware_transfer_reply_cb(const struct zoap_packet *response,
return ret;
}
static enum zoap_block_size default_block_size(void)
{
switch (CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_COAP_BLOCK_SIZE) {
case 16:
return ZOAP_BLOCK_16;
case 32:
return ZOAP_BLOCK_32;
case 64:
return ZOAP_BLOCK_64;
case 128:
return ZOAP_BLOCK_128;
case 256:
return ZOAP_BLOCK_256;
case 512:
return ZOAP_BLOCK_512;
case 1024:
return ZOAP_BLOCK_1024;
}
return ZOAP_BLOCK_256;
}
static void firmware_transfer(struct k_work *work)
{
int ret, family;
@ -325,7 +303,8 @@ static void firmware_transfer(struct k_work *work)
}
/* reset block transfer context */
zoap_block_transfer_init(&firmware_block_ctx, default_block_size(), 0);
zoap_block_transfer_init(&firmware_block_ctx,
lwm2m_default_block_size(), 0);
transfer_request(&firmware_block_ctx, NULL, 0,
do_firmware_transfer_reply_cb);
return;