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 Linux implementation of OTAImageProcessorInterface #12493

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
1 change: 1 addition & 0 deletions examples/ota-requestor-app/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni")

executable("chip-ota-requestor-app") {
sources = [
"LinuxOTAImageProcessor.cpp",
"LinuxOTARequestorDriver.cpp",
"main.cpp",
]
Expand Down
204 changes: 204 additions & 0 deletions examples/ota-requestor-app/linux/LinuxOTAImageProcessor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
/*
*
* 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 <app/clusters/ota-requestor/OTADownloader.h>

#include "LinuxOTAImageProcessor.h"

namespace chip {

// TODO: Dummy function to be removed once BDX downloader is implemented and can return a real instance
OTADownloader * GetDownloaderInstance()
{
return nullptr;
}

CHIP_ERROR LinuxOTAImageProcessor::PrepareDownload()
{
if (mParams.imageFile.empty())
{
ChipLogError(SoftwareUpdate, "Invalid output image file supplied");
return CHIP_ERROR_INTERNAL;
}

DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}

CHIP_ERROR LinuxOTAImageProcessor::Finalize()
{
DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}

CHIP_ERROR LinuxOTAImageProcessor::Abort()
{
if (mParams.imageFile.empty())
{
ChipLogError(SoftwareUpdate, "Invalid output image file supplied");
return CHIP_ERROR_INTERNAL;
}

DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}

CHIP_ERROR LinuxOTAImageProcessor::ProcessBlock(ByteSpan & block)
{
if (!mOfs.is_open() || !mOfs.good())
{
return CHIP_ERROR_INTERNAL;
}

if ((block.data() == nullptr) || block.empty())
{
return CHIP_ERROR_INVALID_ARGUMENT;
}

// Store block data for HandleProcessBlock to access
CHIP_ERROR err = SetBlock(block);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format());
}

DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}

void LinuxOTAImageProcessor::HandlePrepareDownload(intptr_t context)
{
OTADownloader * downloader = GetDownloaderInstance();
if (downloader == nullptr)
{
ChipLogError(SoftwareUpdate, "No known OTA downloader");
return;
}

auto * imageProcessor = reinterpret_cast<LinuxOTAImageProcessor *>(context);
if (imageProcessor == nullptr)
{
downloader->OnPreparedForDownload(CHIP_ERROR_INVALID_ARGUMENT);
return;
}

imageProcessor->mOfs.open(imageProcessor->mParams.imageFile.data(),
std::ofstream::out | std::ofstream::ate | std::ofstream::app);
if (!imageProcessor->mOfs.good())
{
downloader->OnPreparedForDownload(CHIP_ERROR_OPEN_FAILED);
return;
}

downloader->OnPreparedForDownload(CHIP_NO_ERROR);
}

void LinuxOTAImageProcessor::HandleFinalize(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<LinuxOTAImageProcessor *>(context);
if (imageProcessor == nullptr)
{
return;
}

imageProcessor->mOfs.close();
imageProcessor->ReleaseBlock();
}

void LinuxOTAImageProcessor::HandleAbort(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<LinuxOTAImageProcessor *>(context);
if (imageProcessor == nullptr)
{
return;
}

imageProcessor->mOfs.close();
remove(imageProcessor->mParams.imageFile.data());
imageProcessor->ReleaseBlock();
}

void LinuxOTAImageProcessor::HandleProcessBlock(intptr_t context)
{
OTADownloader * downloader = GetDownloaderInstance();
if (downloader == nullptr)
{
ChipLogError(SoftwareUpdate, "No known OTA downloader");
return;
}

auto * imageProcessor = reinterpret_cast<LinuxOTAImageProcessor *>(context);
if (imageProcessor == nullptr)
{
downloader->OnBlockProcessed(CHIP_ERROR_INVALID_ARGUMENT, OTADownloader::kEnd);
return;
}

// TODO: Process block header if any

if (!imageProcessor->mOfs.write(reinterpret_cast<const char *>(imageProcessor->mBlock.data()),
static_cast<std::streamsize>(imageProcessor->mBlock.size())))
{
downloader->OnBlockProcessed(CHIP_ERROR_WRITE_FAILED, OTADownloader::kEnd);
return;
}

imageProcessor->mParams.downloadedBytes += imageProcessor->mBlock.size();
downloader->OnBlockProcessed(CHIP_NO_ERROR, OTADownloader::kGetNext);
}

CHIP_ERROR LinuxOTAImageProcessor::SetBlock(ByteSpan & block)
{
if ((block.data() == nullptr) || block.empty())
{
return CHIP_NO_ERROR;
}

// Allocate memory for block data if it has not been done yet
if (mBlock.empty())
{
mBlock = MutableByteSpan(static_cast<uint8_t *>(chip::Platform::MemoryAlloc(block.size())), block.size());
if (mBlock.data() == nullptr)
{
return CHIP_ERROR_NO_MEMORY;
}
}

// Store the actual block data
CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format());
return err;
}

return CHIP_NO_ERROR;
}

CHIP_ERROR LinuxOTAImageProcessor::ReleaseBlock()
{
if (mBlock.data() != nullptr)
{
chip::Platform::MemoryFree(mBlock.data());
}

mBlock = MutableByteSpan();
return CHIP_NO_ERROR;
}

} // namespace chip
57 changes: 35 additions & 22 deletions examples/ota-requestor-app/linux/LinuxOTAImageProcessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,43 @@
* limitations under the License.
*/

/* This file contains the decalarions for the Linux implementation of the
* the OTAImageProcessorDriver interface class
*/

#include "app/clusters/ota-requestor/OTAImageProcessor.h"

class LinuxOTAImageProcessor : public OTAImageProcessorDriver
{
#pragma once

// Virtuial functions from OTAImageProcessorDriver -- start
// Open file, find block of space in persistent memory, or allocate a buffer, etc.
CHIP_ERROR PrepareDownload() { return CHIP_NO_ERROR; }
#include <app/clusters/ota-requestor/OTAImageProcessor.h>
#include <platform/CHIPDeviceLayer.h>

// 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) { return CHIP_NO_ERROR; }
#include <fstream>

// Close file, close persistent storage, etc
CHIP_ERROR Finalize() { return CHIP_NO_ERROR; }
namespace chip {

chip::Optional<uint8_t> PercentComplete() { return chip::Optional<uint8_t>(0); }

// Clean up the download which could mean erasing everything that was written,
// releasing buffers, etc.
CHIP_ERROR Abort() { return CHIP_NO_ERROR; }

// Virtuial functions from OTAImageProcessorDriver -- end
class LinuxOTAImageProcessor : public OTAImageProcessorInterface
{
public:
//////////// OTAImageProcessorInterface Implementation ///////////////
CHIP_ERROR PrepareDownload() override;
CHIP_ERROR Finalize() override;
CHIP_ERROR Abort() override;
CHIP_ERROR ProcessBlock(ByteSpan & block) override;

private:
//////////// Actual handlers for the OTAImageProcessorInterface ///////////////
static void HandlePrepareDownload(intptr_t context);
static void HandleFinalize(intptr_t context);
static void HandleAbort(intptr_t context);
static void HandleProcessBlock(intptr_t context);

/**
* Called to allocate memory for mBlock if necessary and set it to block
*/
CHIP_ERROR SetBlock(ByteSpan & block);

/**
* Called to release allocated memory for mBlock
*/
CHIP_ERROR ReleaseBlock();

std::ofstream mOfs;
MutableByteSpan mBlock;
};

} // namespace chip
2 changes: 2 additions & 0 deletions examples/ota-requestor-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ using chip::CharSpan;
using chip::DeviceProxy;
using chip::EndpointId;
using chip::FabricIndex;
using chip::LinuxOTAImageProcessor;
using chip::NodeId;
using chip::OnDeviceConnected;
using chip::OnDeviceConnectionFailure;
using chip::OTADownloader;
using chip::PeerId;
using chip::Server;
using chip::VendorId;
Expand Down
12 changes: 8 additions & 4 deletions src/app/clusters/ota-requestor/OTADownloader.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#pragma once

namespace chip {

// A class that abstracts the image download functionality from the particular
// protocol used for that (BDX or possibly HTTPS)
class OTADownloader
Expand All @@ -38,7 +40,7 @@ class OTADownloader
void virtual BeginDownload(){};

// Platform calls this method upon the completion of PrepareDownload() processing
void virtual OnPreparedForDownload(){};
void virtual OnPreparedForDownload(CHIP_ERROR status){};

// Action parameter type for the OnBlockProcessed()
enum BlockActionType
Expand All @@ -48,22 +50,24 @@ class OTADownloader
};

// Platform calls this method upon the completion of ProcessBlock() processing
void virtual OnBlockProcessed(BlockActionType action){};
void virtual OnBlockProcessed(CHIP_ERROR status, BlockActionType action){};

// A setter for the delegate class pointer
void SetImageProcessorDelegate(OTAImageProcessorDriver * delegate) { mImageProcessorDelegate = delegate; }
void SetImageProcessorDelegate(OTAImageProcessorInterface * delegate) { mImageProcessorDelegate = delegate; }

// API declarations end

// Destructor
virtual ~OTADownloader() = default;

private:
OTAImageProcessorDriver * mImageProcessorDelegate;
OTAImageProcessorInterface * mImageProcessorDelegate;
};

// Set the object implementing OTADownloader
void SetDownloaderInstance(OTADownloader * instance);

// Get the object implementing OTADownloaderInterface
OTADownloader * GetDownloaderInstance();

} // namespace chip
Loading