Skip to content

Commit

Permalink
Merge branch 'master' into feature/chiptool-lowermerge
Browse files Browse the repository at this point in the history
  • Loading branch information
openhacker authored and Marty Leisner committed Oct 15, 2021
2 parents 405f615 + 2377fea commit 788391a
Show file tree
Hide file tree
Showing 31 changed files with 517 additions and 449 deletions.
2 changes: 2 additions & 0 deletions examples/chip-tool/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ config("config") {
static_library("chip-tool-utils") {
sources = [
"commands/clusters/ModelCommand.cpp",
"commands/common/CHIPCommand.cpp",
"commands/common/CHIPCommand.h",
"commands/common/Command.cpp",
"commands/common/Commands.cpp",
"commands/discover/DiscoverCommand.cpp",
Expand Down
8 changes: 3 additions & 5 deletions examples/chip-tool/commands/clusters/ModelCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,14 @@

using namespace ::chip;

CHIP_ERROR ModelCommand::Run()
CHIP_ERROR ModelCommand::Run(NodeId remoteId)
{
CHIP_ERROR err = CHIP_NO_ERROR;

auto * ctx = GetExecContext();

err = ctx->commissioner->GetConnectedDevice(ctx->remoteId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
err = mController.GetConnectedDevice(remoteId, &mOnDeviceConnectedCallback, &mOnDeviceConnectionFailureCallback);
VerifyOrExit(err == CHIP_NO_ERROR,
ChipLogError(chipTool, "Failed in initiating connection to the device: %" PRIu64 ", error %" CHIP_ERROR_FORMAT,
ctx->remoteId, err.Format()));
remoteId, err.Format()));

exit:
return err;
Expand Down
14 changes: 8 additions & 6 deletions examples/chip-tool/commands/clusters/ModelCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
#pragma once

#include "../../config/PersistentStorage.h"
#include "../common/Command.h"
#include "../common/CHIPCommand.h"
#include <app/chip-zcl-zpro-codec.h>
#include <controller/ExampleOperationalCredentialsIssuer.h>
#include <lib/core/CHIPEncoding.h>
Expand All @@ -28,26 +28,28 @@
#define CHIP_ZCL_ENDPOINT_MIN 0x00
#define CHIP_ZCL_ENDPOINT_MAX 0xF0

class ModelCommand : public Command
class ModelCommand : public CHIPCommand
{
public:
using ChipDevice = ::chip::Controller::Device;

ModelCommand(const char * commandName) :
Command(commandName), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this),
CHIPCommand(commandName), mOnDeviceConnectedCallback(OnDeviceConnectedFn, this),
mOnDeviceConnectionFailureCallback(OnDeviceConnectionFailureFn, this)
{}

void AddArguments() { AddArgument("endpoint-id", CHIP_ZCL_ENDPOINT_MIN, CHIP_ZCL_ENDPOINT_MAX, &mEndPointId); }

/////////// Command Interface /////////
CHIP_ERROR Run() override;
/////////// CHIPCommand Interface /////////
CHIP_ERROR Run(NodeId remoteId) override;
uint16_t GetWaitDurationInSeconds() const override { return 10; }

virtual CHIP_ERROR SendCommand(ChipDevice * device, uint8_t endPointId) = 0;

private:
uint8_t mEndPointId;

static void OnDeviceConnectedFn(void * context, chip::Controller::Device * device);
static void OnDeviceConnectedFn(void * context, ChipDevice * device);
static void OnDeviceConnectionFailureFn(void * context, NodeId deviceId, CHIP_ERROR error);

chip::Callback::Callback<chip::Controller::OnDeviceConnected> mOnDeviceConnectedCallback;
Expand Down
144 changes: 144 additions & 0 deletions examples/chip-tool/commands/common/CHIPCommand.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* 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 "CHIPCommand.h"

#include <controller/CHIPDeviceControllerFactory.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <credentials/DeviceAttestationVerifier.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <credentials/examples/DeviceAttestationVerifierExample.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/ScopedBuffer.h>

using DeviceControllerFactory = chip::Controller::DeviceControllerFactory;

CHIP_ERROR CHIPCommand::Run()
{
#if CHIP_DEVICE_LAYER_TARGET_LINUX && CHIP_DEVICE_CONFIG_ENABLE_CHIPOBLE
// By default, Linux device is configured as a BLE peripheral while the controller needs a BLE central.
ReturnLogErrorOnFailure(chip::DeviceLayer::Internal::BLEMgrImpl().ConfigureBle(0, true));
#endif

ReturnLogErrorOnFailure(mStorage.Init());
ReturnLogErrorOnFailure(mOpCredsIssuer.Initialize(mStorage));

chip::Platform::ScopedMemoryBuffer<uint8_t> noc;
chip::Platform::ScopedMemoryBuffer<uint8_t> icac;
chip::Platform::ScopedMemoryBuffer<uint8_t> rcac;

chip::Credentials::SetDeviceAttestationCredentialsProvider(chip::Credentials::Examples::GetExampleDACProvider());
chip::Credentials::SetDeviceAttestationVerifier(chip::Credentials::Examples::GetExampleDACVerifier());

VerifyOrReturnError(noc.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(icac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);
VerifyOrReturnError(rcac.Alloc(chip::Controller::kMaxCHIPDERCertLength), CHIP_ERROR_NO_MEMORY);

chip::MutableByteSpan nocSpan(noc.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan icacSpan(icac.Get(), chip::Controller::kMaxCHIPDERCertLength);
chip::MutableByteSpan rcacSpan(rcac.Get(), chip::Controller::kMaxCHIPDERCertLength);

chip::Crypto::P256Keypair ephemeralKey;
ReturnLogErrorOnFailure(ephemeralKey.Initialize());

// TODO - OpCreds should only be generated for pairing command
// store the credentials in persistent storage, and
// generate when not available in the storage.
ReturnLogErrorOnFailure(mOpCredsIssuer.GenerateNOCChainAfterValidation(mStorage.GetLocalNodeId(), 0, ephemeralKey.Pubkey(),
rcacSpan, icacSpan, nocSpan));

chip::Controller::FactoryInitParams factoryInitParams;
factoryInitParams.storageDelegate = &mStorage;
factoryInitParams.listenPort = mStorage.GetListenPort();

chip::Controller::SetupParams commissionerParams;
commissionerParams.operationalCredentialsDelegate = &mOpCredsIssuer;
commissionerParams.ephemeralKeypair = &ephemeralKey;
commissionerParams.controllerRCAC = rcacSpan;
commissionerParams.controllerICAC = icacSpan;
commissionerParams.controllerNOC = nocSpan;

ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().Init(factoryInitParams));
ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().SetupCommissioner(commissionerParams, mController));

chip::DeviceLayer::PlatformMgr().ScheduleWork(RunQueuedCommand, reinterpret_cast<intptr_t>(this));
ReturnLogErrorOnFailure(StartWaiting(GetWaitDurationInSeconds()));

Shutdown();

//
// We can call DeviceController::Shutdown() safely without grabbing the stack lock
// since the CHIP thread and event queue have been stopped, preventing any thread
// races.
//
ReturnLogErrorOnFailure(mController.Shutdown());

return CHIP_NO_ERROR;
}

void CHIPCommand::RunQueuedCommand(intptr_t commandArg)
{
auto * command = reinterpret_cast<CHIPCommand *>(commandArg);
CHIP_ERROR err = command->Run(command->mStorage.GetRemoteNodeId());
if (err != CHIP_NO_ERROR)
{
command->SetCommandExitStatus(err);
}
}

#if !CONFIG_USE_SEPARATE_EVENTLOOP
static void OnResponseTimeout(chip::System::Layer *, void * appState)
{
(reinterpret_cast<CHIPCommand *>(appState))->SetCommandExitStatus(CHIP_ERROR_TIMEOUT);
}
#endif // !CONFIG_USE_SEPARATE_EVENTLOOP

CHIP_ERROR CHIPCommand::StartWaiting(uint16_t seconds)
{
#if CONFIG_USE_SEPARATE_EVENTLOOP
// ServiceEvents() calls StartEventLoopTask(), which is paired with the StopEventLoopTask() below.
ReturnLogErrorOnFailure(DeviceControllerFactory::GetInstance().ServiceEvents());
auto waitingUntil = std::chrono::system_clock::now() + std::chrono::seconds(seconds);
{
std::unique_lock<std::mutex> lk(cvWaitingForResponseMutex);
if (!cvWaitingForResponse.wait_until(lk, waitingUntil, [this]() { return !this->mWaitingForResponse; }))
{
mCommandExitStatus = CHIP_ERROR_TIMEOUT;
}
}
LogErrorOnFailure(chip::DeviceLayer::PlatformMgr().StopEventLoopTask());
#else
ReturnLogErrorOnFailure(chip::DeviceLayer::SystemLayer().StartTimer(seconds * 1000, OnResponseTimeout, this));
chip::DeviceLayer::PlatformMgr().RunEventLoop();
#endif // CONFIG_USE_SEPARATE_EVENTLOOP

return mCommandExitStatus;
}

void CHIPCommand::StopWaiting()
{
#if CONFIG_USE_SEPARATE_EVENTLOOP
{
std::lock_guard<std::mutex> lk(cvWaitingForResponseMutex);
mWaitingForResponse = false;
}
cvWaitingForResponse.notify_all();
#else // CONFIG_USE_SEPARATE_EVENTLOOP
LogErrorOnFailure(chip::DeviceLayer::PlatformMgr().StopEventLoopTask());
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
}
83 changes: 83 additions & 0 deletions examples/chip-tool/commands/common/CHIPCommand.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* 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.
*
*/

#pragma once

#include "../../config/PersistentStorage.h"
#include "Command.h"

#pragma once

class PersistentStorage;

class CHIPCommand : public Command
{
public:
using ChipDevice = ::chip::Controller::Device;
using ChipDeviceCommissioner = ::chip::Controller::DeviceCommissioner;
using ChipDeviceController = ::chip::Controller::DeviceController;
using ChipSerializedDevice = ::chip::Controller::SerializedDevice;
using IPAddress = ::chip::Inet::IPAddress;
using NodeId = ::chip::NodeId;
using PeerAddress = ::chip::Transport::PeerAddress;

CHIPCommand(const char * commandName) : Command(commandName) {}

/////////// Command Interface /////////
CHIP_ERROR Run() override;

void SetCommandExitStatus(CHIP_ERROR status)
{
mCommandExitStatus = status;
StopWaiting();
}

protected:
// Will be called in a setting in which it's safe to touch the CHIP
// stack. The rules for Run() are as follows:
//
// 1) If error is returned, Run() must not call SetCommandExitStatus.
// 2) If success is returned Run() must either have called
// SetCommandExitStatus() or scheduled async work that will do that.
virtual CHIP_ERROR Run(NodeId remoteId) = 0;

// Get the wait duration, in seconds, before the command times out.
virtual uint16_t GetWaitDurationInSeconds() const = 0;

// Shut down the command, in case any work needs to be done after the event
// loop has been stopped.
virtual void Shutdown() {}

ChipDeviceCommissioner mController;
PersistentStorage mStorage;

private:
static void RunQueuedCommand(intptr_t commandArg);

CHIP_ERROR mCommandExitStatus = CHIP_ERROR_INTERNAL;
chip::Controller::ExampleOperationalCredentialsIssuer mOpCredsIssuer;

CHIP_ERROR StartWaiting(uint16_t seconds);
void StopWaiting();

#if CONFIG_USE_SEPARATE_EVENTLOOP
std::condition_variable cvWaitingForResponse;
std::mutex cvWaitingForResponseMutex;
bool mWaitingForResponse{ true };
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
};
75 changes: 19 additions & 56 deletions examples/chip-tool/commands/common/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,20 @@ bool Command::InitArgument(size_t argIndex, char * argValue)
break;
}

case ArgumentType::CharString: {
case ArgumentType::String: {
const char ** value = reinterpret_cast<const char **>(arg.value);
*value = argValue;
isValidArgument = true;
break;
}

case ArgumentType::CharString: {
auto * value = static_cast<chip::Span<const char> *>(arg.value);
*value = chip::Span<const char>(argValue, strlen(argValue));
isValidArgument = true;
break;
}

case ArgumentType::OctetString: {
auto * value = static_cast<chip::ByteSpan *>(arg.value);
// We support two ways to pass an octet string argument. If it happens
Expand Down Expand Up @@ -314,6 +321,17 @@ size_t Command::AddArgument(const char * name, char ** value)
return mArgs.size();
}

size_t Command::AddArgument(const char * name, chip::CharSpan * value)
{
Argument arg;
arg.type = ArgumentType::CharString;
arg.name = name;
arg.value = reinterpret_cast<void *>(value);

mArgs.emplace_back(arg);
return mArgs.size();
}

size_t Command::AddArgument(const char * name, chip::ByteSpan * value)
{
Argument arg;
Expand Down Expand Up @@ -386,58 +404,3 @@ const char * Command::GetAttribute(void) const

return nullptr;
}

void Command::UpdateWaitForResponse(bool value)
{
#if CONFIG_USE_SEPARATE_EVENTLOOP
{
std::lock_guard<std::mutex> lk(cvWaitingForResponseMutex);
mWaitingForResponse = value;
}
cvWaitingForResponse.notify_all();
#else // CONFIG_USE_SEPARATE_EVENTLOOP
if (value == false)
{
if (mCommandExitStatus != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Run command failure: %s", chip::ErrorStr(mCommandExitStatus));
}

chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
}
#endif // CONFIG_USE_SEPARATE_EVENTLOOP
}

#if CONFIG_USE_SEPARATE_EVENTLOOP

void Command::WaitForResponse(uint16_t seconds)
{
std::chrono::seconds waitingForResponseTimeout(seconds);
std::unique_lock<std::mutex> lk(cvWaitingForResponseMutex);
auto waitingUntil = std::chrono::system_clock::now() + waitingForResponseTimeout;
if (!cvWaitingForResponse.wait_until(lk, waitingUntil, [this]() { return !this->mWaitingForResponse; }))
{
ChipLogError(chipTool, "No response from device");
}
}

#else // CONFIG_USE_SEPARATE_EVENTLOOP

static void OnResponseTimeout(chip::System::Layer *, void *)
{
ChipLogError(chipTool, "No response from device");

chip::DeviceLayer::PlatformMgr().StopEventLoopTask();
}

CHIP_ERROR Command::ScheduleWaitForResponse(uint16_t seconds)
{
CHIP_ERROR err = chip::DeviceLayer::SystemLayer().StartTimer(seconds * 1000, OnResponseTimeout, this);
if (err != CHIP_NO_ERROR)
{
ChipLogError(chipTool, "Failed to allocate timer %" CHIP_ERROR_FORMAT, err.Format());
}
return err;
}

#endif // CONFIG_USE_SEPARATE_EVENTLOOP
Loading

0 comments on commit 788391a

Please sign in to comment.