blob: 1a7cd315b6172438e812a0ae7228cfd422c0257a [file] [log] [blame]
/*
*
* Copyright (c) 2021 Project CHIP Authors
*
* 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 <ota-provider-common/BdxOtaSender.h>
#include <lib/core/CHIPError.h>
#include <lib/support/BitFlags.h>
#include <lib/support/CHIPMemString.h>
#include <messaging/ExchangeContext.h>
#include <messaging/Flags.h>
#include <protocols/bdx/BdxTransferSession.h>
#include <fstream>
using chip::bdx::StatusCode;
using chip::bdx::TransferControlFlags;
using chip::bdx::TransferSession;
BdxOtaSender::BdxOtaSender()
{
memset(mFilepath, 0, kFilepathMaxLength);
}
void BdxOtaSender::SetFilepath(const char * path)
{
if (path != nullptr)
{
chip::Platform::CopyString(mFilepath, path);
}
else
{
memset(mFilepath, 0, kFilepathMaxLength);
}
}
void BdxOtaSender::HandleTransferSessionOutput(TransferSession::OutputEvent & event)
{
CHIP_ERROR err = CHIP_NO_ERROR;
if (event.EventType != TransferSession::OutputEventType::kNone)
{
ChipLogDetail(BDX, "OutputEvent type: %s", event.ToString(event.EventType));
}
switch (event.EventType)
{
case TransferSession::OutputEventType::kNone:
break;
case TransferSession::OutputEventType::kMsgToSend: {
chip::Messaging::SendFlags sendFlags;
if (!event.msgTypeData.HasMessageType(chip::Protocols::SecureChannel::MsgType::StatusReport))
{
// All messages sent from the Sender expect a response, except for a StatusReport which would indicate an error and the
// end of the transfer.
sendFlags.Set(chip::Messaging::SendMessageFlags::kExpectResponse);
}
VerifyOrReturn(mExchangeCtx != nullptr, ChipLogError(BDX, "%s: mExchangeCtx is null", __FUNCTION__));
err = mExchangeCtx->SendMessage(event.msgTypeData.ProtocolId, event.msgTypeData.MessageType, std::move(event.MsgData),
sendFlags);
if (err != CHIP_NO_ERROR)
{
ChipLogError(BDX, "SendMessage failed: %s", chip::ErrorStr(err));
}
break;
}
case TransferSession::OutputEventType::kInitReceived: {
// TransferSession will automatically reject a transfer if there are no
// common supported control modes. It will also default to the smaller
// block size.
TransferSession::TransferAcceptData acceptData;
acceptData.ControlMode = TransferControlFlags::kReceiverDrive; // OTA must use receiver drive
acceptData.MaxBlockSize = mTransfer.GetTransferBlockSize();
acceptData.StartOffset = mTransfer.GetStartOffset();
acceptData.Length = mTransfer.GetTransferLength();
VerifyOrReturn(mTransfer.AcceptTransfer(acceptData) == CHIP_NO_ERROR,
ChipLogError(BDX, "%s: %s", __FUNCTION__, chip::ErrorStr(err)));
break;
}
case TransferSession::OutputEventType::kQueryReceived: {
TransferSession::BlockData blockData;
uint16_t blockSize = mTransfer.GetTransferBlockSize();
uint16_t bytesToRead = blockSize;
// TODO: This should be a utility function in TransferSession
if (mTransfer.GetTransferLength() > 0 && mNumBytesSent + blockSize > mTransfer.GetTransferLength())
{
// cast should be safe because of condition above
bytesToRead = static_cast<uint16_t>(mTransfer.GetTransferLength() - mNumBytesSent);
}
chip::System::PacketBufferHandle blockBuf = chip::System::PacketBufferHandle::New(bytesToRead);
if (blockBuf.IsNull())
{
// TODO: AbortTransfer() needs to support GeneralStatusCode failures as well as BDX specific errors.
mTransfer.AbortTransfer(StatusCode::kUnknown);
return;
}
std::ifstream otaFile(mFilepath, std::ifstream::in);
VerifyOrReturn(otaFile.good(), ChipLogError(BDX, "%s: file read failed", __FUNCTION__));
otaFile.seekg(mNumBytesSent);
otaFile.read(reinterpret_cast<char *>(blockBuf->Start()), bytesToRead);
VerifyOrReturn(otaFile.good() || otaFile.eof(), ChipLogError(BDX, "%s: file read failed", __FUNCTION__));
blockData.Data = blockBuf->Start();
blockData.Length = static_cast<size_t>(otaFile.gcount());
blockData.IsEof = (blockData.Length < blockSize) ||
(mNumBytesSent + static_cast<uint64_t>(blockData.Length) == mTransfer.GetTransferLength() || (otaFile.peek() == EOF));
mNumBytesSent = static_cast<uint32_t>(mNumBytesSent + blockData.Length);
otaFile.close();
VerifyOrReturn(CHIP_NO_ERROR == mTransfer.PrepareBlock(blockData),
ChipLogError(BDX, "%s: PrepareBlock failed: %s", __FUNCTION__, chip::ErrorStr(err)));
break;
}
case TransferSession::OutputEventType::kAckReceived:
break;
case TransferSession::OutputEventType::kAckEOFReceived:
ChipLogDetail(BDX, "Transfer completed, got AckEOF");
Reset();
break;
case TransferSession::OutputEventType::kStatusReceived:
ChipLogError(BDX, "Got StatusReport %x", static_cast<uint16_t>(event.statusData.statusCode));
Reset();
break;
case TransferSession::OutputEventType::kInternalError:
ChipLogError(BDX, "InternalError");
Reset();
break;
case TransferSession::OutputEventType::kTransferTimeout:
ChipLogError(BDX, "Transfer timed out");
Reset();
break;
case TransferSession::OutputEventType::kAcceptReceived:
case TransferSession::OutputEventType::kBlockReceived:
default:
// TransferSession should prevent this case from happening.
ChipLogError(BDX, "%s: unsupported event type", __FUNCTION__);
}
}
void BdxOtaSender::Reset()
{
mTransfer.Reset();
if (mExchangeCtx != nullptr)
{
mExchangeCtx->Close();
}
mNumBytesSent = 0;
memset(mFilepath, 0, kFilepathMaxLength);
}