From 96f6d6525eeeccd6b475cefd553a2c2c94c365e9 Mon Sep 17 00:00:00 2001 From: Damian Krolik Date: Thu, 9 Dec 2021 13:55:01 +0100 Subject: [PATCH] [ota-requestor] Add ApplyUpdate command support to OTARequestor class 1. Make OTARequestor class able to send ApplyUpdate command. 2. Add Apply() method OTAImageProcessor so that an image can be applied on ApplyUpdateResponse. --- .../linux/LinuxOTAImageProcessor.cpp | 5 ++ .../linux/LinuxOTAImageProcessor.h | 1 + examples/ota-requestor-app/linux/main.cpp | 2 +- .../clusters/ota-requestor/OTADownloader.h | 1 + .../clusters/ota-requestor/OTARequestor.cpp | 64 ++++++++++++++++++- src/app/clusters/ota-requestor/OTARequestor.h | 30 ++++++++- src/include/platform/OTAImageProcessor.h | 5 ++ 7 files changed, 101 insertions(+), 7 deletions(-) diff --git a/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.cpp b/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.cpp index 41f717bfa3be03..0465a271409930 100644 --- a/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.cpp +++ b/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.cpp @@ -40,6 +40,11 @@ CHIP_ERROR LinuxOTAImageProcessor::Finalize() return CHIP_NO_ERROR; } +CHIP_ERROR LinuxOTAImageProcessor::Apply() +{ + return CHIP_NO_ERROR; +} + CHIP_ERROR LinuxOTAImageProcessor::Abort() { if (mParams.imageFile.empty()) diff --git a/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.h b/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.h index 3fbd28010848fb..ed66a4c091700f 100644 --- a/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.h +++ b/examples/ota-requestor-app/linux/LinuxOTAImageProcessor.h @@ -32,6 +32,7 @@ class LinuxOTAImageProcessor : public OTAImageProcessorInterface //////////// OTAImageProcessorInterface Implementation /////////////// CHIP_ERROR PrepareDownload() override; CHIP_ERROR Finalize() override; + CHIP_ERROR Apply() override; CHIP_ERROR Abort() override; CHIP_ERROR ProcessBlock(ByteSpan & block) override; diff --git a/examples/ota-requestor-app/linux/main.cpp b/examples/ota-requestor-app/linux/main.cpp index 4f38efd37420a2..f0f8fbd5ce9889 100644 --- a/examples/ota-requestor-app/linux/main.cpp +++ b/examples/ota-requestor-app/linux/main.cpp @@ -230,7 +230,7 @@ int main(int argc, char * argv[]) if (delayQueryTimeInSec > 0) { // In this mode Provider node ID and fabric idx must be supplied explicitly from program args - gRequestorCore.TestModeSetProviderParameters(providerNodeId, providerFabricIndex); + gRequestorCore.TestModeSetProviderParameters(providerNodeId, providerFabricIndex, chip::kRootEndpointId); chip::DeviceLayer::SystemLayer().StartTimer(chip::System::Clock::Milliseconds32(delayQueryTimeInSec * 1000), OnStartDelayTimerHandler, nullptr); diff --git a/src/app/clusters/ota-requestor/OTADownloader.h b/src/app/clusters/ota-requestor/OTADownloader.h index 33ec9fac87991a..b77514c35b53b2 100644 --- a/src/app/clusters/ota-requestor/OTADownloader.h +++ b/src/app/clusters/ota-requestor/OTADownloader.h @@ -68,6 +68,7 @@ class OTADownloader // A setter for the delegate class pointer void SetImageProcessorDelegate(OTAImageProcessorInterface * delegate) { mImageProcessor = delegate; } + OTAImageProcessorInterface * GetImageProcessorDelegate() const { return mImageProcessor; } State GetState() const { return mState; } diff --git a/src/app/clusters/ota-requestor/OTARequestor.cpp b/src/app/clusters/ota-requestor/OTARequestor.cpp index 61d0bbd5fed0e5..6af981bb7c7448 100644 --- a/src/app/clusters/ota-requestor/OTARequestor.cpp +++ b/src/app/clusters/ota-requestor/OTARequestor.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include "BDXDownloader.h" @@ -124,6 +125,11 @@ void OTARequestor::OnQueryImageResponse(void * context, const QueryImageResponse err.Format())); requestorCore->mProviderNodeId = nodeId; + MutableByteSpan updateToken(requestorCore->mUpdateTokenBuffer); + CopySpanToMutableSpan(response.updateToken.Value(), updateToken); + requestorCore->mUpdateVersion = response.softwareVersion.Value(); + requestorCore->mUpdateToken = updateToken; + // CSM should already be created for sending QueryImage command so use the same CSM since the // provider node ID that will supply the OTA image must be on the same fabric as the sender of the QueryImageResponse requestorCore->ConnectToProvider(kStartBDX); @@ -145,6 +151,32 @@ void OTARequestor::OnQueryImageFailure(void * context, EmberAfStatus status) ChipLogDetail(SoftwareUpdate, "QueryImage failure response %" PRIu8, status); } +void OTARequestor::OnApplyUpdateResponse(void * context, const ApplyUpdateResponse::DecodableType & response) +{ + VerifyOrReturn(context != nullptr, ChipLogError(SoftwareUpdate, "Received ApplyUpdateResponse with invalid context")); + + OTARequestor * requestorCore = static_cast(context); + + // TODO: Call OTARequestorDriver to schedule the image application. + switch (response.action) + { + case EMBER_ZCL_OTA_APPLY_UPDATE_ACTION_PROCEED: { + VerifyOrReturn(requestorCore->mBdxDownloader != nullptr, ChipLogError(SoftwareUpdate, "Downloader is not set")); + OTAImageProcessorInterface * imageProcessor = requestorCore->mBdxDownloader->GetImageProcessorDelegate(); + VerifyOrReturn(imageProcessor != nullptr, ChipLogError(SoftwareUpdate, "Image processor is not set")); + imageProcessor->Apply(); + break; + } + default: + break; + } +} + +void OTARequestor::OnApplyUpdateFailure(void * context, EmberAfStatus status) +{ + ChipLogDetail(SoftwareUpdate, "ApplyUpdate failure response %" PRIu8, status); +} + EmberAfStatus OTARequestor::HandleAnnounceOTAProvider(app::CommandHandler * commandObj, const app::ConcreteCommandPath & commandPath, const AnnounceOtaProvider::DecodableType & commandData) @@ -275,15 +307,13 @@ void OTARequestor::OnConnected(void * context, OperationalDeviceProxy * devicePr switch (requestorCore->mOnConnectedAction) { case kQueryImage: { - constexpr EndpointId kOtaProviderEndpoint = 0; - QueryImageRequest request; CHIP_ERROR err = requestorCore->BuildQueryImageRequest(request); VerifyOrReturn(err == CHIP_NO_ERROR, ChipLogError(SoftwareUpdate, "Failed to build QueryImage command: %" CHIP_ERROR_FORMAT, err.Format())); Controller::OtaSoftwareUpdateProviderCluster cluster; - cluster.Associate(deviceProxy, kOtaProviderEndpoint); + cluster.Associate(deviceProxy, requestorCore->mProviderEndpointId); err = cluster.InvokeCommand(request.args, requestorCore, OnQueryImageResponse, OnQueryImageFailure); VerifyOrReturn(err == CHIP_NO_ERROR, @@ -339,6 +369,21 @@ void OTARequestor::OnConnected(void * context, OperationalDeviceProxy * devicePr ChipLogError(SoftwareUpdate, "Cannot begin prepare download: %" CHIP_ERROR_FORMAT, err.Format())); break; } + case kApplyUpdate: { + ApplyUpdateRequest::Type args; + CHIP_ERROR err = requestorCore->BuildApplyUpdateRequest(args); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(SoftwareUpdate, "Failed to build ApplyUpdate command: %" CHIP_ERROR_FORMAT, err.Format())); + + Controller::OtaSoftwareUpdateProviderCluster cluster; + cluster.Associate(deviceProxy, requestorCore->mProviderEndpointId); + + err = cluster.InvokeCommand(args, requestorCore, OnApplyUpdateResponse, OnApplyUpdateFailure); + VerifyOrReturn(err == CHIP_NO_ERROR, + ChipLogError(SoftwareUpdate, "Failed to send ApplyUpdate command: %" CHIP_ERROR_FORMAT, err.Format())); + + break; + } default: break; } @@ -355,6 +400,11 @@ void OTARequestor::TriggerImmediateQuery() ConnectToProvider(kQueryImage); } +void OTARequestor::ApplyUpdate() +{ + ConnectToProvider(kApplyUpdate); +} + CHIP_ERROR OTARequestor::BuildQueryImageRequest(QueryImageRequest & request) { constexpr EmberAfOTADownloadProtocol kProtocolsSupported[] = { EMBER_ZCL_OTA_DOWNLOAD_PROTOCOL_BDX_SYNCHRONOUS }; @@ -389,4 +439,12 @@ CHIP_ERROR OTARequestor::BuildQueryImageRequest(QueryImageRequest & request) return CHIP_NO_ERROR; } +CHIP_ERROR OTARequestor::BuildApplyUpdateRequest(ApplyUpdateRequest::Type & args) +{ + VerifyOrReturnError(mUpdateToken.size() > 0, CHIP_ERROR_INCORRECT_STATE); + args.updateToken = mUpdateToken; + args.newVersion = mUpdateVersion; + return CHIP_NO_ERROR; +} + } // namespace chip diff --git a/src/app/clusters/ota-requestor/OTARequestor.h b/src/app/clusters/ota-requestor/OTARequestor.h index a5bfc728731eda..2d300e19d1d63b 100644 --- a/src/app/clusters/ota-requestor/OTARequestor.h +++ b/src/app/clusters/ota-requestor/OTARequestor.h @@ -41,6 +41,7 @@ class OTARequestor : public OTARequestorInterface { kQueryImage = 0, kStartBDX, + kApplyUpdate, }; OTARequestor() : mOnConnectedCallback(OnConnected, this), mOnConnectionFailureCallback(OnConnectionFailure, this) {} @@ -51,6 +52,9 @@ class OTARequestor : public OTARequestorInterface // and download the new image if available void TriggerImmediateQuery(); + // Send ApplyImage + void ApplyUpdate(); + // A setter for the delegate class pointer void SetOtaRequestorDriver(OTARequestorDriver * driver) { mOtaRequestorDriver = driver; } @@ -91,15 +95,18 @@ class OTARequestor : public OTARequestorInterface * Called to indicate test mode. This is when the Requestor is used as a test tool and the the provider parameters are supplied * explicitly. */ - void TestModeSetProviderParameters(NodeId nodeId, FabricIndex fabIndex) + void TestModeSetProviderParameters(NodeId nodeId, FabricIndex fabIndex, EndpointId endpointId) { mProviderNodeId = nodeId; mProviderFabricIndex = fabIndex; + mProviderEndpointId = endpointId; } private: struct QueryImageRequest; + static constexpr size_t kMaxUpdateTokenLen = 32; + // TODO: the application should define this, along with initializing the BDXDownloader // This class is purely for delivering messages and sending outgoing messages to/from the BDXDownloader. @@ -175,7 +182,12 @@ class OTARequestor : public OTARequestorInterface /** * Create a QueryImage request using values from the Basic cluster attributes */ - CHIP_ERROR BuildQueryImageRequest(QueryImageRequest & req); + CHIP_ERROR BuildQueryImageRequest(QueryImageRequest & request); + + /** + * Create a ApplyUpdate request using values obtained from QueryImageResponse + */ + CHIP_ERROR BuildApplyUpdateRequest(app::Clusters::OtaSoftwareUpdateProvider::Commands::ApplyUpdateRequest::Type & args); /** * Session connection callbacks @@ -193,16 +205,28 @@ class OTARequestor : public OTARequestorInterface const app::Clusters::OtaSoftwareUpdateProvider::Commands::QueryImageResponse::DecodableType & response); static void OnQueryImageFailure(void * context, EmberAfStatus status); + /** + * ApplyUpdate callbacks + */ + static void + OnApplyUpdateResponse(void * context, + const app::Clusters::OtaSoftwareUpdateProvider::Commands::ApplyUpdateResponse::DecodableType & response); + static void OnApplyUpdateFailure(void * context, EmberAfStatus); + OTARequestorDriver * mOtaRequestorDriver = nullptr; NodeId mProviderNodeId = kUndefinedNodeId; FabricIndex mProviderFabricIndex = kUndefinedFabricIndex; + EndpointId mProviderEndpointId = kRootEndpointId; uint32_t mOtaStartDelayMs = 0; CASESessionManager * mCASESessionManager = nullptr; OnConnectedAction mOnConnectedAction = kQueryImage; Messaging::ExchangeContext * mExchangeCtx = nullptr; BDXDownloader * mBdxDownloader = nullptr; // TODO: this should be OTADownloader BDXMessenger mBdxMessenger; // TODO: ideally this is held by the application - Server * mServer = nullptr; + uint8_t mUpdateTokenBuffer[kMaxUpdateTokenLen]; + ByteSpan mUpdateToken; + uint32_t mUpdateVersion = 0; + Server * mServer = nullptr; }; } // namespace chip diff --git a/src/include/platform/OTAImageProcessor.h b/src/include/platform/OTAImageProcessor.h index 7afc845d56efdd..7aaa346a8a5196 100644 --- a/src/include/platform/OTAImageProcessor.h +++ b/src/include/platform/OTAImageProcessor.h @@ -58,6 +58,11 @@ class DLL_EXPORT OTAImageProcessorInterface */ virtual CHIP_ERROR Finalize() = 0; + /** + * Called when the OTA image should be applied. + */ + virtual CHIP_ERROR Apply() = 0; + /** * Called when the OTA image download process is incomplete or cannot continue. This may include but not limited to erasing * everything that has been written and releasing buffers. This must not be a blocking call.