From 0f4cddafab56c1fb5bc3af900351227886a62d81 Mon Sep 17 00:00:00 2001 From: ATmobica Date: Fri, 3 Dec 2021 13:46:30 +0000 Subject: [PATCH] Add Mbed OTA Downloader Add download image service to ota-requestor app --- .../ota-requestor-app/mbed/CMakeLists.txt | 3 + .../ota-requestor-app/mbed/main/AppTask.cpp | 64 +++++- .../mbed/main/OTADownloaderImpl.cpp | 205 ++++++++++++++++++ .../mbed/main/OTAImageProcessorImpl.cpp | 44 ++++ .../mbed/main/OTARequestorImpl.cpp | 82 ++++--- .../mbed/main/include/AppEvent.h | 14 ++ .../mbed/main/include/AppTask.h | 8 +- .../mbed/main/include/CHIPProjectConfig.h | 3 - .../mbed/main/include/OTADownloaderImpl.h | 105 +++++++++ .../mbed/main/include/OTAImageProcessorImpl.h | 44 ++++ .../mbed/main/include/OTARequestorImpl.h | 33 ++- 11 files changed, 550 insertions(+), 55 deletions(-) create mode 100644 examples/ota-requestor-app/mbed/main/OTADownloaderImpl.cpp create mode 100644 examples/ota-requestor-app/mbed/main/include/OTADownloaderImpl.h diff --git a/examples/ota-requestor-app/mbed/CMakeLists.txt b/examples/ota-requestor-app/mbed/CMakeLists.txt index a4cae46e1dbfce..6efcab4e9edc2f 100644 --- a/examples/ota-requestor-app/mbed/CMakeLists.txt +++ b/examples/ota-requestor-app/mbed/CMakeLists.txt @@ -51,6 +51,9 @@ target_sources(${APP_TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/main/AppTask.cpp ${MBED_COMMON}/util/LEDWidget.cpp ${CMAKE_CURRENT_SOURCE_DIR}/main/OTARequestorImpl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/OTARequestorDriverImpl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/OTADownloaderImpl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/main/OTAImageProcessorImpl.cpp ${GEN_DIR}/ota-requestor-app/zap-generated/attribute-size.cpp ${GEN_DIR}/ota-requestor-app/zap-generated/callback-stub.cpp diff --git a/examples/ota-requestor-app/mbed/main/AppTask.cpp b/examples/ota-requestor-app/mbed/main/AppTask.cpp index 898c1536c8849a..3980dc561ae016 100644 --- a/examples/ota-requestor-app/mbed/main/AppTask.cpp +++ b/examples/ota-requestor-app/mbed/main/AppTask.cpp @@ -45,7 +45,8 @@ #include "events/EventQueue.h" #include "platform/Callback.h" -#include "OTARequestorImpl.h" +#include +#include static bool sIsWiFiStationProvisioned = false; static bool sIsWiFiStationEnabled = false; @@ -61,6 +62,9 @@ using namespace ::chip::DeviceLayer; static LEDWidget sStatusLED(MBED_CONF_APP_SYSTEM_STATE_LED); +static OTARequestorDriverImpl sRequestorDriver; +static OTAImageProcessorImpl sImageProcessor; + AppTask AppTask::sAppTask; int AppTask::Init() @@ -87,7 +91,11 @@ int AppTask::Init() ConnectivityMgr().SetBLEAdvertisingEnabled(true); } - OTARequestorImpl::GetInstance().Init(OnConnectProviderCallback); + OTARequestorImpl::GetInstance().Init(OnConnectProviderCallback, OnProviderResponseCallback); + OTARequestorImpl::GetInstance().SetOtaRequestorDriver(&sRequestorDriver); + + OTADownloaderImpl::GetInstance().Init(OnDownloadCompletedCallback); + OTADownloaderImpl::GetInstance().SetImageProcessorDelegate(&sImageProcessor); chip::DeviceLayer::ConnectivityMgrImpl().StartWiFiManagement(); @@ -163,13 +171,33 @@ int AppTask::StartApp() } } -void AppTask::OnConnectProviderHandler(AppEvent * aEvent) +void AppTask::OnOtaEventHandler(AppEvent * aEvent) { - ChipLogProgress(NotSpecified, "OnConnectProviderHandler"); - - OTARequestorImpl::GetInstance().ConnectProvider(aEvent->OTAProviderConnectEvent.nodeId, - aEvent->OTAProviderConnectEvent.fabricIndex, - aEvent->OTAProviderConnectEvent.ipAddress); + switch (aEvent->Type) + { + case AppEvent::kEventType_ota_provider_connect: + ChipLogProgress(NotSpecified, "OTA provider connect event"); + + OTARequestorImpl::GetInstance().ConnectProvider(aEvent->OTAProviderConnectEvent.nodeId, + aEvent->OTAProviderConnectEvent.fabricIndex, + aEvent->OTAProviderConnectEvent.ipAddress); + break; + case AppEvent::kEventType_ota_provider_response: { + ChipLogProgress(NotSpecified, "OTA provider response event"); + OTADownloaderImpl::GetInstance().SetDownloadImageInfo(aEvent->OTAProviderResponseEvent.imageDatails->updateFileName); + OTADownloaderImpl::GetInstance().BeginDownload(); + break; + } + case AppEvent::kEventType_ota_download_completed: + ChipLogProgress(NotSpecified, "OTA download completed event"); + ChipLogProgress(NotSpecified, "Download %.*s image size %ukB", + static_cast(aEvent->OTADownloadCompletedEvent.imageInfo->imageName.size()), + aEvent->OTADownloadCompletedEvent.imageInfo->imageName.data(), + (static_cast(aEvent->OTADownloadCompletedEvent.imageInfo->imageSize) / 1024u)); + break; + default: + ChipLogError(NotSpecified, "OTA event unknown"); + } } void AppTask::OnConnectProviderCallback(NodeId nodeId, FabricIndex fabricIndex, chip::Optional ipAddress) @@ -179,10 +207,28 @@ void AppTask::OnConnectProviderCallback(NodeId nodeId, FabricIndex fabricIndex, ota_connect_provider_event.OTAProviderConnectEvent.nodeId = nodeId; ota_connect_provider_event.OTAProviderConnectEvent.fabricIndex = fabricIndex; ota_connect_provider_event.OTAProviderConnectEvent.ipAddress = reinterpret_cast(ipAddress.Value().data()); - ota_connect_provider_event.Handler = OnConnectProviderHandler; + ota_connect_provider_event.Handler = OnOtaEventHandler; sAppTask.PostEvent(&ota_connect_provider_event); } +void AppTask::OnProviderResponseCallback(OTARequestorImpl::OTAUpdateDetails * updateDetails) +{ + AppEvent ota_provider_response_event; + ota_provider_response_event.Type = AppEvent::kEventType_ota_provider_response; + ota_provider_response_event.Handler = OnOtaEventHandler; + ota_provider_response_event.OTAProviderResponseEvent.imageDatails = updateDetails; + sAppTask.PostEvent(&ota_provider_response_event); +} + +void AppTask::OnDownloadCompletedCallback(OTADownloaderImpl::ImageInfo * imageInfo) +{ + AppEvent ota_download_completed_event; + ota_download_completed_event.Type = AppEvent::kEventType_ota_download_completed; + ota_download_completed_event.Handler = OnOtaEventHandler; + ota_download_completed_event.OTADownloadCompletedEvent.imageInfo = imageInfo; + sAppTask.PostEvent(&ota_download_completed_event); +} + void AppTask::PostEvent(AppEvent * aEvent) { auto handle = sAppEventQueue.call([event = *aEvent, this] { DispatchEvent(&event); }); diff --git a/examples/ota-requestor-app/mbed/main/OTADownloaderImpl.cpp b/examples/ota-requestor-app/mbed/main/OTADownloaderImpl.cpp new file mode 100644 index 00000000000000..8f90671dbb5f13 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/OTADownloaderImpl.cpp @@ -0,0 +1,205 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include +#include +#include +#include + +using namespace ::chip; +using namespace chip::bdx; + +OTADownloaderImpl OTADownloaderImpl::sInstance; + +OTADownloaderImpl::OTADownloaderImpl() {} + +void OTADownloaderImpl::BeginDownload() +{ + ChipLogProgress(SoftwareUpdate, "Begin download"); + + OperationalDeviceProxy * deviceProxy = Server::GetInstance().GetOperationalDeviceProxy(); + if (deviceProxy == nullptr) + { + ChipLogError(SoftwareUpdate, "Provider connection not established"); + return; + } + + Messaging::ExchangeManager * exchangeMgr = deviceProxy->GetExchangeManager(); + Optional session = deviceProxy->GetSecureSession(); + Messaging::ExchangeContext * exchangeCtx = nullptr; + + if (exchangeMgr != nullptr && session.HasValue()) + { + exchangeCtx = exchangeMgr->NewContext(session.Value(), this); + } + + if (exchangeCtx == nullptr) + { + ChipLogError(SoftwareUpdate, "Failed to allocate exchange"); + return; + } + + mExchangeCtx = exchangeCtx; + + TransferSession::TransferInitData initOptions; + initOptions.TransferCtlFlags = TransferControlFlags::kReceiverDrive; + initOptions.MaxBlockSize = mBlockSize; + initOptions.FileDesLength = mImageInfo.imageName.size(); + initOptions.FileDesignator = reinterpret_cast(mImageInfo.imageName.data()); + + CHIP_ERROR error = + InitiateTransfer(&DeviceLayer::SystemLayer(), TransferRole::kReceiver, initOptions, System::Clock::Seconds16(20)); + + if (error != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Failed to initiate BDX transfer: %" CHIP_ERROR_FORMAT, error.Format()); + } +} + +void OTADownloaderImpl::OnPreparedForDownload() +{ + ChipLogProgress(SoftwareUpdate, "On Prepared For Download"); +} + +void OTADownloaderImpl::OnBlockProcessed(BlockActionType action) +{ + ChipLogProgress(SoftwareUpdate, "On Block Processed"); +} + +void OTADownloaderImpl::HandleTransferSessionOutput(TransferSession::OutputEvent & event) +{ + using OutputEventType = TransferSession::OutputEventType; + using SendMessageFlags = Messaging::SendMessageFlags; + + CHIP_ERROR error = CHIP_NO_ERROR; + + if (event.EventType != OutputEventType::kNone) + { + ChipLogDetail(BDX, "OutputEvent type: %s", event.ToString(event.EventType)); + } + + switch (event.EventType) + { + case OutputEventType::kNone: + if (mIsTransferComplete) + { + ChipLogProgress(BDX, "Image file transfer complete"); + mTransfer.Reset(); + mIsTransferComplete = false; + + error = mImageProcessorDelegate->Finalize(); + if (error != CHIP_NO_ERROR) + { + ChipLogError(BDX, "Image processing finalize failed: %" CHIP_ERROR_FORMAT, error.Format()); + mImageProcessorDelegate->Abort(); + return; + } + + if (mDownloadCompletedCallback) + { + mDownloadCompletedCallback(&mImageInfo); + } + } + break; + case OutputEventType::kMsgToSend: { + Messaging::SendFlags flags; + flags.Set(SendMessageFlags::kFromInitiator, event.msgTypeData.MessageType == to_underlying(MessageType::ReceiveInit)); + flags.Set(SendMessageFlags::kExpectResponse, event.msgTypeData.MessageType != to_underlying(MessageType::BlockAckEOF)); + + VerifyOrReturn(mExchangeCtx != nullptr, ChipLogError(BDX, "Exchange context is null")); + error = + mExchangeCtx->SendMessage(event.msgTypeData.ProtocolId, event.msgTypeData.MessageType, std::move(event.MsgData), flags); + VerifyOrReturn(error == CHIP_NO_ERROR, ChipLogError(BDX, "SendMessage failed: %" CHIP_ERROR_FORMAT, error.Format())); + break; + } + case TransferSession::OutputEventType::kAcceptReceived: + ChipLogProgress(BDX, "Starting image file transfer size %lldB", static_cast(event.transferAcceptData.Length)); + error = mImageProcessorDelegate->PrepareDownload(); + if (error != CHIP_NO_ERROR) + { + ChipLogError(BDX, "Image processing prepare failed: %" CHIP_ERROR_FORMAT, error.Format()); + mTransfer.Reset(); + mImageProcessorDelegate->Abort(); + return; + } + + mImageInfo.imageSize = 0; + + error = mTransfer.PrepareBlockQuery(); + VerifyOrReturn(error == CHIP_NO_ERROR, ChipLogError(BDX, "PrepareBlockQuery failed: %" CHIP_ERROR_FORMAT, error.Format())); + + break; + case TransferSession::OutputEventType::kBlockReceived: { + ChipLogProgress(BDX, "Received %uB (total: %ukB)", static_cast(event.blockdata.Length), + static_cast(mTransfer.GetNumBytesProcessed()) / 1024u); + + ByteSpan data(event.blockdata.Data, event.blockdata.Length); + mImageProcessorDelegate->ProcessBlock(data); + if (error != CHIP_NO_ERROR) + { + ChipLogError(BDX, "Image processing process block failed: %" CHIP_ERROR_FORMAT, error.Format()); + mTransfer.Reset(); + ; + mImageProcessorDelegate->Abort(); + return; + } + + mImageInfo.imageSize += event.blockdata.Length; + + if (event.blockdata.IsEof) + { + error = mTransfer.PrepareBlockAck(); + VerifyOrReturn(error == CHIP_NO_ERROR, + ChipLogError(BDX, "PrepareBlockAck failed: %" CHIP_ERROR_FORMAT, error.Format())); + mIsTransferComplete = true; + } + else + { + error = mTransfer.PrepareBlockQuery(); + VerifyOrReturn(error == CHIP_NO_ERROR, + ChipLogError(BDX, "PrepareBlockQuery failed: %" CHIP_ERROR_FORMAT, error.Format())); + } + break; + } + case TransferSession::OutputEventType::kStatusReceived: + ChipLogError(BDX, "Received status %" PRIu16, to_underlying(event.statusData.statusCode)); + + if (event.statusData.statusCode != bdx::StatusCode::kNone) + { + mTransfer.Reset(); + mImageProcessorDelegate->Abort(); + } + break; + case TransferSession::OutputEventType::kInternalError: + ChipLogError(BDX, "Transfer stopped due to internal error"); + mTransfer.Reset(); + mImageProcessorDelegate->Abort(); + break; + case TransferSession::OutputEventType::kTransferTimeout: + ChipLogError(BDX, "Transfer timed out"); + mTransfer.Reset(); + mImageProcessorDelegate->Abort(); + break; + case TransferSession::OutputEventType::kInitReceived: + case TransferSession::OutputEventType::kAckReceived: + case TransferSession::OutputEventType::kQueryReceived: + case TransferSession::OutputEventType::kAckEOFReceived: + default: + ChipLogError(BDX, "Unexpected BDX event type: %" PRIu16, to_underlying(event.EventType)); + } +} diff --git a/examples/ota-requestor-app/mbed/main/OTAImageProcessorImpl.cpp b/examples/ota-requestor-app/mbed/main/OTAImageProcessorImpl.cpp index e69de29bb2d1d6..64f42287741092 100644 --- a/examples/ota-requestor-app/mbed/main/OTAImageProcessorImpl.cpp +++ b/examples/ota-requestor-app/mbed/main/OTAImageProcessorImpl.cpp @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +#include +#include + +CHIP_ERROR OTAImageProcessorImpl::PrepareDownload() +{ + ChipLogProgress(SoftwareUpdate, "Prepare download"); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(chip::ByteSpan & data) +{ + ChipLogProgress(SoftwareUpdate, "Process block"); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Finalize() +{ + ChipLogProgress(SoftwareUpdate, "Finalize"); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAImageProcessorImpl::Abort() +{ + ChipLogProgress(SoftwareUpdate, "Abort"); + return CHIP_NO_ERROR; +}; diff --git a/examples/ota-requestor-app/mbed/main/OTARequestorImpl.cpp b/examples/ota-requestor-app/mbed/main/OTARequestorImpl.cpp index e2241756923879..09489a4c175e34 100644 --- a/examples/ota-requestor-app/mbed/main/OTARequestorImpl.cpp +++ b/examples/ota-requestor-app/mbed/main/OTARequestorImpl.cpp @@ -69,9 +69,30 @@ static Callback mOnConnectionFailureCallback(OnProvid OTARequestorImpl::OTARequestorImpl() { - mProviderNodeId = chip::kUndefinedNodeId; - mProviderFabricIndex = chip::kUndefinedFabricIndex; - mConnectProviderCallback = nullptr; + mOperationalDeviceProxy = nullptr; + mProviderNodeId = chip::kUndefinedNodeId; + mProviderFabricIndex = chip::kUndefinedFabricIndex; + mConnectProviderCallback = nullptr; + mProviderResponseCallback = nullptr; + + mUpdateDetails.updateVersionString = chip::MutableCharSpan(mUpdateVersion, kVersionBufLen); + mUpdateDetails.updateFileName = chip::MutableCharSpan(mUpdateFileName, kFileNameBufLen); + mUpdateDetails.updateToken = chip::MutableByteSpan(mUpdateToken, kTokenBufLen); +} + +chip::CharSpan OTARequestorImpl::GetFileNameFromURI(chip::CharSpan imageURI) +{ + constexpr char delimiter = '/'; + + char * ptr = strrchr(imageURI.data(), delimiter); + + if (ptr != nullptr) + { + size_t offset = ptr - imageURI.data() + 1; + imageURI = imageURI.SubSpan(offset, imageURI.size() - offset); + } + + return imageURI; } bool OTARequestorImpl::HandleAnnounceOTAProvider( @@ -93,7 +114,8 @@ bool OTARequestorImpl::HandleAnnounceOTAProvider( ChipLogProgress(SoftwareUpdate, " FabricIndex: %" PRIu8, mProviderFabricIndex); ChipLogProgress(SoftwareUpdate, " ProviderNodeID: %" PRIu64 " (0x%" PRIX64 ")", mProviderNodeId, mProviderNodeId); ChipLogProgress(SoftwareUpdate, " VendorID: %" PRIu16 " (0x%" PRIX16 ")", commandData.vendorId, commandData.vendorId); - ChipLogProgress(SoftwareUpdate, " ProviderIP: %s", reinterpret_cast(mProviderIpAddress.Value().data())); + ChipLogProgress(SoftwareUpdate, " ProviderIP: %.*s", static_cast(mProviderIpAddress.Value().size()), + reinterpret_cast(mProviderIpAddress.Value().data())); ChipLogProgress(SoftwareUpdate, " AnnouncementReason: %" PRIu8, mAnnouncementReason); if (mConnectProviderCallback) @@ -122,24 +144,23 @@ void OTARequestorImpl::ConnectProvider(NodeId peerNodeId, FabricIndex peerFabric .imDelegate = chip::Platform::New(), }; - chip::OperationalDeviceProxy * operationalDeviceProxy = - chip::Platform::New(initParams, fabric->GetPeerIdForNode(peerNodeId)); - if (operationalDeviceProxy == nullptr) + mOperationalDeviceProxy = chip::Platform::New(initParams, fabric->GetPeerIdForNode(peerNodeId)); + if (mOperationalDeviceProxy == nullptr) { ChipLogError(SoftwareUpdate, "Failed in creating an instance of OperationalDeviceProxy"); return; } - server->SetOperationalDeviceProxy(operationalDeviceProxy); + server->SetOperationalDeviceProxy(mOperationalDeviceProxy); // Explicitly calling UpdateDeviceData() should not be needed once OperationalDeviceProxy can resolve IP address from node // ID and fabric index IPAddress ipAddr; IPAddress::FromString(ipAddress, ipAddr); PeerAddress addr = PeerAddress::UDP(ipAddr, CHIP_PORT); - operationalDeviceProxy->UpdateDeviceData(addr, operationalDeviceProxy->GetMRPConfig()); + mOperationalDeviceProxy->UpdateDeviceData(addr, mOperationalDeviceProxy->GetMRPConfig()); - CHIP_ERROR err = operationalDeviceProxy->Connect(&mOnConnectedCallback, &mOnConnectionFailureCallback); + CHIP_ERROR err = mOperationalDeviceProxy->Connect(&mOnConnectedCallback, &mOnConnectionFailureCallback); if (err != CHIP_NO_ERROR) { ChipLogError(SoftwareUpdate, "Cannot establish connection to peer device: %" CHIP_ERROR_FORMAT, err.Format()); @@ -223,31 +244,24 @@ bool OTARequestorImpl::HandleQueryImageResponse(void * context, uint8_t status, } ChipLogProgress(SoftwareUpdate, " Image URI: %.*s", static_cast(imageURI.size()), imageURI.data()); - ChipLogProgress(SoftwareUpdate, " Version: %" PRIu32, softwareVersion); - - if (updateToken.size() > sizeof(mUpdateDetails.updateToken)) - { - ChipLogError(SoftwareUpdate, "Update token too long"); - return false; - } - - memcpy(mUpdateDetails.updateToken, updateToken.data(), updateToken.size()); - mUpdateDetails.updateTokenLen = static_cast(updateToken.size()); - mUpdateDetails.updateVersion = softwareVersion; - - if (mOtaRequestorDriver) - { - auto ret = mOtaRequestorDriver->CheckImageDownloadAllowed(); - if (!ret) - { - ChipLogProgress(SoftwareUpdate, "Update download not allowed"); - return false; - } - } - - if (mDownloadUpdateCallback) + ChipLogProgress(SoftwareUpdate, " Version: %d name: %.*s", softwareVersion, static_cast(softwareVersionString.size()), + softwareVersionString.data()); + + mUpdateDetails.updateVersion = softwareVersion; + memcpy((void *) mUpdateDetails.updateVersionString.begin(), softwareVersionString.data(), softwareVersionString.size()); + mUpdateDetails.updateVersionString.reduce_size(softwareVersionString.size()); + auto fileName = GetFileNameFromURI(imageURI); + memcpy((void *) mUpdateDetails.updateFileName.begin(), fileName.data(), fileName.size()); + mUpdateDetails.updateFileName.reduce_size(fileName.size()); + memcpy((void *) mUpdateDetails.updateToken.begin(), updateToken.data(), updateToken.size()); + mUpdateDetails.updateToken.reduce_size(updateToken.size()); + + ChipLogProgress(SoftwareUpdate, " Image name: %.*s", static_cast(mUpdateDetails.updateFileName.size()), + mUpdateDetails.updateFileName.data()); + + if (mProviderResponseCallback) { - mDownloadUpdateCallback(); + mProviderResponseCallback(&mUpdateDetails); } return false; } \ No newline at end of file diff --git a/examples/ota-requestor-app/mbed/main/include/AppEvent.h b/examples/ota-requestor-app/mbed/main/include/AppEvent.h index 527a6a2b9e1267..eec6cbb0839fb2 100644 --- a/examples/ota-requestor-app/mbed/main/include/AppEvent.h +++ b/examples/ota-requestor-app/mbed/main/include/AppEvent.h @@ -19,6 +19,8 @@ #pragma once +#include +#include #include #include @@ -30,6 +32,8 @@ struct AppEvent enum AppEventTypes { kEventType_ota_provider_connect = 0, + kEventType_ota_provider_response, + kEventType_ota_download_completed, }; uint16_t Type; @@ -42,6 +46,16 @@ struct AppEvent chip::FabricIndex fabricIndex; const char * ipAddress; } OTAProviderConnectEvent; + + struct + { + OTARequestorImpl::OTAUpdateDetails * imageDatails; + } OTAProviderResponseEvent; + + struct + { + OTADownloaderImpl::ImageInfo * imageInfo; + } OTADownloadCompletedEvent; }; EventHandler Handler; diff --git a/examples/ota-requestor-app/mbed/main/include/AppTask.h b/examples/ota-requestor-app/mbed/main/include/AppTask.h index f850f4b4d5d76f..c0c22e25b38baa 100644 --- a/examples/ota-requestor-app/mbed/main/include/AppTask.h +++ b/examples/ota-requestor-app/mbed/main/include/AppTask.h @@ -20,6 +20,8 @@ #pragma once #include "AppEvent.h" +#include +#include class AppTask { @@ -34,11 +36,15 @@ class AppTask void PostEvent(AppEvent * aEvent); void DispatchEvent(const AppEvent * event); - static void OnConnectProviderHandler(AppEvent * aEvent); + static void OnOtaEventHandler(AppEvent * aEvent); static void OnConnectProviderCallback(chip::NodeId nodeId, chip::FabricIndex fabricIndex, chip::Optional ipAddress); + static void OnProviderResponseCallback(OTARequestorImpl::OTAUpdateDetails * updateDetails); + + static void OnDownloadCompletedCallback(OTADownloaderImpl::ImageInfo * imageInfo); + static AppTask sAppTask; }; diff --git a/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h b/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h index 4e30516ad1db4a..222b6ed33c0b37 100644 --- a/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h +++ b/examples/ota-requestor-app/mbed/main/include/CHIPProjectConfig.h @@ -31,6 +31,3 @@ // Use a default pairing code if one hasn't been provisioned in flash. #define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE 20202021 #define CHIP_DEVICE_CONFIG_USE_TEST_SETUP_DISCRIMINATOR 0xF00 - -#define CHIP_DEVICE_CONFIG_ENABLE_EXTENDED_DISCOVERY 1 -#define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DISCOVERY 1 diff --git a/examples/ota-requestor-app/mbed/main/include/OTADownloaderImpl.h b/examples/ota-requestor-app/mbed/main/include/OTADownloaderImpl.h new file mode 100644 index 00000000000000..20bb7d320d1961 --- /dev/null +++ b/examples/ota-requestor-app/mbed/main/include/OTADownloaderImpl.h @@ -0,0 +1,105 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +/* This file contains the declarations for the OTADownloader class which + * abstracts the image download functionality from the particular protocol + * used for it. + * Applications and platforms implementing the OTA Requestor functionality + * must include this file + */ + +#include +#include +#include +#include +#include +#include + +#pragma once + +// A class that abstracts the image download functionality from the particular +// protocol used for that (BDX or possibly HTTPS) +class OTADownloaderImpl : public chip::bdx::Initiator +{ +public: + struct ImageInfo + { + chip::MutableCharSpan imageName; + size_t imageSize; + }; + +private: + typedef void (*DownloadCompletedCallback)(ImageInfo *); + +public: + static OTADownloaderImpl & GetInstance() { return sInstance; } + + void Init(DownloadCompletedCallback downloadCompletedCallback = nullptr) + { + mDownloadCompletedCallback = downloadCompletedCallback; + } + + // Application calls this method to direct OTADownloader to begin the download + void BeginDownload(); + + // Platform calls this method upon the completion of PrepareDownload() processing + void OnPreparedForDownload(); + + // Action parameter type for the OnBlockProcessed() + enum BlockActionType + { + kGetNext, + kEnd + }; + + // Platform calls this method upon the completion of ProcessBlock() processing + void OnBlockProcessed(BlockActionType action); + + // A setter for the delegate class pointer + void SetImageProcessorDelegate(OTAImageProcessorImpl * delegate); + + void SetDownloadImageInfo(chip::MutableCharSpan & imageName); + +private: + OTADownloaderImpl(); + + static OTADownloaderImpl sInstance; + + OTAImageProcessorImpl * mImageProcessorDelegate; + + // inherited from bdx::Endpoint + void HandleTransferSessionOutput(chip::bdx::TransferSession::OutputEvent & event); + + bool mIsTransferComplete = false; + + ImageInfo mImageInfo; + + static const uint16_t mBlockSize = 1024; + + DownloadCompletedCallback mDownloadCompletedCallback; +}; + +inline void OTADownloaderImpl::SetImageProcessorDelegate(OTAImageProcessorImpl * delegate) +{ + mImageProcessorDelegate = delegate; +} + +inline void OTADownloaderImpl::SetDownloadImageInfo(chip::MutableCharSpan & imageName) +{ + mImageInfo.imageName = imageName; +} \ No newline at end of file diff --git a/examples/ota-requestor-app/mbed/main/include/OTAImageProcessorImpl.h b/examples/ota-requestor-app/mbed/main/include/OTAImageProcessorImpl.h index e69de29bb2d1d6..bd9243c29304f9 100644 --- a/examples/ota-requestor-app/mbed/main/include/OTAImageProcessorImpl.h +++ b/examples/ota-requestor-app/mbed/main/include/OTAImageProcessorImpl.h @@ -0,0 +1,44 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * 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. + */ + +/* This file contains the declarations for OTAImageProcessor, a platform-agnostic + * interface for processing downloaded chunks of OTA image data. + * Each platform should provide an implementation of this interface. + */ + +#pragma once + +#include +#include + +class OTAImageProcessorImpl +{ +public: + // Open file, find block of space in persistent memory, or allocate a buffer, etc. + CHIP_ERROR PrepareDownload(); + + // Must not be a blocking call to support cases that require IO to elements such as // external peripherals/radios + CHIP_ERROR ProcessBlock(chip::ByteSpan & data); + + // Close file, close persistent storage, etc + CHIP_ERROR Finalize(); + + // Clean up the download which could mean erasing everything that was written, + // releasing buffers, etc. + CHIP_ERROR Abort(); +}; \ No newline at end of file diff --git a/examples/ota-requestor-app/mbed/main/include/OTARequestorImpl.h b/examples/ota-requestor-app/mbed/main/include/OTARequestorImpl.h index 32722f89cc374a..68c5c0f8d82d8b 100644 --- a/examples/ota-requestor-app/mbed/main/include/OTARequestorImpl.h +++ b/examples/ota-requestor-app/mbed/main/include/OTARequestorImpl.h @@ -31,23 +31,36 @@ class OTARequestorImpl { - typedef void (*ConnectToProviderCallback)(chip::NodeId, chip::FabricIndex, chip::Optional); - typedef void (*DownloadUpdateCallback)(void); + static constexpr uint8_t kVersionBufLen = 32; + char mUpdateVersion[kVersionBufLen]; + + static constexpr size_t kFileNameBufLen = 256; + char mUpdateFileName[kFileNameBufLen]; // null-terminated + + static constexpr uint8_t kTokenBufLen = 32; + uint8_t mUpdateToken[kTokenBufLen]; public: struct OTAUpdateDetails { - uint8_t updateToken[32]; - uint8_t updateTokenLen; uint32_t updateVersion; + chip::MutableCharSpan updateVersionString; + chip::MutableCharSpan updateFileName; + chip::MutableByteSpan updateToken; }; +private: + typedef void (*ConnectToProviderCallback)(chip::NodeId, chip::FabricIndex, chip::Optional); + typedef void (*ProviderResponseCallback)(OTAUpdateDetails * updateDetails); + +public: static OTARequestorImpl & GetInstance() { return sInstance; } - void Init(ConnectToProviderCallback connectProviderCallback = nullptr, DownloadUpdateCallback downloadUpdateCallback = nullptr) + void Init(ConnectToProviderCallback connectProviderCallback = nullptr, + ProviderResponseCallback providerResponseCallback = nullptr) { - mConnectProviderCallback = connectProviderCallback; - mDownloadUpdateCallback = downloadUpdateCallback; + mConnectProviderCallback = connectProviderCallback; + mProviderResponseCallback = providerResponseCallback; } // Handler for the AnnounceOTAProvider command @@ -85,6 +98,8 @@ class OTARequestorImpl static OTARequestorImpl sInstance; + chip::OperationalDeviceProxy * mOperationalDeviceProxy; + chip::NodeId mProviderNodeId; chip::FabricIndex mProviderFabricIndex; EmberAfOTAAnnouncementReason mAnnouncementReason; @@ -93,9 +108,11 @@ class OTARequestorImpl OTAUpdateDetails mUpdateDetails; ConnectToProviderCallback mConnectProviderCallback; - DownloadUpdateCallback mDownloadUpdateCallback; + ProviderResponseCallback mProviderResponseCallback; OTARequestorDriverImpl * mOtaRequestorDriver; + + chip::CharSpan GetFileNameFromURI(chip::CharSpan imageURI); }; inline void OTARequestorImpl::SetOtaRequestorDriver(OTARequestorDriverImpl * driver)