2017-07-07 20:04:03 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2017 Linaro Limited
|
2019-01-25 23:13:07 +01:00
|
|
|
* Copyright (c) 2018-2019 Foundries.io
|
2017-07-07 20:04:03 +02:00
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
2018-09-19 10:22:19 +02:00
|
|
|
#define LOG_MODULE_NAME net_lwm2m_obj_firmware
|
|
|
|
#define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
|
|
|
|
|
|
|
|
#include <logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME);
|
|
|
|
|
2017-07-07 20:04:03 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <init.h>
|
|
|
|
|
|
|
|
#include "lwm2m_object.h"
|
|
|
|
#include "lwm2m_engine.h"
|
|
|
|
|
|
|
|
/* Firmware resource IDs */
|
|
|
|
#define FIRMWARE_PACKAGE_ID 0
|
2017-10-11 20:39:37 +02:00
|
|
|
#define FIRMWARE_PACKAGE_URI_ID 1
|
2017-07-07 20:04:03 +02:00
|
|
|
#define FIRMWARE_UPDATE_ID 2
|
|
|
|
#define FIRMWARE_STATE_ID 3
|
|
|
|
#define FIRMWARE_UPDATE_RESULT_ID 5
|
2018-05-01 02:27:28 +02:00
|
|
|
#define FIRMWARE_PACKAGE_NAME_ID 6
|
|
|
|
#define FIRMWARE_PACKAGE_VERSION_ID 7
|
2017-07-07 20:04:03 +02:00
|
|
|
#define FIRMWARE_UPDATE_PROTO_SUPPORT_ID 8 /* TODO */
|
|
|
|
#define FIRMWARE_UPDATE_DELIV_METHOD_ID 9
|
|
|
|
|
|
|
|
#define FIRMWARE_MAX_ID 10
|
|
|
|
|
|
|
|
#define DELIVERY_METHOD_PULL_ONLY 0
|
|
|
|
#define DELIVERY_METHOD_PUSH_ONLY 1
|
|
|
|
#define DELIVERY_METHOD_BOTH 2
|
|
|
|
|
|
|
|
#define PACKAGE_URI_LEN 255
|
|
|
|
|
|
|
|
/* resource state variables */
|
|
|
|
static u8_t update_state;
|
|
|
|
static u8_t update_result;
|
|
|
|
static u8_t delivery_method;
|
|
|
|
static char package_uri[PACKAGE_URI_LEN];
|
|
|
|
|
|
|
|
/* only 1 instance of firmware object exists */
|
|
|
|
static struct lwm2m_engine_obj firmware;
|
|
|
|
static struct lwm2m_engine_obj_field fields[] = {
|
2019-07-29 19:06:00 +02:00
|
|
|
OBJ_FIELD_DATA(FIRMWARE_PACKAGE_ID, W, OPAQUE),
|
|
|
|
OBJ_FIELD_DATA(FIRMWARE_PACKAGE_URI_ID, RW, STRING),
|
2017-07-07 20:04:03 +02:00
|
|
|
OBJ_FIELD_EXECUTE(FIRMWARE_UPDATE_ID),
|
|
|
|
OBJ_FIELD_DATA(FIRMWARE_STATE_ID, R, U8),
|
|
|
|
OBJ_FIELD_DATA(FIRMWARE_UPDATE_RESULT_ID, R, U8),
|
2018-05-01 02:27:28 +02:00
|
|
|
OBJ_FIELD_DATA(FIRMWARE_PACKAGE_NAME_ID, R_OPT, STRING),
|
|
|
|
OBJ_FIELD_DATA(FIRMWARE_PACKAGE_VERSION_ID, R_OPT, STRING),
|
2019-07-29 19:06:00 +02:00
|
|
|
OBJ_FIELD_DATA(FIRMWARE_UPDATE_PROTO_SUPPORT_ID, R_OPT, U8),
|
2017-07-07 20:04:03 +02:00
|
|
|
OBJ_FIELD_DATA(FIRMWARE_UPDATE_DELIV_METHOD_ID, R, U8)
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct lwm2m_engine_obj_inst inst;
|
|
|
|
static struct lwm2m_engine_res_inst res[FIRMWARE_MAX_ID];
|
|
|
|
|
|
|
|
static lwm2m_engine_set_data_cb_t write_cb;
|
2018-07-12 19:00:00 +02:00
|
|
|
static lwm2m_engine_user_cb_t update_cb;
|
2017-07-07 20:04:03 +02:00
|
|
|
|
|
|
|
#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
|
|
|
|
extern int lwm2m_firmware_start_transfer(char *package_uri);
|
|
|
|
#endif
|
|
|
|
|
2017-07-05 17:07:41 +02:00
|
|
|
u8_t lwm2m_firmware_get_update_state(void)
|
|
|
|
{
|
|
|
|
return update_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwm2m_firmware_set_update_state(u8_t state)
|
|
|
|
{
|
|
|
|
bool error = false;
|
|
|
|
|
|
|
|
/* Check LWM2M SPEC appendix E.6.1 */
|
|
|
|
switch (state) {
|
|
|
|
case STATE_DOWNLOADING:
|
|
|
|
if (update_state != STATE_IDLE) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_DOWNLOADED:
|
|
|
|
if (update_state != STATE_DOWNLOADING &&
|
|
|
|
update_state != STATE_UPDATING) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_UPDATING:
|
|
|
|
if (update_state != STATE_DOWNLOADED) {
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_IDLE:
|
|
|
|
break;
|
|
|
|
default:
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_ERR("Unhandled state: %u", state);
|
2017-07-05 17:07:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_ERR("Invalid state transition: %u -> %u",
|
|
|
|
update_state, state);
|
2017-07-05 17:07:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
update_state = state;
|
|
|
|
NOTIFY_OBSERVER(LWM2M_OBJECT_FIRMWARE_ID, 0, FIRMWARE_STATE_ID);
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_DBG("Update state = %d", update_state);
|
2017-07-05 17:07:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u8_t lwm2m_firmware_get_update_result(void)
|
|
|
|
{
|
|
|
|
return update_result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void lwm2m_firmware_set_update_result(u8_t result)
|
|
|
|
{
|
|
|
|
u8_t state;
|
|
|
|
bool error = false;
|
|
|
|
|
|
|
|
/* Check LWM2M SPEC appendix E.6.1 */
|
|
|
|
switch (result) {
|
|
|
|
case RESULT_DEFAULT:
|
|
|
|
lwm2m_firmware_set_update_state(STATE_IDLE);
|
|
|
|
break;
|
|
|
|
case RESULT_SUCCESS:
|
|
|
|
if (update_state != STATE_UPDATING) {
|
|
|
|
error = true;
|
|
|
|
state = update_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwm2m_firmware_set_update_state(STATE_IDLE);
|
|
|
|
break;
|
|
|
|
case RESULT_NO_STORAGE:
|
|
|
|
case RESULT_OUT_OF_MEM:
|
|
|
|
case RESULT_CONNECTION_LOST:
|
|
|
|
case RESULT_UNSUP_FW:
|
|
|
|
case RESULT_INVALID_URI:
|
|
|
|
case RESULT_UNSUP_PROTO:
|
|
|
|
if (update_state != STATE_DOWNLOADING) {
|
|
|
|
error = true;
|
|
|
|
state = update_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwm2m_firmware_set_update_state(STATE_IDLE);
|
|
|
|
break;
|
|
|
|
case RESULT_INTEGRITY_FAILED:
|
|
|
|
if (update_state != STATE_DOWNLOADING &&
|
|
|
|
update_state != STATE_UPDATING) {
|
|
|
|
error = true;
|
|
|
|
state = update_state;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwm2m_firmware_set_update_state(STATE_IDLE);
|
|
|
|
break;
|
|
|
|
case RESULT_UPDATE_FAILED:
|
2019-01-28 19:57:17 +01:00
|
|
|
if (update_state != STATE_DOWNLOADING &&
|
|
|
|
update_state != STATE_UPDATING) {
|
2017-07-05 17:07:41 +02:00
|
|
|
error = true;
|
|
|
|
state = update_state;
|
|
|
|
}
|
|
|
|
|
2019-01-28 19:57:17 +01:00
|
|
|
lwm2m_firmware_set_update_state(STATE_IDLE);
|
2017-07-05 17:07:41 +02:00
|
|
|
break;
|
|
|
|
default:
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_ERR("Unhandled result: %u", result);
|
2017-07-05 17:07:41 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error) {
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_ERR("Unexpected result(%u) set while state is %u",
|
|
|
|
result, state);
|
2017-07-05 17:07:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
update_result = result;
|
|
|
|
NOTIFY_OBSERVER(LWM2M_OBJECT_FIRMWARE_ID, 0, FIRMWARE_UPDATE_RESULT_ID);
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_DBG("Update result = %d", update_result);
|
2017-07-05 17:07:41 +02:00
|
|
|
}
|
|
|
|
|
2017-07-07 20:04:03 +02:00
|
|
|
static int package_write_cb(u16_t obj_inst_id,
|
|
|
|
u8_t *data, u16_t data_len,
|
|
|
|
bool last_block, size_t total_size)
|
|
|
|
{
|
2017-07-25 10:54:25 +02:00
|
|
|
u8_t state;
|
2017-11-02 11:36:24 +01:00
|
|
|
int ret;
|
2017-07-25 10:54:25 +02:00
|
|
|
|
|
|
|
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) {
|
2019-03-27 02:57:45 +01:00
|
|
|
if (data_len == 0U && state == STATE_DOWNLOADED) {
|
2017-07-25 10:54:25 +02:00
|
|
|
/* reset to state idle and result default */
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_DEFAULT);
|
2017-10-27 23:57:44 +02:00
|
|
|
return 0;
|
2017-07-25 10:54:25 +02:00
|
|
|
}
|
|
|
|
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_DBG("Cannot download: state = %d", state);
|
2017-07-25 10:54:25 +02:00
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
|
2017-11-02 11:36:24 +01:00
|
|
|
ret = write_cb ? write_cb(obj_inst_id, data, data_len,
|
|
|
|
last_block, total_size) : 0;
|
|
|
|
if (ret >= 0) {
|
|
|
|
if (last_block) {
|
|
|
|
lwm2m_firmware_set_update_state(STATE_DOWNLOADED);
|
2017-07-25 10:54:25 +02:00
|
|
|
}
|
|
|
|
|
2017-11-02 11:36:24 +01:00
|
|
|
return 0;
|
|
|
|
} else if (ret == -ENOMEM) {
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_OUT_OF_MEM);
|
|
|
|
} else if (ret == -ENOSPC) {
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_NO_STORAGE);
|
|
|
|
/* Response 4.13 (RFC7959, section 2.9.3) */
|
|
|
|
/* TODO: should include size1 option to indicate max size */
|
|
|
|
ret = -EFBIG;
|
|
|
|
} else if (ret == -EFAULT) {
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_INTEGRITY_FAILED);
|
|
|
|
} else {
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_UPDATE_FAILED);
|
2017-07-07 20:04:03 +02:00
|
|
|
}
|
|
|
|
|
2017-10-27 23:57:44 +02:00
|
|
|
return ret;
|
2017-07-07 20:04:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static int package_uri_write_cb(u16_t obj_inst_id,
|
|
|
|
u8_t *data, u16_t data_len,
|
|
|
|
bool last_block, size_t total_size)
|
|
|
|
{
|
2019-07-08 19:44:06 +02:00
|
|
|
LOG_DBG("PACKAGE_URI WRITE: %s", log_strdup(package_uri));
|
2017-07-05 17:07:41 +02:00
|
|
|
|
2017-07-07 20:04:03 +02:00
|
|
|
#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
|
2017-07-05 17:07:41 +02:00
|
|
|
u8_t state = lwm2m_firmware_get_update_state();
|
|
|
|
|
|
|
|
if (state == STATE_IDLE) {
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_DEFAULT);
|
|
|
|
lwm2m_firmware_start_transfer(package_uri);
|
2019-03-27 02:57:45 +01:00
|
|
|
} else if (state == STATE_DOWNLOADED && data_len == 0U) {
|
2017-07-05 17:07:41 +02:00
|
|
|
/* reset to state idle and result default */
|
|
|
|
lwm2m_firmware_set_update_result(RESULT_DEFAULT);
|
|
|
|
}
|
|
|
|
|
2017-07-07 20:04:03 +02:00
|
|
|
return 0;
|
2017-10-27 23:57:44 +02:00
|
|
|
#else
|
|
|
|
return -EINVAL;
|
2017-07-05 17:07:41 +02:00
|
|
|
#endif
|
2017-07-07 20:04:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void lwm2m_firmware_set_write_cb(lwm2m_engine_set_data_cb_t cb)
|
|
|
|
{
|
|
|
|
write_cb = cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
lwm2m_engine_set_data_cb_t lwm2m_firmware_get_write_cb(void)
|
|
|
|
{
|
|
|
|
return write_cb;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:00:00 +02:00
|
|
|
void lwm2m_firmware_set_update_cb(lwm2m_engine_user_cb_t cb)
|
2017-07-05 17:07:41 +02:00
|
|
|
{
|
|
|
|
update_cb = cb;
|
|
|
|
}
|
|
|
|
|
2018-07-12 19:00:00 +02:00
|
|
|
lwm2m_engine_user_cb_t lwm2m_firmware_get_update_cb(void)
|
2017-07-05 17:07:41 +02:00
|
|
|
{
|
|
|
|
return update_cb;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int firmware_update_cb(u16_t obj_inst_id)
|
|
|
|
{
|
2018-07-12 19:00:00 +02:00
|
|
|
lwm2m_engine_user_cb_t callback;
|
2017-07-05 17:07:41 +02:00
|
|
|
u8_t state;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
state = lwm2m_firmware_get_update_state();
|
|
|
|
if (state != STATE_DOWNLOADED) {
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_ERR("State other than downloaded: %d", state);
|
2017-10-11 20:39:37 +02:00
|
|
|
return -EPERM;
|
2017-07-05 17:07:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
lwm2m_firmware_set_update_state(STATE_UPDATING);
|
|
|
|
|
|
|
|
callback = lwm2m_firmware_get_update_cb();
|
|
|
|
if (callback) {
|
|
|
|
ret = callback(obj_inst_id);
|
|
|
|
if (ret < 0) {
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_ERR("Failed to update firmware: %d", ret);
|
2017-07-05 17:07:41 +02:00
|
|
|
lwm2m_firmware_set_update_result(
|
|
|
|
ret == -EINVAL ? RESULT_INTEGRITY_FAILED :
|
|
|
|
RESULT_UPDATE_FAILED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-07-07 20:04:03 +02:00
|
|
|
static struct lwm2m_engine_obj_inst *firmware_create(u16_t obj_inst_id)
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
/* initialize instance resource data */
|
|
|
|
INIT_OBJ_RES(res, i, FIRMWARE_PACKAGE_ID, 0, NULL, 0,
|
|
|
|
NULL, NULL, package_write_cb, NULL);
|
|
|
|
INIT_OBJ_RES(res, i, FIRMWARE_PACKAGE_URI_ID, 0,
|
|
|
|
package_uri, PACKAGE_URI_LEN,
|
|
|
|
NULL, NULL, package_uri_write_cb, NULL);
|
2017-07-05 17:07:41 +02:00
|
|
|
INIT_OBJ_RES_EXECUTE(res, i, FIRMWARE_UPDATE_ID,
|
|
|
|
firmware_update_cb);
|
2017-07-07 20:04:03 +02:00
|
|
|
INIT_OBJ_RES_DATA(res, i, FIRMWARE_STATE_ID,
|
|
|
|
&update_state, sizeof(update_state));
|
|
|
|
INIT_OBJ_RES_DATA(res, i, FIRMWARE_UPDATE_RESULT_ID,
|
|
|
|
&update_result, sizeof(update_result));
|
|
|
|
INIT_OBJ_RES_DATA(res, i, FIRMWARE_UPDATE_DELIV_METHOD_ID,
|
|
|
|
&delivery_method, sizeof(delivery_method));
|
|
|
|
|
|
|
|
inst.resources = res;
|
|
|
|
inst.resource_count = i;
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_DBG("Create LWM2M firmware instance: %d", obj_inst_id);
|
2017-07-07 20:04:03 +02:00
|
|
|
return &inst;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int lwm2m_firmware_init(struct device *dev)
|
|
|
|
{
|
|
|
|
struct lwm2m_engine_obj_inst *obj_inst = NULL;
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
/* Set default values */
|
|
|
|
package_uri[0] = '\0';
|
2017-07-05 17:07:41 +02:00
|
|
|
/* Initialize state machine */
|
|
|
|
/* TODO: should be restored from the permanent storage */
|
2017-07-07 20:04:03 +02:00
|
|
|
update_state = STATE_IDLE;
|
|
|
|
update_result = RESULT_DEFAULT;
|
|
|
|
#ifdef CONFIG_LWM2M_FIRMWARE_UPDATE_PULL_SUPPORT
|
|
|
|
delivery_method = DELIVERY_METHOD_BOTH;
|
|
|
|
#else
|
2017-10-10 00:10:40 +02:00
|
|
|
delivery_method = DELIVERY_METHOD_PUSH_ONLY;
|
2017-07-07 20:04:03 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
firmware.obj_id = LWM2M_OBJECT_FIRMWARE_ID;
|
|
|
|
firmware.fields = fields;
|
2018-06-14 21:27:11 +02:00
|
|
|
firmware.field_count = ARRAY_SIZE(fields);
|
2018-11-29 20:23:03 +01:00
|
|
|
firmware.max_instance_count = 1U;
|
2017-07-07 20:04:03 +02:00
|
|
|
firmware.create_cb = firmware_create;
|
|
|
|
lwm2m_register_obj(&firmware);
|
|
|
|
|
|
|
|
/* auto create the only instance */
|
|
|
|
ret = lwm2m_create_obj_inst(LWM2M_OBJECT_FIRMWARE_ID, 0, &obj_inst);
|
|
|
|
if (ret < 0) {
|
2018-09-19 10:22:19 +02:00
|
|
|
LOG_DBG("Create LWM2M instance 0 error: %d", ret);
|
2017-07-07 20:04:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
SYS_INIT(lwm2m_firmware_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
|