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

Auto-commissioner: support secondary network interface commissioning #33801

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
9 changes: 9 additions & 0 deletions examples/chip-tool/commands/pairing/Commands.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ class PairCodeThread : public PairingCommand
{}
};

class PairCodeWiFiThread : public PairingCommand
{
public:
PairCodeWiFiThread(CredentialIssuerCommands * credsIssuerConfig) :
PairingCommand("code-wifi-thread", PairingMode::Code, PairingNetworkType::WiFiOrThread, credsIssuerConfig)
{}
};

class PairOnNetwork : public PairingCommand
{
public:
Expand Down Expand Up @@ -231,6 +239,7 @@ void registerCommandsPairing(Commands & commands, CredentialIssuerCommands * cre
make_unique<PairCodePase>(credsIssuerConfig),
make_unique<PairCodeWifi>(credsIssuerConfig),
make_unique<PairCodeThread>(credsIssuerConfig),
make_unique<PairCodeWiFiThread>(credsIssuerConfig),
make_unique<PairBleWiFi>(credsIssuerConfig),
make_unique<PairBleThread>(credsIssuerConfig),
make_unique<PairSoftAP>(credsIssuerConfig),
Expand Down
4 changes: 4 additions & 0 deletions examples/chip-tool/commands/pairing/PairingCommand.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ CommissioningParameters PairingCommand::GetCommissioningParameters()
case PairingNetworkType::Thread:
params.SetThreadOperationalDataset(mOperationalDataset);
break;
case PairingNetworkType::WiFiOrThread:
params.SetWiFiCredentials(Controller::WiFiCredentials(mSSID, mPassword));
params.SetThreadOperationalDataset(mOperationalDataset);
break;
case PairingNetworkType::None:
break;
}
Expand Down
6 changes: 6 additions & 0 deletions examples/chip-tool/commands/pairing/PairingCommand.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ enum class PairingNetworkType
None,
WiFi,
Thread,
WiFiOrThread,
};

class PairingCommand : public CHIPCommand,
Expand Down Expand Up @@ -85,6 +86,11 @@ class PairingCommand : public CHIPCommand,
case PairingNetworkType::Thread:
AddArgument("operationalDataset", &mOperationalDataset);
break;
case PairingNetworkType::WiFiOrThread:
AddArgument("ssid", &mSSID);
AddArgument("password", &mPassword);
AddArgument("operationalDataset", &mOperationalDataset);
break;
}

switch (mode)
Expand Down
59 changes: 36 additions & 23 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,19 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag

CommissioningStage AutoCommissioner::GetNextCommissioningStageNetworkSetup(CommissioningStage currentStage, CHIP_ERROR & lastErr)
{
if (IsSecondaryNetworkSupported())
{
if (TryingSecondaryNetwork())
{
// Try secondary network interface.
return mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? CommissioningStage::kThreadNetworkSetup
: CommissioningStage::kWiFiNetworkSetup;
}
// Try primary network interface
return mDeviceCommissioningInfo.network.wifi.endpoint == kRootEndpointId ? CommissioningStage::kWiFiNetworkSetup
: CommissioningStage::kThreadNetworkSetup;
}

if (mParams.GetWiFiCredentials().HasValue() && mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kWiFiNetworkSetup;
Expand Down Expand Up @@ -455,35 +468,15 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
case CommissioningStage::kNeedsNetworkCreds:
return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
case CommissioningStage::kWiFiNetworkSetup:
if (mParams.GetThreadOperationalDataset().HasValue() &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkSetup;
}
else
{
return CommissioningStage::kFailsafeBeforeWiFiEnable;
}
return CommissioningStage::kFailsafeBeforeWiFiEnable;
case CommissioningStage::kThreadNetworkSetup:
if (mParams.GetWiFiCredentials().HasValue() && mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kFailsafeBeforeWiFiEnable;
}
else
{
return CommissioningStage::kFailsafeBeforeThreadEnable;
}
return CommissioningStage::kFailsafeBeforeThreadEnable;
case CommissioningStage::kFailsafeBeforeWiFiEnable:
return CommissioningStage::kWiFiNetworkEnable;
case CommissioningStage::kFailsafeBeforeThreadEnable:
return CommissioningStage::kThreadNetworkEnable;
case CommissioningStage::kWiFiNetworkEnable:
if (mParams.GetThreadOperationalDataset().HasValue() &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkEnable;
}
else if (mParams.GetSkipCommissioningComplete().ValueOr(false))
if (mParams.GetSkipCommissioningComplete().ValueOr(false))
{
SetCASEFailsafeTimerIfNeeded();
return CommissioningStage::kCleanup;
Expand All @@ -502,6 +495,10 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStageInternal(Commissio
return CommissioningStage::kEvictPreviousCaseSessions;
case CommissioningStage::kEvictPreviousCaseSessions:
return CommissioningStage::kFindOperationalForStayActive;
case CommissioningStage::kPrimaryOperationalNetworkFailed:
return CommissioningStage::kDisablePrimaryNetworkInterface;
case CommissioningStage::kDisablePrimaryNetworkInterface:
return GetNextCommissioningStageNetworkSetup(currentStage, lastErr);
case CommissioningStage::kFindOperationalForStayActive:
return CommissioningStage::kICDSendStayActive;
case CommissioningStage::kICDSendStayActive:
Expand Down Expand Up @@ -564,6 +561,8 @@ EndpointId AutoCommissioner::GetEndpoint(const CommissioningStage & stage) const
case CommissioningStage::kThreadNetworkSetup:
case CommissioningStage::kThreadNetworkEnable:
return mDeviceCommissioningInfo.network.thread.endpoint;
case CommissioningStage::kDisablePrimaryNetworkInterface:
return kRootEndpointId;
default:
return kRootEndpointId;
}
Expand Down Expand Up @@ -729,6 +728,16 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
report.stageCompleted = CommissioningStage::kScanNetworks;
}
}

if (err != CHIP_NO_ERROR && IsSecondaryNetworkSupported() && !TryingSecondaryNetwork() &&
completionStatus.failedStage.HasValue() && completionStatus.failedStage.Value() >= kWiFiNetworkSetup &&
completionStatus.failedStage.Value() <= kICDSendStayActive)
{
// Primary network failed, disable primary network interface and try secondary network interface.
TrySecondaryNetwork();
err = CHIP_NO_ERROR;
report.stageCompleted = CommissioningStage::kPrimaryOperationalNetworkFailed;
}
}
else
{
Expand Down Expand Up @@ -847,6 +856,10 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
mOperationalDeviceProxy = report.Get<OperationalNodeFoundData>().operationalProxy;
break;
case CommissioningStage::kCleanup:
if (IsSecondaryNetworkSupported() && TryingSecondaryNetwork())
{
ResetTryingSecondaryNetwork();
}
ReleasePAI();
ReleaseDAC();
mCommissioneeDeviceProxy = nullptr;
Expand Down
16 changes: 16 additions & 0 deletions src/controller/AutoCommissioner.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,22 @@ class AutoCommissioner : public CommissioningDelegate
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId));
};

// Helper function to Determine whether secondary network interface is supported.
// Only true if information is provided for both networks, and the target has endpoint
// for wifi and thread.
bool IsSecondaryNetworkSupported() const
{
return ((mParams.GetSupportsConcurrentConnection().ValueOr(false) && mParams.GetWiFiCredentials().HasValue() &&
mDeviceCommissioningInfo.network.wifi.endpoint != kInvalidEndpointId) &&
mParams.GetThreadOperationalDataset().HasValue() &&
mDeviceCommissioningInfo.network.thread.endpoint != kInvalidEndpointId);
}

void TrySecondaryNetwork() { mTryingSecondaryNetwork = true; }
bool TryingSecondaryNetwork() const { return mTryingSecondaryNetwork; }
void ResetTryingSecondaryNetwork() { mTryingSecondaryNetwork = false; }
bool mTryingSecondaryNetwork = false;

bool mStopCommissioning = false;

DeviceCommissioner * mCommissioner = nullptr;
Expand Down
47 changes: 47 additions & 0 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include <app/server/Dnssd.h>
#include <controller/CurrentFabricRemover.h>
#include <controller/InvokeInteraction.h>
#include <controller/WriteInteraction.h>
#include <credentials/CHIPCert.h>
#include <credentials/DeviceAttestationCredsProvider.h>
#include <crypto/CHIPCryptoPAL.h>
Expand Down Expand Up @@ -1029,6 +1030,12 @@ void DeviceCommissioner::CancelCommissioningInteractions()
mInvokeCancelFn();
mInvokeCancelFn = nullptr;
}
if (mWriteCancelFn)
{
ChipLogDetail(Controller, "Cancelling write request for step '%s'", StageToString(mCommissioningStage));
mWriteCancelFn();
mWriteCancelFn = nullptr;
}
if (mOnDeviceConnectedCallback.IsRegistered())
{
ChipLogDetail(Controller, "Cancelling CASE setup for step '%s'", StageToString(mCommissioningStage));
Expand Down Expand Up @@ -1801,6 +1808,12 @@ void DeviceCommissioner::OnBasicSuccess(void * context, const chip::app::DataMod
commissioner->CommissioningStageComplete(CHIP_NO_ERROR);
}

void DeviceCommissioner::OnInterfaceEnableWriteSuccessResponse(void * context)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
commissioner->CommissioningStageComplete(CHIP_NO_ERROR);
}

void DeviceCommissioner::OnBasicFailure(void * context, CHIP_ERROR error)
{
ChipLogProgress(Controller, "Received failure response %s\n", chip::ErrorStr(error));
Expand Down Expand Up @@ -1972,6 +1985,7 @@ void DeviceCommissioner::CommissioningStageComplete(CHIP_ERROR err, Commissionin
DeviceProxy * proxy = mDeviceBeingCommissioned;
mDeviceBeingCommissioned = nullptr;
mInvokeCancelFn = nullptr;
mWriteCancelFn = nullptr;

if (mPairingDelegate != nullptr)
{
Expand Down Expand Up @@ -2740,6 +2754,20 @@ DeviceCommissioner::SendCommissioningCommand(DeviceProxy * device, const Request
onFailureCb, NullOptional, timeout, (!fireAndForget) ? &mInvokeCancelFn : nullptr);
}

template <typename AttrType>
CHIP_ERROR DeviceCommissioner::SendCommissioningWriteRequest(DeviceProxy * device, EndpointId endpoint, ClusterId cluster,
AttributeId attribute, const AttrType & requestData,
WriteResponseSuccessCallback successCb,
WriteResponseFailureCallback failureCb)
{
VerifyOrDie(!mWriteCancelFn); // we don't make parallel (cancellable) calls
auto onSuccessCb = [this, successCb](const app::ConcreteAttributePath & aPath) { successCb(this); };
auto onFailureCb = [this, failureCb](const app::ConcreteAttributePath * aPath, CHIP_ERROR aError) { failureCb(this, aError); };
return WriteAttribute(device->GetSecureSession().Value(), endpoint, cluster, attribute, requestData, onSuccessCb, onFailureCb,
/* aTimedWriteTimeoutMs = */ NullOptional, /* onDoneCb = */ nullptr, /* aDataVersion = */ NullOptional,
/* outCancelFn = */ &mWriteCancelFn);
}

void DeviceCommissioner::SendCommissioningReadRequest(DeviceProxy * proxy, Optional<System::Clock::Timeout> timeout,
app::AttributePathParams * readPaths, size_t readPathsSize)
{
Expand Down Expand Up @@ -3424,6 +3452,25 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
);
}
break;
case CommissioningStage::kPrimaryOperationalNetworkFailed: {
// nothing to do. This stage indicates that the primary operational network failed and the network interface should be
// disabled later.
break;
}
case CommissioningStage::kDisablePrimaryNetworkInterface: {
NetworkCommissioning::Attributes::InterfaceEnabled::TypeInfo::Type request = false;
CHIP_ERROR err = SendCommissioningWriteRequest(proxy, endpoint, NetworkCommissioning::Id,
NetworkCommissioning::Attributes::InterfaceEnabled::Id, request,
OnInterfaceEnableWriteSuccessResponse, OnBasicFailure);
if (err != CHIP_NO_ERROR)
{
// We won't get any async callbacks here, so just complete our stage.
ChipLogError(Controller, "Failed to send InterfaceEnabled write request: %" CHIP_ERROR_FORMAT, err.Format());
CommissioningStageComplete(err);
return;
}
break;
}
case CommissioningStage::kICDSendStayActive: {
if (!(params.GetICDStayActiveDurationMsec().HasValue()))
{
Expand Down
7 changes: 7 additions & 0 deletions src/controller/CHIPDeviceController.h
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,7 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
CommissioningStage mCommissioningStage = CommissioningStage::kSecurePairing;
bool mRunCommissioningAfterConnection = false;
Internal::InvokeCancelFn mInvokeCancelFn;
Internal::WriteCancelFn mWriteCancelFn;

ObjectPool<CommissioneeDeviceProxy, kNumMaxActiveDevices> mCommissioneeDevicePool;

Expand Down Expand Up @@ -965,6 +966,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
OnICDManagementStayActiveResponse(void * context,
const app::Clusters::IcdManagement::Commands::StayActiveResponse::DecodableType & data);

static void OnInterfaceEnableWriteSuccessResponse(void * context);

/**
* @brief
* This function processes the CSR sent by the device.
Expand Down Expand Up @@ -1025,6 +1028,10 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController,
Optional<System::Clock::Timeout> timeout = NullOptional, bool fireAndForget = false);
void SendCommissioningReadRequest(DeviceProxy * proxy, Optional<System::Clock::Timeout> timeout,
app::AttributePathParams * readPaths, size_t readPathsSize);
template <typename AttrType>
CHIP_ERROR SendCommissioningWriteRequest(DeviceProxy * device, EndpointId endpoint, ClusterId cluster, AttributeId attribute,
const AttrType & requestData, WriteResponseSuccessCallback successCb,
WriteResponseFailureCallback failureCb);
void CancelCommissioningInteractions();
void CancelCASECallbacks();

Expand Down
6 changes: 6 additions & 0 deletions src/controller/CommissioningDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,12 @@ const char * StageToString(CommissioningStage stage)
case kNeedsNetworkCreds:
return "NeedsNetworkCreds";

case kPrimaryOperationalNetworkFailed:
return "PrimaryOperationalNetworkFailed";

case kDisablePrimaryNetworkInterface:
return "DisablePrimaryNetworkInterface";

default:
return "???";
}
Expand Down
3 changes: 3 additions & 0 deletions src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ enum CommissioningStage : uint8_t
/// Call CHIPDeviceController::NetworkCredentialsReady() when CommissioningParameters is populated with
/// network credentials to use in kWiFiNetworkSetup or kThreadNetworkSetup steps.
kNeedsNetworkCreds,
kPrimaryOperationalNetworkFailed, ///< Indicate that the primary operational network (on root endpoint) failed, should disable
///< the primary network interface later.
kDisablePrimaryNetworkInterface, ///< Send InterfaceEnabled write request to the device to disable network interface.
};

enum class ICDRegistrationStrategy : uint8_t
Expand Down
18 changes: 17 additions & 1 deletion src/controller/WriteInteraction.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@
#include <app/WriteClient.h>
#include <controller/CommandSenderAllocator.h>
#include <controller/TypedCommandCallback.h>
#include <functional>
#include <lib/core/Optional.h>

namespace chip {
namespace Controller {

namespace Internal {
// WriteCancelFn functions on WriteAttribute() are for internal use only.
typedef std::function<void()> WriteCancelFn;
} // namespace Internal

/*
* An adapter callback that permits applications to provide std::function callbacks for success, error and on done.
* This permits a slightly more flexible programming model that allows applications to pass in lambdas and bound member functions
Expand Down Expand Up @@ -130,7 +136,8 @@ CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId
AttributeId attributeId, const AttrType & requestData, WriteCallback::OnSuccessCallbackType onSuccessCb,
WriteCallback::OnErrorCallbackType onErrorCb, const Optional<uint16_t> & aTimedWriteTimeoutMs,
WriteCallback::OnDoneCallbackType onDoneCb = nullptr,
const Optional<DataVersion> & aDataVersion = NullOptional)
const Optional<DataVersion> & aDataVersion = NullOptional,
Internal::WriteCancelFn * outCancelFn = nullptr)
{
auto callback = Platform::MakeUnique<WriteCallback>(onSuccessCb, onErrorCb, onDoneCb, sessionHandle->IsGroupSession());
VerifyOrReturnError(callback != nullptr, CHIP_ERROR_NO_MEMORY);
Expand All @@ -151,6 +158,15 @@ CHIP_ERROR WriteAttribute(const SessionHandle & sessionHandle, chip::EndpointId

ReturnErrorOnFailure(client->SendWriteRequest(sessionHandle));

// If requested by the caller, provide a way to cancel the write interaction.
if (outCancelFn != nullptr)
{
*outCancelFn = [rawCallback = callback.get(), rawClient = client.get()]() {
chip::Platform::Delete(rawClient);
chip::Platform::Delete(rawCallback);
};
}

// At this point the handle will ensure our callback's OnDone is always
// called.
client.release();
Expand Down
Loading