Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[OTA] Add NotifyUpdateApplied API to OTA Requestor #13555

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/clusters/ota-provider/ota-provider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ bool emberAfOtaSoftwareUpdateProviderClusterNotifyUpdateAppliedCallback(
EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS;
OTAProviderDelegate * delegate = GetDelegate(endpoint);

ChipLogDetail(Zcl, "OTA Provider received NotifyUpdateUpplied");
ChipLogDetail(Zcl, "OTA Provider received NotifyUpdateApplied");

if (SendStatusIfDelegateNull(endpoint))
{
Expand Down
98 changes: 74 additions & 24 deletions src/app/clusters/ota-requestor/OTARequestor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ void OTARequestor::OnQueryImageResponse(void * context, const QueryImageResponse

if (err != CHIP_NO_ERROR)
{
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kQuerying, err);
Damian-Nordic marked this conversation as resolved.
Show resolved Hide resolved
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kQuerying, err);
return;
}

Expand All @@ -153,7 +153,7 @@ void OTARequestor::OnQueryImageResponse(void * context, const QueryImageResponse
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
break;
default:
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kQuerying, CHIP_ERROR_BAD_REQUEST);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kQuerying, CHIP_ERROR_BAD_REQUEST);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
break;
}
Expand All @@ -165,7 +165,7 @@ void OTARequestor::OnQueryImageFailure(void * context, EmberAfStatus status)
VerifyOrDie(requestorCore != nullptr);

ChipLogDetail(SoftwareUpdate, "QueryImage failure response %" PRIu8, status);
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kQuerying, CHIP_ERROR_BAD_REQUEST);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kQuerying, CHIP_ERROR_BAD_REQUEST);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
}

Expand Down Expand Up @@ -198,7 +198,19 @@ void OTARequestor::OnApplyUpdateFailure(void * context, EmberAfStatus status)
VerifyOrDie(requestorCore != nullptr);

ChipLogDetail(SoftwareUpdate, "ApplyUpdate failure response %" PRIu8, status);
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kApplying, CHIP_ERROR_BAD_REQUEST);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kApplying, CHIP_ERROR_BAD_REQUEST);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
}

void OTARequestor::OnNotifyUpdateAppliedResponse(void * context, const app::DataModel::NullObjectType & response) {}

void OTARequestor::OnNotifyUpdateAppliedFailure(void * context, EmberAfStatus status)
{
OTARequestor * requestorCore = static_cast<OTARequestor *>(context);
VerifyOrDie(requestorCore != nullptr);

ChipLogDetail(SoftwareUpdate, "NotifyUpdateApplied failure response %" PRIu8, status);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kNotifying, CHIP_ERROR_BAD_REQUEST);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
}

Expand Down Expand Up @@ -287,7 +299,7 @@ void OTARequestor::OnConnected(void * context, OperationalDeviceProxy * devicePr
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Failed to send QueryImage command: %" CHIP_ERROR_FORMAT, err.Format());
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kQuerying, err);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kQuerying, err);
return;
}

Expand All @@ -300,7 +312,7 @@ void OTARequestor::OnConnected(void * context, OperationalDeviceProxy * devicePr
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Failed to start download: %" CHIP_ERROR_FORMAT, err.Format());
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kDownloading, err);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kDownloading, err);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
return;
}
Expand All @@ -314,14 +326,28 @@ void OTARequestor::OnConnected(void * context, OperationalDeviceProxy * devicePr
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Failed to send ApplyUpdate command: %" CHIP_ERROR_FORMAT, err.Format());
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kApplying, err);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kApplying, err);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
return;
}

SetUpdateStateAttribute(OTAUpdateStateEnum::kApplying);
break;
}
case kNotifyUpdateApplied: {
CHIP_ERROR err = requestorCore->SendNotifyUpdateAppliedRequest(*deviceProxy);

if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Failed to send NotifyUpdateApplied command: %" CHIP_ERROR_FORMAT, err.Format());
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kNotifying, err);
SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
return;
}

SetUpdateStateAttribute(OTAUpdateStateEnum::kIdle);
break;
}
default:
break;
}
Expand All @@ -339,13 +365,13 @@ void OTARequestor::OnConnectionFailure(void * context, PeerId peerId, CHIP_ERROR
switch (requestorCore->mOnConnectedAction)
{
case kQueryImage:
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kQuerying, error);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kQuerying, error);
break;
case kStartBDX:
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kDownloading, error);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kDownloading, error);
break;
case kApplyUpdate:
requestorCore->mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kApplying, error);
requestorCore->mOtaRequestorDriver->HandleError(UpdateFailureState::kApplying, error);
break;
default:
break;
Expand Down Expand Up @@ -376,6 +402,11 @@ void OTARequestor::ApplyUpdate()
ConnectToProvider(kApplyUpdate);
}

void OTARequestor::NotifyUpdateApplied()
{
ConnectToProvider(kNotifyUpdateApplied);
}

void OTARequestor::OnDownloadStateChanged(OTADownloader::State state)
{
VerifyOrReturn(mOtaRequestorDriver != nullptr);
Expand All @@ -386,7 +417,7 @@ void OTARequestor::OnDownloadStateChanged(OTADownloader::State state)
mOtaRequestorDriver->UpdateDownloaded();
break;
case OTADownloader::State::kIdle:
mOtaRequestorDriver->HandleError(OTAUpdateStateEnum::kDownloading, CHIP_ERROR_CONNECTION_ABORTED);
mOtaRequestorDriver->HandleError(UpdateFailureState::kDownloading, CHIP_ERROR_CONNECTION_ABORTED);
break;
default:
break;
Expand All @@ -398,6 +429,23 @@ void OTARequestor::OnUpdateProgressChanged(uint8_t percent)
OtaRequestorServerSetUpdateStateProgress(percent);
}

CHIP_ERROR OTARequestor::GenerateUpdateToken()
{
if (mUpdateToken.empty())
{
VerifyOrReturnError(mServer != nullptr, CHIP_ERROR_INCORRECT_STATE);

FabricInfo * fabricInfo = mServer->GetFabricTable().FindFabricWithIndex(mProviderFabricIndex);
VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE);

static_assert(sizeof(NodeId) == sizeof(uint64_t), "Unexpected NodeId size");
Encoding::BigEndian::Put64(mUpdateTokenBuffer, fabricInfo->GetPeerId().GetNodeId());
mUpdateToken = ByteSpan(mUpdateTokenBuffer, sizeof(NodeId));
}

return CHIP_NO_ERROR;
}

CHIP_ERROR OTARequestor::SendQueryImageRequest(OperationalDeviceProxy & deviceProxy)
{
constexpr OTADownloadProtocol kProtocolsSupported[] = { OTADownloadProtocol::kBDXSynchronous };
Expand Down Expand Up @@ -488,19 +536,7 @@ CHIP_ERROR OTARequestor::StartDownload(OperationalDeviceProxy & deviceProxy)

CHIP_ERROR OTARequestor::SendApplyUpdateRequest(OperationalDeviceProxy & deviceProxy)
{
if (mUpdateToken.empty())
{
// OTA Requestor shall use its node ID as the update token in case the original update
// token, received in QueryImageResponse, got lost.
VerifyOrReturnError(mServer != nullptr, CHIP_ERROR_INCORRECT_STATE);

FabricInfo * fabricInfo = mServer->GetFabricTable().FindFabricWithIndex(mProviderFabricIndex);
VerifyOrReturnError(fabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE);

static_assert(sizeof(NodeId) == sizeof(uint64_t), "Unexpected NodeId size");
Encoding::BigEndian::Put64(mUpdateTokenBuffer, fabricInfo->GetPeerId().GetNodeId());
mUpdateToken = ByteSpan(mUpdateTokenBuffer, sizeof(NodeId));
}
ReturnErrorOnFailure(GenerateUpdateToken());

ApplyUpdateRequest::Type args;
args.updateToken = mUpdateToken;
Expand All @@ -512,4 +548,18 @@ CHIP_ERROR OTARequestor::SendApplyUpdateRequest(OperationalDeviceProxy & deviceP
return cluster.InvokeCommand(args, this, OnApplyUpdateResponse, OnApplyUpdateFailure);
}

CHIP_ERROR OTARequestor::SendNotifyUpdateAppliedRequest(OperationalDeviceProxy & deviceProxy)
{
ReturnErrorOnFailure(GenerateUpdateToken());

NotifyUpdateApplied::Type args;
args.updateToken = mUpdateToken;
args.softwareVersion = mUpdateVersion;

Controller::OtaSoftwareUpdateProviderCluster cluster;
cluster.Associate(&deviceProxy, mProviderEndpointId);

return cluster.InvokeCommand(args, this, OnNotifyUpdateAppliedResponse, OnNotifyUpdateAppliedFailure);
}

} // namespace chip
20 changes: 20 additions & 0 deletions src/app/clusters/ota-requestor/OTARequestor.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
kQueryImage = 0,
kStartBDX,
kApplyUpdate,
kNotifyUpdateApplied,
};

OTARequestor() : mOnConnectedCallback(OnConnected, this), mOnConnectionFailureCallback(OnConnectionFailure, this) {}
Expand All @@ -62,6 +63,9 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
// Send ApplyImage
void ApplyUpdate() override;

// Send NotifyUpdateApplied
void NotifyUpdateApplied() override;

//////////// BDXDownloader::StateDelegate Implementation ///////////////
void OnDownloadStateChanged(OTADownloader::State state) override;
void OnUpdateProgressChanged(uint8_t percent) override;
Expand Down Expand Up @@ -188,6 +192,11 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
chip::BDXDownloader * mDownloader;
};

/**
* Generate an update token using the operational node ID in case of token lost, received in QueryImageResponse
*/
CHIP_ERROR GenerateUpdateToken();

/**
* Send QueryImage request using values matching Basic cluster
*/
Expand All @@ -208,6 +217,11 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
*/
CHIP_ERROR SendApplyUpdateRequest(OperationalDeviceProxy & deviceProxy);

/**
* Send NotifyUpdateApplied request
*/
CHIP_ERROR SendNotifyUpdateAppliedRequest(OperationalDeviceProxy & deviceProxy);

/**
* Session connection callbacks
*/
Expand All @@ -228,6 +242,12 @@ class OTARequestor : public OTARequestorInterface, public BDXDownloader::StateDe
static void OnApplyUpdateResponse(void * context, const ApplyUpdateResponseDecodableType & response);
static void OnApplyUpdateFailure(void * context, EmberAfStatus);

/**
* NotifyUpdateApplied callbacks
*/
static void OnNotifyUpdateAppliedResponse(void * context, const app::DataModel::NullObjectType & response);
static void OnNotifyUpdateAppliedFailure(void * context, EmberAfStatus);

OTARequestorDriver * mOtaRequestorDriver = nullptr;
NodeId mProviderNodeId = kUndefinedNodeId;
FabricIndex mProviderFabricIndex = kUndefinedFabricIndex;
Expand Down
11 changes: 10 additions & 1 deletion src/include/platform/OTARequestorDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ struct UpdateDescription
ByteSpan metadataForRequestor;
};

enum class UpdateFailureState
{
kQuerying,
kDownloading,
kApplying,
kNotifying,
kAwaitingNextAction,
};

enum class UpdateNotFoundReason
{
Busy,
Expand All @@ -61,7 +70,7 @@ class OTARequestorDriver
virtual uint16_t GetMaxDownloadBlockSize() { return 1024; }

/// Called when an error occurs at any OTA requestor operation
virtual void HandleError(app::Clusters::OtaSoftwareUpdateRequestor::OTAUpdateStateEnum state, CHIP_ERROR error) = 0;
virtual void HandleError(UpdateFailureState state, CHIP_ERROR error) = 0;

/// Called when the latest query found a software update
virtual void UpdateAvailable(const UpdateDescription & update, System::Clock::Seconds32 delay) = 0;
Expand Down
3 changes: 3 additions & 0 deletions src/include/platform/OTARequestorInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class OTARequestorInterface
// Send ApplyImage command
virtual void ApplyUpdate() = 0;

// Send NotifyUpdateApplied command
virtual void NotifyUpdateApplied() = 0;

// Manually set OTA Provider parameters
virtual void TestModeSetProviderParameters(NodeId nodeId, FabricIndex fabIndex, EndpointId endpointId) = 0;
};
Expand Down
16 changes: 16 additions & 0 deletions src/lib/shell/commands/Ota.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,20 @@ CHIP_ERROR ApplyImageHandler(int argc, char ** argv)
return CHIP_NO_ERROR;
}

CHIP_ERROR NotifyImageHandler(int argc, char ** argv)
{
VerifyOrReturnError(GetRequestorInstance() != nullptr, CHIP_ERROR_INCORRECT_STATE);
VerifyOrReturnError(argc == 3, CHIP_ERROR_INVALID_ARGUMENT);

const FabricIndex fabricIndex = static_cast<FabricIndex>(strtoul(argv[0], nullptr, 10));
const NodeId providerNodeId = static_cast<NodeId>(strtoull(argv[1], nullptr, 10));
const EndpointId providerEndpointId = static_cast<EndpointId>(strtoul(argv[2], nullptr, 10));

GetRequestorInstance()->TestModeSetProviderParameters(providerNodeId, fabricIndex, providerEndpointId);
PlatformMgr().ScheduleWork([](intptr_t) { GetRequestorInstance()->NotifyUpdateApplied(); });
return CHIP_NO_ERROR;
}

CHIP_ERROR OtaHandler(int argc, char ** argv)
{
if (argc == 0)
Expand All @@ -85,6 +99,8 @@ void RegisterOtaCommands()
{ &QueryImageHandler, "query", "Query for a new image. Usage: ota query <fabric-index> <provider-node-id> <endpoint-id>" },
{ &ApplyImageHandler, "apply",
"Apply the current update. Usage ota apply <fabric-index> <provider-node-id> <endpoint-id>" },
{ &NotifyImageHandler, "notify",
"Notify the new image has been applied. Usage: ota notify <fabric-index> <provider-node-id> <endpoint-id>" },
};

sSubShell.RegisterCommands(subCommands, ArraySize(subCommands));
Expand Down
10 changes: 5 additions & 5 deletions src/platform/GenericOTARequestorDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ uint16_t GenericOTARequestorDriver::GetMaxDownloadBlockSize()
return 1024;
}

void GenericOTARequestorDriver::HandleError(OTAUpdateStateEnum state, CHIP_ERROR error)
void GenericOTARequestorDriver::HandleError(UpdateFailureState state, CHIP_ERROR error)
{
// TODO: Schedule the next QueryImage
}

void GenericOTARequestorDriver::UpdateAvailable(const UpdateDescription & update, System::Clock::Seconds32 delay)
{
VerifyOrDie(mRequestor != nullptr);
ScheduleDelayedAction(OTAUpdateStateEnum::kDelayedOnQuery, delay,
ScheduleDelayedAction(UpdateFailureState::kDownloading, delay,
[](System::Layer *, void * context) { ToDriver(context)->mRequestor->DownloadUpdate(); });
}

Expand All @@ -70,14 +70,14 @@ void GenericOTARequestorDriver::UpdateDownloaded()
void GenericOTARequestorDriver::UpdateConfirmed(System::Clock::Seconds32 delay)
{
VerifyOrDie(mImageProcessor != nullptr);
ScheduleDelayedAction(OTAUpdateStateEnum::kDelayedOnApply, delay,
ScheduleDelayedAction(UpdateFailureState::kApplying, delay,
[](System::Layer *, void * context) { ToDriver(context)->mImageProcessor->Apply(); });
}

void GenericOTARequestorDriver::UpdateSuspended(System::Clock::Seconds32 delay)
{
VerifyOrDie(mRequestor != nullptr);
ScheduleDelayedAction(OTAUpdateStateEnum::kDelayedOnApply, delay,
ScheduleDelayedAction(UpdateFailureState::kAwaitingNextAction, delay,
Damian-Nordic marked this conversation as resolved.
Show resolved Hide resolved
[](System::Layer *, void * context) { ToDriver(context)->mRequestor->ApplyUpdate(); });
}

Expand All @@ -87,7 +87,7 @@ void GenericOTARequestorDriver::UpdateDiscontinued()
mImageProcessor->Abort();
}

void GenericOTARequestorDriver::ScheduleDelayedAction(OTAUpdateStateEnum state, System::Clock::Seconds32 delay,
void GenericOTARequestorDriver::ScheduleDelayedAction(UpdateFailureState state, System::Clock::Seconds32 delay,
System::TimerCompleteCallback action)
{
CHIP_ERROR error = SystemLayer().StartTimer(std::chrono::duration_cast<System::Clock::Timeout>(delay), action, this);
Expand Down
5 changes: 2 additions & 3 deletions src/platform/GenericOTARequestorDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class GenericOTARequestorDriver : public OTARequestorDriver
bool CanConsent() override;
uint16_t GetMaxDownloadBlockSize() override;

void HandleError(app::Clusters::OtaSoftwareUpdateRequestor::OTAUpdateStateEnum state, CHIP_ERROR error) override;
void HandleError(UpdateFailureState state, CHIP_ERROR error) override;
void UpdateAvailable(const UpdateDescription & update, System::Clock::Seconds32 delay) override;
void UpdateNotFound(UpdateNotFoundReason reason, System::Clock::Seconds32 delay) override;
void UpdateDownloaded() override;
Expand All @@ -54,8 +54,7 @@ class GenericOTARequestorDriver : public OTARequestorDriver
void UpdateDiscontinued() override;

private:
void ScheduleDelayedAction(app::Clusters::OtaSoftwareUpdateRequestor::OTAUpdateStateEnum state, System::Clock::Seconds32 delay,
System::TimerCompleteCallback action);
void ScheduleDelayedAction(UpdateFailureState state, System::Clock::Seconds32 delay, System::TimerCompleteCallback action);

OTARequestorInterface * mRequestor = nullptr;
OTAImageProcessorInterface * mImageProcessor = nullptr;
Expand Down
Loading