Example
Dependencies: FXAS21002 FXOS8700Q
Diff: simple-mbed-cloud-client/mbed-cloud-client/update-client-hub/modules/lwm2m-mbed/source/FirmwareUpdateResource.cpp
- Revision:
- 0:11cc2b7889af
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/simple-mbed-cloud-client/mbed-cloud-client/update-client-hub/modules/lwm2m-mbed/source/FirmwareUpdateResource.cpp Tue Nov 19 09:49:38 2019 +0000 @@ -0,0 +1,578 @@ +// ---------------------------------------------------------------------------- +// Copyright 2016-2017 ARM Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------- + +// Note: this macro is needed on armcc to get the the PRI*32 macros +// from inttypes.h in a C++ code. +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif + +#include "update-client-lwm2m/FirmwareUpdateResource.h" +#include "update-client-common/arm_uc_config.h" + +#include "update-client-common/arm_uc_common.h" +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +#include "mbed-client/source/include/m2mcallbackstorage.h" +#include "mbed-client/m2mexecuteparams.h" +#include "mbed-client/m2mbase.h" +#endif +#include "source/update_client_hub_state_machine.h" +#include "source/update_client_hub_error_handler.h" + +#include <stdio.h> +#include <inttypes.h> + +#define ARM_UCS_LWM2M_INTERNAL_ERROR (-1) +#define ARM_UCS_LWM2M_INTERNAL_SUCCESS (0) + +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +#define RESOURCE_VALUE(arg) arg +#else +#define RESOURCE_VALUE(arg) #arg +#endif + +namespace FirmwareUpdateResource { + +/* send delayed response */ +enum { + ResourcePackage, + ResourcePackageURI, + ResourceUpdate +}; + +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +static void packageCallback(void *_parameters, const M2MExecuteParameter ¶ms); +static void packageURICallback(void *_parameters, const M2MExecuteParameter ¶ms); +static void updateCallback(void *, const M2MExecuteParameter ¶ms); +static void notificationCallback(void *client_args, const M2MBase &object, NotificationDeliveryStatus delivery_status); +/* Default values are non-standard, but the standard has no + values for indicating that the device is initializing. + To address this, Service ignores -1 and/or 255 values coming through, + so for our purposes this is the correct form of initialization. +*/ +const uint8_t defaultValue = -1; +#else +static void packageCallback(void *_parameters); +static void updateCallback(void *); +static void notificationCallback(const M2MBase &object, NotificationDeliveryStatus delivery_status, void *client_args); +uint8_t defaultValue[] = {"-1"}; +#endif +static void sendDelayedResponseTask(uintptr_t parameter); + +/* LWM2M Firmware Update Object */ +static M2MObject *updateObject; + +/* LWM2M Firmware Update Object resources */ +static M2MResource *resourcePackage = NULL; +static M2MResource *resourcePackageURI = NULL; +static M2MResource *resourceUpdate = NULL; +static M2MResource *resourceState = NULL; +static M2MResource *resourceResult = NULL; +static M2MResource *resourceName = NULL; +static M2MResource *resourceVersion = NULL; + +/* function pointers to callback functions */ +static void (*externalPackageCallback)(const uint8_t *buffer, uint16_t length) = NULL; +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +static void (*externalPackageURICallback)(const uint8_t *buffer, uint16_t length) = NULL; +#endif +static void (*externalUpdateCallback)(void) = NULL; +static void (*externalNotificationCallback)(void) = NULL; + +/* Callback structs for delayed response. + * + * There needs to be one per callback type to avoid collisions between different operations. + */ +static arm_uc_callback_t callbackNodePackage = { NULL, 0, NULL, 0 }; +static arm_uc_callback_t callbackNodePackageURI = { NULL, 0, NULL, 0 }; +static arm_uc_callback_t callbackNodeResourceUpdate = { NULL, 0, NULL, 0 }; + +#if defined(ARM_UC_FEATURE_FW_SOURCE_COAP) && (ARM_UC_FEATURE_FW_SOURCE_COAP == 1) +/* M2MInterface */ +static M2MInterface *_m2m_interface; +#endif +} + +/** + * @brief Initialize LWM2M Firmware Update Object + * @details Sets up LWM2M object with accompanying resources. + */ +void FirmwareUpdateResource::Initialize(void) +{ + static bool initialized = false; + + if (!initialized) { + /* The LWM2M Firmware Update Object for LITE client is at /10252 (Manifest) */ +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) + updateObject = M2MInterfaceFactory::create_object(10252, _m2m_interface); +#else + updateObject = M2MInterfaceFactory::create_object("10252"); +#endif + + if (updateObject) { + initialized = true; + +#if defined(ARM_UC_PROFILE_MBED_CLOUD_CLIENT) && (ARM_UC_PROFILE_MBED_CLOUD_CLIENT == 1) + updateObject->set_register_uri(false); + +#endif + + /* Create first (and only) instance /10252/0 */ + M2MObjectInstance *updateInstance = updateObject->create_object_instance(); + + if (updateInstance) { + +#if defined(ARM_UC_PROFILE_MBED_CLOUD_CLIENT) && (ARM_UC_PROFILE_MBED_CLOUD_CLIENT == 1) + /* Set observable so the Portal can read it */ + updateInstance->set_register_uri(false); +#endif + uint8_t defaultVersion[] = {"-1"}; + + /* Create Package resource /10252/0/1 */ + resourcePackage = updateInstance->create_dynamic_resource( + RESOURCE_VALUE(1), "Package", M2MResourceInstance::OPAQUE, false); + if (resourcePackage) { + /* This should be PUT according to the standard but + Connector client doesn't support callbacks for PUT. + */ + resourcePackage->set_operation(M2MBase::POST_ALLOWED); +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) + resourcePackage->set_execute_function((M2MResourceBase::execute_callback)packageCallback, NULL); +#else + resourcePackage->set_execute_function(packageCallback); + + /* The delayed response if for processing heavier loads */ + resourcePackage->set_delayed_response(true); +#endif + + } + + /* Create State resource /10252/0/2 */ + resourceState = updateInstance->create_dynamic_resource( + RESOURCE_VALUE(2), "State", M2MResourceInstance::INTEGER, true); + if (resourceState) { + resourceState->set_operation(M2MBase::GET_ALLOWED); + resourceState->set_notification_delivery_status_cb(notificationCallback, NULL); +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) + resourceState->set_value(defaultValue); +#else + resourceState->set_value(defaultValue, sizeof(defaultValue) - 1); +#endif + resourceState->publish_value_in_registration_msg(true); + resourceState->set_auto_observable(true); + } + + /* Create Update Result resource /10252/0/3 */ + resourceResult = updateInstance->create_dynamic_resource( + RESOURCE_VALUE(3), "UpdateResult", M2MResourceInstance::INTEGER, true); + if (resourceResult) { + resourceResult->set_operation(M2MBase::GET_ALLOWED); + resourceResult->set_notification_delivery_status_cb(notificationCallback, NULL); +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) + resourceResult->set_value(defaultValue); +#else + resourceResult->set_value(defaultValue, sizeof(defaultValue) - 1); +#endif + resourceResult->publish_value_in_registration_msg(true); + resourceResult->set_auto_observable(true); + } + + /* Create PkgName resource /10252/0/5 */ + resourceName = updateInstance->create_dynamic_resource( + RESOURCE_VALUE(5), "PkgName", M2MResourceInstance::STRING, true); + if (resourceName) { + resourceName->set_operation(M2MBase::GET_ALLOWED); + resourceName->set_value(defaultVersion, sizeof(defaultVersion) - 1); + resourceName->publish_value_in_registration_msg(true); + resourceName->set_auto_observable(true); + } + + /* Create PkgVersion resource /10252/0/6 */ + resourceVersion = updateInstance->create_dynamic_resource( + RESOURCE_VALUE(6), "PkgVersion", M2MResourceInstance::STRING, true); + if (resourceVersion) { + resourceVersion->set_operation(M2MBase::GET_ALLOWED); + resourceVersion->set_value(defaultVersion, sizeof(defaultVersion) - 1); + resourceVersion->publish_value_in_registration_msg(true); + resourceVersion->set_auto_observable(true); + } + +#if !defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) || (ARM_UC_PROFILE_MBED_CLIENT_LITE == 0) + /* Create Update resource /10252/0/9 */ + resourceUpdate = updateInstance->create_dynamic_resource( + "9", "Update", M2MResourceInstance::STRING, false); + if (resourceUpdate) { + resourceUpdate->set_operation(M2MBase::POST_ALLOWED); + resourceUpdate->set_execute_function(updateCallback); + resourceUpdate->set_delayed_response(true); + } +#endif + } + } + } +} + +M2MObject *FirmwareUpdateResource::getObject() +{ + Initialize(); + + return updateObject; +} + +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +void FirmwareUpdateResource::packageCallback(void *_parameters, const M2MExecuteParameter ¶ms) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::packageCallback"); + + // Reset the resource values for every new Campaign + // to make sure values of new Campaign get sent to service + resourceState->set_value(defaultValue); + resourceResult->set_value(defaultValue); + + if (externalPackageCallback) { + /* read payload */ + const uint8_t *buffer = params.get_argument_value(); + uint16_t length = params.get_argument_value_length(); + + /* invoke external callback function */ + externalPackageCallback(buffer, length); + + params.get_resource().send_post_response(params.get_token(), params.get_token_length()); + + // TODO: Do we need to pass the token param to delayed callback + // Or is above send_post_response enough? + // Below callback is needed because otherwise UC Hub is not notified +#else +void FirmwareUpdateResource::packageCallback(void *_parameters) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::packageCallback"); + + // Reset the resource values for every new Campaign + // to make sure values of new Campaign get sent to service + resourceState->set_value(defaultValue, sizeof(defaultValue) - 1); + resourceResult->set_value(defaultValue, sizeof(defaultValue) - 1); + + if (_parameters && externalPackageCallback) { + /* recast parameter */ + M2MResource::M2MExecuteParameter *parameters = + static_cast<M2MResource::M2MExecuteParameter *>(_parameters); + + /* read payload */ + const uint8_t *buffer = parameters->get_argument_value(); + uint16_t length = parameters->get_argument_value_length(); + + /* invoke external callback function */ + externalPackageCallback(buffer, length); +#endif + /* schedule delayed response */ + ARM_UC_PostCallback(&callbackNodePackage, + FirmwareUpdateResource::sendDelayedResponseTask, + FirmwareUpdateResource::ResourcePackage); + } +} + +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +void FirmwareUpdateResource::packageURICallback(void *_parameters, const M2MExecuteParameter ¶ms) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::packageURICallback"); + + if (_parameters && externalPackageURICallback) { + /* read payload */ + const uint8_t *buffer = params.get_argument_value(); + uint16_t length = params.get_argument_value_length(); + + /* invoke external callback function */ + externalPackageURICallback(buffer, length); + + params.get_resource().send_post_response(params.get_token(), params.get_token_length()); + + // TODO: Do we need to pass the token param to delayed callback + // Or is above send_post_response enough? + // Below callback is needed because otherwise UC Hub is not notified + /* schedule delayed response */ + ARM_UC_PostCallback(&callbackNodePackageURI, + FirmwareUpdateResource::sendDelayedResponseTask, + FirmwareUpdateResource::ResourcePackageURI); + } +} +#endif + +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +void FirmwareUpdateResource::updateCallback(void *_parameters, const M2MExecuteParameter ¶ms) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::updateCallback"); + + (void) _parameters; + + if (externalUpdateCallback) { + /* invoke external callback function */ + externalUpdateCallback(); + + params.get_resource().send_post_response(params.get_token(), params.get_token_length()); + + // TODO: Do we need to pass the token param to delayed callback + // Or is above send_post_response enough? +#else +void FirmwareUpdateResource::updateCallback(void *_parameters) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::updateCallback"); + + (void) _parameters; + + if (externalUpdateCallback) { + /* invoke external callback function */ + externalUpdateCallback(); + +#endif + // Below callback is needed because otherwise UC Hub is not notified + /* schedule delayed response */ + ARM_UC_PostCallback(&callbackNodeResourceUpdate, + FirmwareUpdateResource::sendDelayedResponseTask, + FirmwareUpdateResource::ResourceUpdate); + } +} + +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) +void FirmwareUpdateResource::notificationCallback(void *client_args, + const M2MBase &object, + const NotificationDeliveryStatus delivery_status) + +#else +void FirmwareUpdateResource::notificationCallback(const M2MBase &base, + const NotificationDeliveryStatus delivery_status, + void *client_args) +#endif +{ + UC_SRCE_TRACE("FirmwareUpdateResource::notificationCallback status: %d", delivery_status); +#if defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) && (ARM_UC_PROFILE_MBED_CLIENT_LITE == 1) + path_buffer buffer; + object.uri_path(buffer); + UC_SRCE_TRACE("Callback for resource: %s", buffer.c_str()); +#endif + + if (delivery_status == NOTIFICATION_STATUS_DELIVERED) { + // Notification has been ACKed by server, complete to callback + UC_SRCE_TRACE("FirmwareUpdateResource::notificationCallback DELIVERED"); + + if (externalNotificationCallback) { + externalNotificationCallback(); + } + } else if (delivery_status == NOTIFICATION_STATUS_BUILD_ERROR || + delivery_status == NOTIFICATION_STATUS_RESEND_QUEUE_FULL || + delivery_status == NOTIFICATION_STATUS_SEND_FAILED || + delivery_status == NOTIFICATION_STATUS_UNSUBSCRIBED) { + // Error case, notification not reaching service + // We are sending out error because we cannot rely connection is + // anymore up and the service and client are not in sync anymore. + // Also sending new notifications after this might lock event + // machine because comms cannot service us anymore. + UC_SRCE_ERR_MSG("Received Notification delivery status: %d - ERROR!", delivery_status); + ARM_UC_HUB_ErrorHandler(HUB_ERR_CONNECTION, ARM_UC_HUB_getState()); + } else { + // NOTIFICATION_STATUS_INIT + // NOTIFICATION_STATUS_SENT + // NOTIFICATION_STATUS_SUBSCRIBED + UC_SRCE_TRACE("FirmwareUpdateResource::notificationCallback Status ignored, waiting delivery..."); + } +} + +void FirmwareUpdateResource::sendDelayedResponseTask(uintptr_t parameter) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::sendDelayedResponseTask"); + + switch (parameter) { + case FirmwareUpdateResource::ResourcePackage: + UC_SRCE_TRACE("resourcePackage->send_delayed_post_response"); +#if !defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) || (ARM_UC_PROFILE_MBED_CLIENT_LITE == 0) + resourcePackage->send_delayed_post_response(); +#else + //called already in callback: resourcePackage->send_delayed_post_response(); +#endif + break; + case FirmwareUpdateResource::ResourcePackageURI: + UC_SRCE_TRACE("resourcePackageURI->send_delayed_post_response"); +#if !defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) || (ARM_UC_PROFILE_MBED_CLIENT_LITE == 0) + resourcePackageURI->send_delayed_post_response(); +#else + //called already in callback: resourcePackageURI->send_delayed_post_response(); +#endif + break; + case FirmwareUpdateResource::ResourceUpdate: + UC_SRCE_TRACE("resourceUpdate->send_delayed_post_response"); +#if !defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) || (ARM_UC_PROFILE_MBED_CLIENT_LITE == 0) + resourceUpdate->send_delayed_post_response(); +#else + //called already in callback: resourceUpdate->send_delayed_post_response(); +#endif + break; + default: + UC_SRCE_ERR_MSG("unsupported resource"); + break; + } +} + +/*****************************************************************************/ +/* Update Client Source */ +/*****************************************************************************/ + +/* Add callback for resource /10252/0/1, Package */ +int32_t FirmwareUpdateResource::addPackageCallback(void (*cb)(const uint8_t *buffer, uint16_t length)) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::addPackageCallback: %p", cb); + + externalPackageCallback = cb; + + return ARM_UCS_LWM2M_INTERNAL_SUCCESS; +} + +#if !defined(ARM_UC_PROFILE_MBED_CLIENT_LITE) || (ARM_UC_PROFILE_MBED_CLIENT_LITE == 0) +/* Add callback for resource /10252/0/9, Update */ +int32_t FirmwareUpdateResource::addUpdateCallback(void (*cb)(void)) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::addUpdateCallback: %p", cb); + + externalUpdateCallback = cb; + + return ARM_UCS_LWM2M_INTERNAL_SUCCESS; +} +#endif + +/* Add callback for when send{State, UpdateResult} is done */ +int32_t FirmwareUpdateResource::addNotificationCallback(void (*cb)(void)) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::addNotificationCallback: %p", cb); + + externalNotificationCallback = cb; + + return ARM_UCS_LWM2M_INTERNAL_SUCCESS; +} + +/*****************************************************************************/ +/* Update Client Status */ +/*****************************************************************************/ + +/* Send state for resource /10252/0/2, State */ +int32_t FirmwareUpdateResource::sendState(arm_ucs_lwm2m_state_t state) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::sendState"); + + int32_t result = ARM_UCS_LWM2M_INTERNAL_ERROR; + + if ((resourceState != NULL) + && ARM_UC_IsValidState(state) + && resourceState->set_value((int64_t)state)) { + result = ARM_UCS_LWM2M_INTERNAL_SUCCESS; + } + return result; +} + +/* Send result for resource /10252/0/3, Update Result */ +int32_t FirmwareUpdateResource::sendUpdateResult(arm_ucs_lwm2m_result_t updateResult) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::sendUpdateResult"); + + int32_t result = ARM_UCS_LWM2M_INTERNAL_ERROR; + + if ((resourceResult != NULL) + && ARM_UC_IsValidResult(updateResult) + && resourceResult->set_value((int64_t)updateResult)) { + result = ARM_UCS_LWM2M_INTERNAL_SUCCESS; + } + return result; +} + +/* Send name for resource /10252/0/5 PkgName */ +#define MAX_PACKAGE_NAME_CHARS 32 +int32_t FirmwareUpdateResource::sendPkgName(const uint8_t *name, uint16_t length) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::sendPkgName"); + + int32_t result = ARM_UCS_LWM2M_INTERNAL_ERROR; + + if ((resourceName == NULL) + || (name == NULL) + || (length > MAX_PACKAGE_NAME_CHARS)) { + UC_SRCE_ERR_MSG("bad arguments - resourceName, package name or length."); + } else { + /* the maximum length is defined in the OMA LWM2M standard. */ + uint8_t value[MAX_PACKAGE_NAME_CHARS * 2] = { 0 }; + uint8_t index = 0; + + /* convert to printable characters using lookup table */ + for (; (index < 32) && (index < length); index++) { + value[2 * index ] = arm_uc_hex_table[name[index] >> 4]; + value[2 * index + 1] = arm_uc_hex_table[name[index] & 0x0F]; + } + if (resourceName->set_value(value, 2 * index)) { + result = ARM_UCS_LWM2M_INTERNAL_SUCCESS; + } + } + return result; +} + +/* Send version for resource /10252/0/6, PkgVersion */ +#define MAX_PACKAGE_VERSION_CHARS 21 +int32_t FirmwareUpdateResource::sendPkgVersion(uint64_t version) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::sendPkgVersion"); + + int32_t result = ARM_UCS_LWM2M_INTERNAL_ERROR; + if (resourceVersion != NULL) { + uint8_t value[MAX_PACKAGE_VERSION_CHARS + 1] = { 0 }; + uint8_t length = snprintf((char *)value, MAX_PACKAGE_VERSION_CHARS, "%" PRIu64, version); + if (resourceVersion->set_value(value, length)) { + result = ARM_UCS_LWM2M_INTERNAL_SUCCESS; + } + } + return result; +} + +#if defined(ARM_UC_FEATURE_FW_SOURCE_COAP) && (ARM_UC_FEATURE_FW_SOURCE_COAP == 1) +int32_t FirmwareUpdateResource::setM2MInterface(M2MInterface *interface) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::setM2MInterface"); + + int32_t result = ARM_UCS_LWM2M_INTERNAL_ERROR; + + if (interface != NULL) { + _m2m_interface = interface; + result = ARM_UCS_LWM2M_INTERNAL_SUCCESS; + } + return result; +} + +M2MInterface *FirmwareUpdateResource::getM2MInterface(void) +{ + return _m2m_interface; +} +#endif //ARM_UC_FEATURE_FW_SOURCE_COAP + +void FirmwareUpdateResource::Uninitialize(void) +{ + UC_SRCE_TRACE("FirmwareUpdateResource::Uninitialize"); + delete updateObject; + updateObject = NULL; + resourcePackage = NULL; + resourcePackageURI = NULL; + resourceUpdate = NULL; + resourceState = NULL; + resourceResult = NULL; + resourceName = NULL; + resourceVersion = NULL; + +}