Skip to content

Commit

Permalink
Auto-commissioner: support secondary network interface commissioning (p…
Browse files Browse the repository at this point in the history
…roject-chip#33801)

* Auto-commissioner: support secondary network interface commissioning

* Fix code-wifi-thread command and reset trying secondary network flag

* Revert server-side changes

* Added write request cancel function
  • Loading branch information
DejinChen authored Jun 27, 2024
1 parent 720c834 commit d71a363
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 24 deletions.
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 @@ -1028,6 +1029,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 @@ -1800,6 +1807,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 @@ -1971,6 +1984,7 @@ void DeviceCommissioner::CommissioningStageComplete(CHIP_ERROR err, Commissionin
DeviceProxy * proxy = mDeviceBeingCommissioned;
mDeviceBeingCommissioned = nullptr;
mInvokeCancelFn = nullptr;
mWriteCancelFn = nullptr;

if (mPairingDelegate != nullptr)
{
Expand Down Expand Up @@ -2739,6 +2753,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 @@ -3423,6 +3451,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 @@ -830,6 +830,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 @@ -973,6 +974,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

0 comments on commit d71a363

Please sign in to comment.