From 85d3970237f8b9f7627ca1ecde36b411bee9f470 Mon Sep 17 00:00:00 2001
From: Alex Tsitsiura <s07641069@gmail.com>
Date: Mon, 23 Dec 2024 12:52:43 +0000
Subject: [PATCH 1/3] [Telink] Add OTA resume support

---
 src/platform/telink/OTAImageProcessorImpl.cpp | 81 ++++++++++++++++---
 src/platform/telink/OTAImageProcessorImpl.h   |  7 ++
 2 files changed, 76 insertions(+), 12 deletions(-)

diff --git a/src/platform/telink/OTAImageProcessorImpl.cpp b/src/platform/telink/OTAImageProcessorImpl.cpp
index a47a59fadfdb38..e335ac531c33cd 100644
--- a/src/platform/telink/OTAImageProcessorImpl.cpp
+++ b/src/platform/telink/OTAImageProcessorImpl.cpp
@@ -20,6 +20,7 @@
 #include <app/clusters/ota-requestor/OTADownloader.h>
 #include <app/clusters/ota-requestor/OTARequestorInterface.h>
 #include <platform/CHIPDeviceLayer.h>
+#include <platform/KeyValueStoreManager.h>
 
 #include <zephyr/dfu/mcuboot.h>
 #include <zephyr/storage/flash_map.h>
@@ -28,6 +29,8 @@
 
 static struct stream_flash_ctx stream;
 
+using namespace ::chip::DeviceLayer::PersistedStorage;
+
 namespace chip {
 namespace {
 
@@ -53,14 +56,10 @@ CHIP_ERROR OTAImageProcessorImpl::PrepareDownload()
 
     return DeviceLayer::SystemLayer().ScheduleLambda([this] { mDownloader->OnPreparedForDownload(PrepareDownloadImpl()); });
 }
-
-CHIP_ERROR OTAImageProcessorImpl::PrepareDownloadImpl()
-{
-    mHeaderParser.Init();
-    mParams = {};
-
     const struct device * flash_dev;
 
+CHIP_ERROR OTAImageProcessorImpl::InitFlashStream(size_t offset)
+{
     flash_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller));
     if (flash_dev == NULL)
     {
@@ -68,18 +67,28 @@ CHIP_ERROR OTAImageProcessorImpl::PrepareDownloadImpl()
         return System::MapErrorZephyr(-EFAULT);
     }
 
-    int err = stream_flash_init(&stream, flash_dev, mBuffer, sizeof(mBuffer), FIXED_PARTITION_OFFSET(slot1_partition),
-                                FIXED_PARTITION_SIZE(slot1_partition), NULL);
+    int err = stream_flash_init(&stream, flash_dev, mBuffer, sizeof(mBuffer), FIXED_PARTITION_OFFSET(slot1_partition) + offset,
+                                FIXED_PARTITION_SIZE(slot1_partition) - offset, NULL);
 
     if (err)
     {
         ChipLogError(SoftwareUpdate, "stream_flash_init failed (err %d)", err);
     }
 
-    PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadInProgress);
     return System::MapErrorZephyr(err);
 }
 
+CHIP_ERROR OTAImageProcessorImpl::PrepareDownloadImpl()
+{
+    mHeaderParser.Init();
+    mParams = {};
+
+    CHIP_ERROR error = InitFlashStream(0);
+
+    PostOTAStateChangeEvent(DeviceLayer::kOtaDownloadInProgress);
+    return error;
+}
+
 CHIP_ERROR OTAImageProcessorImpl::Finalize()
 {
     int err = stream_flash_buffered_write(&stream, NULL, 0, true);
@@ -136,8 +145,16 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & aBlock)
 
     if (error == CHIP_NO_ERROR)
     {
-        error = System::MapErrorZephyr(stream_flash_buffered_write(&stream, aBlock.data(), aBlock.size(), false));
-        mParams.downloadedBytes += aBlock.size();
+        if (downloadedBytesRestored)
+        {
+            mParams.downloadedBytes = downloadedBytesRestored;
+        }
+        else
+        {
+            error = System::MapErrorZephyr(stream_flash_buffered_write(&stream, aBlock.data(), aBlock.size(), false));
+            mParams.downloadedBytes += aBlock.size();
+        }
+        ReturnErrorOnFailure(KeyValueStoreMgr().Put(kDownloadedBytes, &mParams.downloadedBytes, sizeof(mParams.downloadedBytes)));
     }
 
     // Report the result back to the downloader asynchronously.
@@ -146,7 +163,15 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & aBlock)
         {
             ChipLogDetail(SoftwareUpdate, "Downloaded %u/%u bytes", static_cast<unsigned>(mParams.downloadedBytes),
                           static_cast<unsigned>(mParams.totalFileBytes));
-            mDownloader->FetchNextData();
+            if (downloadedBytesRestored)
+            {
+                mDownloader->SkipData(downloadedBytesRestored - aBlock.size());
+                downloadedBytesRestored = 0;
+            }
+            else
+            {
+                mDownloader->FetchNextData();
+            }
         }
         else
         {
@@ -174,6 +199,34 @@ CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage()
     return System::MapErrorZephyr(boot_write_img_confirmed());
 }
 
+CHIP_ERROR OTAImageProcessorImpl::RestoreBytes(ByteSpan & aBlock)
+{
+    uint8_t *ImageDigestBuffer = new uint8_t[aBlock.size()];
+    MutableByteSpan mImageDigest(ImageDigestBuffer, aBlock.size());
+
+    if (KeyValueStoreMgr().Get(kImageDigest, mImageDigest.data(), mImageDigest.size()) == CHIP_NO_ERROR && 
+        KeyValueStoreMgr().Get(kDownloadedBytes, &downloadedBytesRestored, sizeof(downloadedBytesRestored)) == CHIP_NO_ERROR &&
+        mImageDigest.data_equal(aBlock) && downloadedBytesRestored < mParams.totalFileBytes)
+    {
+        // Align to the nearest lower multiple of sector size (4 KB) for Flash erase/write
+        downloadedBytesRestored = ROUND_DOWN(downloadedBytesRestored, 0x1000);
+        ChipLogDetail(SoftwareUpdate, "Restored %u/%u bytes", static_cast<unsigned>(downloadedBytesRestored),
+                      static_cast<unsigned>(mParams.totalFileBytes))
+
+        // Reinit Flash Stream with offset
+        ReturnErrorOnFailure(System::MapErrorZephyr(stream_flash_buffered_write(&stream, NULL, 0, true)));
+        ReturnErrorOnFailure(InitFlashStream(downloadedBytesRestored));
+    }
+    else
+    {
+        downloadedBytesRestored = 0;
+        ReturnErrorOnFailure(KeyValueStoreMgr().Put(kImageDigest, aBlock.data(), aBlock.size()));
+    }
+    delete[] ImageDigestBuffer;
+
+    return CHIP_NO_ERROR;
+}
+
 CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & aBlock)
 {
     if (mHeaderParser.IsInitialized())
@@ -186,6 +239,10 @@ CHIP_ERROR OTAImageProcessorImpl::ProcessHeader(ByteSpan & aBlock)
         ReturnErrorOnFailure(error);
 
         mParams.totalFileBytes = header.mPayloadSize;
+
+        // Restore interrupted OTA process
+        RestoreBytes(header.mImageDigest);
+
         mHeaderParser.Clear();
     }
 
diff --git a/src/platform/telink/OTAImageProcessorImpl.h b/src/platform/telink/OTAImageProcessorImpl.h
index e49da76b835739..96870a016369e5 100755
--- a/src/platform/telink/OTAImageProcessorImpl.h
+++ b/src/platform/telink/OTAImageProcessorImpl.h
@@ -44,10 +44,17 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
 private:
     CHIP_ERROR PrepareDownloadImpl();
     CHIP_ERROR ProcessHeader(ByteSpan & aBlock);
+    CHIP_ERROR InitFlashStream(size_t offset);
+    CHIP_ERROR RestoreBytes(ByteSpan & aBlock);
 
     OTADownloader * mDownloader = nullptr;
     OTAImageHeaderParser mHeaderParser;
     uint8_t mBuffer[kBufferSize];
+
+    // Define non-volatile storage keys for DownloadedBytes and ImageDigest.
+    static constexpr char kDownloadedBytes[] = "DownloadedBytes";
+    static constexpr char kImageDigest[]     = "ImageDigest";
+    uint64_t downloadedBytesRestored = 0;
 };
 
 } // namespace DeviceLayer

From e0375206183bfcfc662d3863b3b1954fe15e139f Mon Sep 17 00:00:00 2001
From: "Restyled.io" <commits@restyled.io>
Date: Wed, 12 Mar 2025 11:17:39 +0000
Subject: [PATCH 2/3] Restyled by whitespace

---
 src/platform/telink/OTAImageProcessorImpl.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/platform/telink/OTAImageProcessorImpl.cpp b/src/platform/telink/OTAImageProcessorImpl.cpp
index e335ac531c33cd..c8911b3ba28675 100644
--- a/src/platform/telink/OTAImageProcessorImpl.cpp
+++ b/src/platform/telink/OTAImageProcessorImpl.cpp
@@ -204,7 +204,7 @@ CHIP_ERROR OTAImageProcessorImpl::RestoreBytes(ByteSpan & aBlock)
     uint8_t *ImageDigestBuffer = new uint8_t[aBlock.size()];
     MutableByteSpan mImageDigest(ImageDigestBuffer, aBlock.size());
 
-    if (KeyValueStoreMgr().Get(kImageDigest, mImageDigest.data(), mImageDigest.size()) == CHIP_NO_ERROR && 
+    if (KeyValueStoreMgr().Get(kImageDigest, mImageDigest.data(), mImageDigest.size()) == CHIP_NO_ERROR &&
         KeyValueStoreMgr().Get(kDownloadedBytes, &downloadedBytesRestored, sizeof(downloadedBytesRestored)) == CHIP_NO_ERROR &&
         mImageDigest.data_equal(aBlock) && downloadedBytesRestored < mParams.totalFileBytes)
     {

From 109469d2b060e07f3f9c65d986413061112425b0 Mon Sep 17 00:00:00 2001
From: "Restyled.io" <commits@restyled.io>
Date: Wed, 12 Mar 2025 11:17:43 +0000
Subject: [PATCH 3/3] Restyled by clang-format

---
 src/platform/telink/OTAImageProcessorImpl.cpp | 8 ++++----
 src/platform/telink/OTAImageProcessorImpl.h   | 2 +-
 2 files changed, 5 insertions(+), 5 deletions(-)
 mode change 100755 => 100644 src/platform/telink/OTAImageProcessorImpl.h

diff --git a/src/platform/telink/OTAImageProcessorImpl.cpp b/src/platform/telink/OTAImageProcessorImpl.cpp
index c8911b3ba28675..d3bcff3129b58b 100644
--- a/src/platform/telink/OTAImageProcessorImpl.cpp
+++ b/src/platform/telink/OTAImageProcessorImpl.cpp
@@ -56,7 +56,7 @@ CHIP_ERROR OTAImageProcessorImpl::PrepareDownload()
 
     return DeviceLayer::SystemLayer().ScheduleLambda([this] { mDownloader->OnPreparedForDownload(PrepareDownloadImpl()); });
 }
-    const struct device * flash_dev;
+const struct device * flash_dev;
 
 CHIP_ERROR OTAImageProcessorImpl::InitFlashStream(size_t offset)
 {
@@ -201,7 +201,7 @@ CHIP_ERROR OTAImageProcessorImpl::ConfirmCurrentImage()
 
 CHIP_ERROR OTAImageProcessorImpl::RestoreBytes(ByteSpan & aBlock)
 {
-    uint8_t *ImageDigestBuffer = new uint8_t[aBlock.size()];
+    uint8_t * ImageDigestBuffer = new uint8_t[aBlock.size()];
     MutableByteSpan mImageDigest(ImageDigestBuffer, aBlock.size());
 
     if (KeyValueStoreMgr().Get(kImageDigest, mImageDigest.data(), mImageDigest.size()) == CHIP_NO_ERROR &&
@@ -213,8 +213,8 @@ CHIP_ERROR OTAImageProcessorImpl::RestoreBytes(ByteSpan & aBlock)
         ChipLogDetail(SoftwareUpdate, "Restored %u/%u bytes", static_cast<unsigned>(downloadedBytesRestored),
                       static_cast<unsigned>(mParams.totalFileBytes))
 
-        // Reinit Flash Stream with offset
-        ReturnErrorOnFailure(System::MapErrorZephyr(stream_flash_buffered_write(&stream, NULL, 0, true)));
+            // Reinit Flash Stream with offset
+            ReturnErrorOnFailure(System::MapErrorZephyr(stream_flash_buffered_write(&stream, NULL, 0, true)));
         ReturnErrorOnFailure(InitFlashStream(downloadedBytesRestored));
     }
     else
diff --git a/src/platform/telink/OTAImageProcessorImpl.h b/src/platform/telink/OTAImageProcessorImpl.h
old mode 100755
new mode 100644
index 96870a016369e5..19ebb291fa98ea
--- a/src/platform/telink/OTAImageProcessorImpl.h
+++ b/src/platform/telink/OTAImageProcessorImpl.h
@@ -54,7 +54,7 @@ class OTAImageProcessorImpl : public OTAImageProcessorInterface
     // Define non-volatile storage keys for DownloadedBytes and ImageDigest.
     static constexpr char kDownloadedBytes[] = "DownloadedBytes";
     static constexpr char kImageDigest[]     = "ImageDigest";
-    uint64_t downloadedBytesRestored = 0;
+    uint64_t downloadedBytesRestored         = 0;
 };
 
 } // namespace DeviceLayer