Skip to content

Commit

Permalink
[ota-requestor] Add ApplyUpdate command support to OTARequestor class
Browse files Browse the repository at this point in the history
1. Make OTARequestor class able to send ApplyUpdate command.
2. Add Apply() method OTAImageProcessor so that an image can
   be applied on ApplyUpdateResponse.
  • Loading branch information
Damian-Nordic committed Dec 9, 2021
1 parent a726ce4 commit 96f6d65
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 7 deletions.
5 changes: 5 additions & 0 deletions examples/ota-requestor-app/linux/LinuxOTAImageProcessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
1 change: 1 addition & 0 deletions examples/ota-requestor-app/linux/LinuxOTAImageProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion examples/ota-requestor-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/app/clusters/ota-requestor/OTADownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }

Expand Down
64 changes: 61 additions & 3 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include <app-common/zap-generated/attributes/Accessors.h>
#include <lib/core/CHIPEncoding.h>
#include <platform/CHIPDeviceLayer.h>
#include <platform/OTAImageProcessor.h>
#include <zap-generated/CHIPClusters.h>

#include "BDXDownloader.h"
Expand Down Expand Up @@ -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);
Expand All @@ -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<OTARequestor *>(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)
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}
Expand All @@ -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 };
Expand Down Expand Up @@ -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
30 changes: 27 additions & 3 deletions src/app/clusters/ota-requestor/OTARequestor.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class OTARequestor : public OTARequestorInterface
{
kQueryImage = 0,
kStartBDX,
kApplyUpdate,
};

OTARequestor() : mOnConnectedCallback(OnConnected, this), mOnConnectionFailureCallback(OnConnectionFailure, this) {}
Expand All @@ -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; }

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
5 changes: 5 additions & 0 deletions src/include/platform/OTAImageProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit 96f6d65

Please sign in to comment.