blob: 9d983cb8e999bf7a43ceae389ef04c1c7727caea [file] [log] [blame]
/*
* Copyright (c) 2020 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 "ModelCommand.h"
#include <atomic>
#include <chrono>
#include <sstream>
#include <string>
using namespace ::chip;
using namespace ::chip::DeviceController;
constexpr std::chrono::seconds kWaitingForResponseTimeout(10);
namespace {
constexpr uint8_t kZCLGlobalCmdFrameControlHeader = 8;
constexpr uint8_t kZCLClusterCmdFrameControlHeader = 9;
bool isValidFrame(uint8_t frameControl)
{
// Bit 3 of the frame control byte set means direction is server to client.
return (frameControl == kZCLGlobalCmdFrameControlHeader || frameControl == kZCLClusterCmdFrameControlHeader);
}
bool isGlobalCommand(uint8_t frameControl)
{
return (frameControl == kZCLGlobalCmdFrameControlHeader);
}
} // namespace
void ModelCommand::OnConnect(ChipDeviceController * dc)
{
if (SendCommand(dc))
{
UpdateWaitForResponse(true);
WaitForResponse();
}
}
void ModelCommand::OnError(ChipDeviceController * dc, CHIP_ERROR err)
{
UpdateWaitForResponse(false);
}
void ModelCommand::OnMessage(ChipDeviceController * dc, PacketBufferHandle buffer)
{
SetCommandExitStatus(ReceiveCommandResponse(dc, std::move(buffer)));
UpdateWaitForResponse(false);
}
CHIP_ERROR ModelCommand::Run(ChipDeviceController * dc, NodeId remoteId)
{
CHIP_ERROR err = CHIP_NO_ERROR;
err = NetworkCommand::Run(dc, remoteId);
SuccessOrExit(err);
err = dc->ServiceEventSignal();
SuccessOrExit(err);
VerifyOrExit(GetCommandExitStatus(), err = CHIP_ERROR_INTERNAL);
exit:
return err;
}
bool ModelCommand::SendCommand(ChipDeviceController * dc)
{
// Make sure our buffer is big enough, but this will need a better setup!
static const uint16_t bufferSize = 1024;
PacketBufferHandle buffer = PacketBuffer::NewWithAvailableSize(bufferSize);
if (buffer.IsNull())
{
ChipLogError(chipTool, "Failed to allocate memory for packet data.");
return false;
}
ChipLogProgress(chipTool, "Endpoint id: '0x%02x', Cluster id: '0x%04x', Command id: '0x%02x'", mEndPointId, mClusterId,
mCommandId);
uint16_t dataLength = EncodeCommand(buffer, bufferSize, mEndPointId);
if (dataLength == 0)
{
ChipLogError(chipTool, "Error while encoding data for command: %s", GetName());
return false;
}
buffer->SetDataLength(dataLength);
ChipLogDetail(chipTool, "Encoded data of length %d", dataLength);
#ifdef DEBUG
PrintBuffer(buffer);
#endif
dc->SendMessage(NULL, buffer.Release_ForNow());
return true;
}
bool ModelCommand::ReceiveCommandResponse(ChipDeviceController * dc, PacketBufferHandle buffer) const
{
EmberApsFrame frame;
uint8_t * message;
uint16_t messageLen;
uint8_t frameControl;
uint8_t sequenceNumber;
uint8_t commandId;
bool success = false;
if (extractApsFrame(buffer->Start(), buffer->DataLength(), &frame) == 0)
{
ChipLogError(chipTool, "APS frame processing failure!");
ExitNow();
}
ChipLogDetail(chipTool, "APS frame processing success!");
messageLen = extractMessage(buffer->Start(), buffer->DataLength(), &message);
VerifyOrExit(messageLen >= 3, ChipLogError(chipTool, "Unexpected response length: %d", messageLen));
frameControl = chip::Encoding::Read8(message);
sequenceNumber = chip::Encoding::Read8(message);
commandId = chip::Encoding::Read8(message);
messageLen = static_cast<uint16_t>(messageLen - 3);
VerifyOrExit(isValidFrame(frameControl), ChipLogError(chipTool, "Unexpected frame control byte: 0x%02x", frameControl));
VerifyOrExit(sequenceNumber == 1, ChipLogError(chipTool, "Unexpected sequence number: %d", sequenceNumber));
VerifyOrExit(mEndPointId == frame.sourceEndpoint,
ChipLogError(chipTool, "Unexpected endpoint id '0x%02x'", frame.sourceEndpoint));
VerifyOrExit(mClusterId == frame.clusterId, ChipLogError(chipTool, "Unexpected cluster id '0x%04x'", frame.clusterId));
success = isGlobalCommand(frameControl) ? HandleGlobalResponse(commandId, message, messageLen)
: HandleSpecificResponse(commandId, message, messageLen);
exit:
return success;
}
void ModelCommand::UpdateWaitForResponse(bool value)
{
{
std::lock_guard<std::mutex> lk(cvWaitingForResponseMutex);
mWaitingForResponse = value;
}
cvWaitingForResponse.notify_all();
}
void ModelCommand::WaitForResponse()
{
std::unique_lock<std::mutex> lk(cvWaitingForResponseMutex);
auto waitingUntil = std::chrono::system_clock::now() + kWaitingForResponseTimeout;
if (!cvWaitingForResponse.wait_until(lk, waitingUntil, [this]() { return !this->mWaitingForResponse; }))
{
ChipLogError(chipTool, "No response from device");
}
}
void ModelCommand::PrintBuffer(const PacketBufferHandle & buffer) const
{
const size_t data_len = buffer->DataLength();
fprintf(stderr, "SENDING: %zu ", data_len);
for (size_t i = 0; i < data_len; ++i)
{
fprintf(stderr, "%d ", buffer->Start()[i]);
}
fprintf(stderr, "\n");
}