Skip to content

Commit

Permalink
Query network cluster using wildcard reads (#14567)
Browse files Browse the repository at this point in the history
* Read parts list from descriptor cluster

* Add step to read server clusters from descriptor.

* Read basic cluster information during commissioning.

* Pass back status from iter.

* sequence network cluster existance and feature checks.

If we check the feature map right after we check for the existance
of the networking cluster, we can just bail out once we find a
networking cluster that matches

Test: on-network with lighting app on linux
      wifi on all-clusters app on M5
      tested thread using all clusters app - saw reads of endpoints,
      found network cluster on different endpoint.

* Restyled by clang-format

* Use multi-endpoint wildcard to simplify commissioning

* Restyled by clang-format

* Update src/controller/AutoCommissioner.cpp

Co-authored-by: Michael Sandstedt <michael.sandstedt@gmail.com>

* Update src/controller/CHIPDeviceController.cpp

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Update src/controller/CHIPDeviceController.cpp

Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>

* Restyled by clang-format

* Address review comments.

Co-authored-by: Restyled.io <commits@restyled.io>
Co-authored-by: Michael Sandstedt <michael.sandstedt@gmail.com>
Co-authored-by: Boris Zbarsky <bzbarsky@apple.com>
  • Loading branch information
4 people authored Feb 3, 2022
1 parent ed1276e commit 1ee9366
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 62 deletions.
1 change: 1 addition & 0 deletions src/app/AttributeCache.h
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ class AttributeCache : protected ReadClient::Callback
}
}
}
return CHIP_NO_ERROR;
}

/*
Expand Down
79 changes: 46 additions & 33 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,20 +102,27 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
{
return CommissioningStage::kCleanup;
}

switch (currentStage)
{
case CommissioningStage::kSecurePairing:
return CommissioningStage::kArmFailsafe;
case CommissioningStage::kArmFailsafe:
return CommissioningStage::kReadVendorId;
case CommissioningStage::kReadVendorId:
return CommissioningStage::kReadProductId;
case CommissioningStage::kReadProductId:
return CommissioningStage::kReadSoftwareVersion;
case CommissioningStage::kReadSoftwareVersion:
if (mNeedsNetworkSetup)
{
return CommissioningStage::kGetNetworkTechnology;
}
else
{
return CommissioningStage::kConfigRegulatory;
return CommissioningStage::kArmFailsafe;
}
case CommissioningStage::kGetNetworkTechnology:
return CommissioningStage::kArmFailsafe;
case CommissioningStage::kArmFailsafe:
return CommissioningStage::kConfigRegulatory;
case CommissioningStage::kConfigRegulatory:
return CommissioningStage::kSendPAICertificateRequest;
Expand All @@ -139,15 +146,11 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
// operational network because the provisioning of certificates will trigger the device to start operational advertising.
if (mNeedsNetworkSetup)
{
if (mParams.GetWiFiCredentials().HasValue() &&
mNetworkTechnology.Has(
chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kWiFiNetworkInterface))
if (mParams.GetWiFiCredentials().HasValue() && mNetworkEndpoints.wifi != kInvalidEndpointId)
{
return CommissioningStage::kWiFiNetworkSetup;
}
else if (mParams.GetThreadOperationalDataset().HasValue() &&
mNetworkTechnology.Has(
chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kThreadNetworkInterface))
else if (mParams.GetThreadOperationalDataset().HasValue() && mNetworkEndpoints.thread != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkSetup;
}
Expand All @@ -168,8 +171,7 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
#endif
}
case CommissioningStage::kWiFiNetworkSetup:
if (mParams.GetThreadOperationalDataset().HasValue() &&
mNetworkTechnology.Has(chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kThreadNetworkInterface))
if (mParams.GetThreadOperationalDataset().HasValue() && mNetworkEndpoints.thread != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkSetup;
}
Expand All @@ -178,8 +180,7 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
return CommissioningStage::kWiFiNetworkEnable;
}
case CommissioningStage::kThreadNetworkSetup:
if (mParams.GetWiFiCredentials().HasValue() &&
mNetworkTechnology.Has(chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kWiFiNetworkInterface))
if (mParams.GetWiFiCredentials().HasValue() && mNetworkEndpoints.wifi != kInvalidEndpointId)
{
return CommissioningStage::kWiFiNetworkEnable;
}
Expand All @@ -189,8 +190,7 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
}

case CommissioningStage::kWiFiNetworkEnable:
if (mParams.GetThreadOperationalDataset().HasValue() &&
mNetworkTechnology.Has(chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kThreadNetworkInterface))
if (mParams.GetThreadOperationalDataset().HasValue() && mNetworkEndpoints.thread != kInvalidEndpointId)
{
return CommissioningStage::kThreadNetworkEnable;
}
Expand Down Expand Up @@ -221,6 +221,23 @@ CommissioningStage AutoCommissioner::GetNextCommissioningStage(CommissioningStag
return CommissioningStage::kError;
}

EndpointId AutoCommissioner::GetEndpoint(const CommissioningStage & stage)
{
switch (stage)
{
case CommissioningStage::kWiFiNetworkSetup:
case CommissioningStage::kWiFiNetworkEnable:
return mNetworkEndpoints.wifi;
case CommissioningStage::kThreadNetworkSetup:
case CommissioningStage::kThreadNetworkEnable:
return mNetworkEndpoints.thread;
case CommissioningStage::kGetNetworkTechnology:
return kInvalidEndpointId;
default:
return kRootEndpointId;
}
}

CHIP_ERROR AutoCommissioner::StartCommissioning(DeviceCommissioner * commissioner, CommissioneeDeviceProxy * proxy)
{
// TODO: check that there is no commissioning in progress currently.
Expand All @@ -242,7 +259,8 @@ CHIP_ERROR AutoCommissioner::StartCommissioning(DeviceCommissioner * commissione
Transport::Type::kBle;
CHIP_ERROR err = CHIP_NO_ERROR;
CommissioningStage nextStage = GetNextCommissioningStage(CommissioningStage::kSecurePairing, err);
mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, 0, GetCommandTimeout(nextStage));
mCommissioner->PerformCommissioningStep(mCommissioneeDeviceProxy, nextStage, mParams, this, GetEndpoint(nextStage),
GetCommandTimeout(nextStage));
return CHIP_NO_ERROR;
}

Expand Down Expand Up @@ -297,23 +315,17 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
{
switch (report.stageCompleted)
{
case CommissioningStage::kReadVendorId:
mVendorId = report.Get<BasicVendor>().vendorId;
break;
case CommissioningStage::kReadProductId:
mProductId = report.Get<BasicProduct>().productId;
break;
case CommissioningStage::kReadSoftwareVersion:
mSoftwareVersion = report.Get<BasicSoftware>().softwareVersion;
break;
case CommissioningStage::kGetNetworkTechnology:
mNetworkTechnology.SetRaw(report.Get<FeatureMap>().features);
// Only one of these features can be set at a time.
if (!mNetworkTechnology.HasOnly(
chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kWiFiNetworkInterface) &&
!mNetworkTechnology.HasOnly(
chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kThreadNetworkInterface) &&
mNetworkTechnology.HasOnly(
chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kEthernetNetworkInterface))
{
ChipLogError(
Controller,
"Network Commissioning cluster is malformed - more than one network technology is specified (0x%" PRIX32 ")",
report.Get<FeatureMap>().features);
err = CHIP_ERROR_INTEGRITY_CHECK_FAILED;
break;
}
mNetworkEndpoints = report.Get<NetworkClusters>();
break;
case CommissioningStage::kSendPAICertificateRequest:
SetPAI(report.Get<RequestedCertificate>().certificate);
Expand Down Expand Up @@ -355,6 +367,7 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
mCommissioneeDeviceProxy = nullptr;
mOperationalDeviceProxy = nullptr;
mParams = CommissioningParameters();
mNetworkEndpoints = NetworkClusters();
return CHIP_NO_ERROR;
default:
break;
Expand Down Expand Up @@ -382,7 +395,7 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio

mParams.SetCompletionStatus(err);
// TODO: Get real endpoint
mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this, 0, GetCommandTimeout(nextStage));
mCommissioner->PerformCommissioningStep(proxy, nextStage, mParams, this, GetEndpoint(nextStage), GetCommandTimeout(nextStage));
return CHIP_NO_ERROR;
}

Expand Down
9 changes: 6 additions & 3 deletions src/controller/AutoCommissioner.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,19 +50,23 @@ class AutoCommissioner : public CommissioningDelegate

CHIP_ERROR NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac, AesCcm128KeySpan ipk, NodeId adminSubject);
Optional<System::Clock::Timeout> GetCommandTimeout(CommissioningStage stage);
EndpointId GetEndpoint(const CommissioningStage & stage);

DeviceCommissioner * mCommissioner = nullptr;
CommissioneeDeviceProxy * mCommissioneeDeviceProxy = nullptr;
OperationalDeviceProxy * mOperationalDeviceProxy = nullptr;
OperationalCredentialsDelegate * mOperationalCredentialsDelegate = nullptr;
CommissioningParameters mParams = CommissioningParameters();
VendorId mVendorId;
uint16_t mProductId;
uint32_t mSoftwareVersion;
// Memory space for the commisisoning parameters that come in as ByteSpans - the caller is not guaranteed to retain this memory
uint8_t mSsid[CommissioningParameters::kMaxSsidLen];
uint8_t mCredentials[CommissioningParameters::kMaxCredentialsLen];
uint8_t mThreadOperationalDataset[CommissioningParameters::kMaxThreadDatasetLen];
// TODO: if the device library adds a network commissioning device type, this will need to be 1 per endpoint.
BitFlags<chip::app::Clusters::NetworkCommissioning::NetworkCommissioningFeature> mNetworkTechnology;

bool mNeedsNetworkSetup = false;
NetworkClusters mNetworkEndpoints;

// TODO: Why were the nonces statically allocated, but the certs dynamically allocated?
uint8_t * mDAC = nullptr;
Expand All @@ -74,6 +78,5 @@ class AutoCommissioner : public CommissioningDelegate
uint8_t mNOCertBuffer[Credentials::kMaxCHIPCertLength];
uint8_t mICACertBuffer[Credentials::kMaxCHIPCertLength];
};

} // namespace Controller
} // namespace chip
140 changes: 129 additions & 11 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include <controller/CHIPDeviceController.h>

#include <app-common/zap-generated/enums.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <controller-clusters/zap-generated/CHIPClusters.h>

#if CONFIG_DEVICE_LAYER
Expand Down Expand Up @@ -442,7 +444,7 @@ CHIP_ERROR DeviceController::ComputePASEVerifier(uint32_t iterations, uint32_t s

CHIP_ERROR DeviceController::OpenCommissioningWindowWithCallback(NodeId deviceId, uint16_t timeout, uint16_t iteration,
uint16_t discriminator, uint8_t option,
Callback::Callback<OnOpenCommissioningWindow> * callback,
chip::Callback::Callback<OnOpenCommissioningWindow> * callback,
bool readVIDPIDAttributes)
{
mSetupPayload = SetupPayload();
Expand Down Expand Up @@ -781,8 +783,8 @@ CHIP_ERROR DeviceCommissioner::GetDeviceBeingCommissioned(NodeId deviceId, Commi
return CHIP_NO_ERROR;
}

CHIP_ERROR DeviceCommissioner::GetConnectedDevice(NodeId deviceId, Callback::Callback<OnDeviceConnected> * onConnection,
Callback::Callback<OnDeviceConnectionFailure> * onFailure)
CHIP_ERROR DeviceCommissioner::GetConnectedDevice(NodeId deviceId, chip::Callback::Callback<OnDeviceConnected> * onConnection,
chip::Callback::Callback<OnDeviceConnectionFailure> * onFailure)
{
return DeviceController::GetConnectedDevice(deviceId, onConnection, onFailure);
}
Expand Down Expand Up @@ -1582,15 +1584,31 @@ void DeviceCommissioner::OnDeviceConnectionFailureFn(void * context, PeerId peer
void DeviceCommissioner::SetupCluster(ClusterBase & base, DeviceProxy * proxy, EndpointId endpoint,
Optional<System::Clock::Timeout> timeout)
{
base.Associate(proxy, 0);
base.Associate(proxy, endpoint);
base.SetCommandTimeout(timeout);
}

void OnFeatureMapSuccess(void * context, uint32_t value)
void BasicVendorCallback(void * context, VendorId vendorId)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
CommissioningDelegate::CommissioningReport report;
report.Set<FeatureMap>(value);
report.Set<BasicVendor>(vendorId);
commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report);
}

void BasicProductCallback(void * context, uint16_t productId)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
CommissioningDelegate::CommissioningReport report;
report.Set<BasicProduct>(productId);
commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report);
}

void BasicSoftwareCallback(void * context, uint32_t softwareVersion)
{
DeviceCommissioner * commissioner = static_cast<DeviceCommissioner *>(context);
CommissioningDelegate::CommissioningReport report;
report.Set<BasicSoftware>(softwareVersion);
commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report);
}

Expand All @@ -1600,6 +1618,64 @@ void AttributeReadFailure(void * context, CHIP_ERROR status)
commissioner->CommissioningStageComplete(status);
}

// AttributeCache::Callback impl
void DeviceCommissioner::OnDone()
{
NetworkClusters clusters;

CHIP_ERROR err = mAttributeCache->ForEachAttribute(
app::Clusters::NetworkCommissioning::Id, [this, &clusters](const app::ConcreteAttributePath & path) {
if (path.mAttributeId != app::Clusters::NetworkCommissioning::Attributes::FeatureMap::Id)
{
return CHIP_NO_ERROR;
}
TLV::TLVReader reader;
if (this->mAttributeCache->Get(path, reader) == CHIP_NO_ERROR)
{
BitFlags<app::Clusters::NetworkCommissioning::NetworkCommissioningFeature> features;
if (app::DataModel::Decode(reader, features) == CHIP_NO_ERROR)
{
if (features.Has(app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kWiFiNetworkInterface))
{
clusters.wifi = path.mEndpointId;
}
else if (features.Has(
app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kThreadNetworkInterface))
{
clusters.thread = path.mEndpointId;
}
else if (features.Has(
app::Clusters::NetworkCommissioning::NetworkCommissioningFeature::kEthernetNetworkInterface))
{
clusters.eth = path.mEndpointId;
}
else
{
// TODO: Gross workaround for the empty feature map on all clusters. Remove.
if (clusters.thread == kInvalidEndpointId)
{
clusters.thread = path.mEndpointId;
}
if (clusters.wifi == kInvalidEndpointId)
{
clusters.wifi = path.mEndpointId;
}
}
}
}
return CHIP_NO_ERROR;
});

if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Error parsing Network commissioning features");
}
mAttributeCache = nullptr;
mReadClient = nullptr;
CommissioningDelegate::CommissioningReport report;
report.Set<NetworkClusters>(clusters);
CommissioningStageComplete(err, report);
}
void DeviceCommissioner::OnArmFailSafe(void * context,
const GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data)
{
Expand Down Expand Up @@ -1663,6 +1739,30 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio

switch (step)
{
case CommissioningStage::kReadVendorId: {
ChipLogProgress(Controller, "Reading vendor ID");
BasicCluster basic;
SetupCluster(basic, proxy, endpoint, timeout);
basic.ReadAttribute<chip::app::Clusters::Basic::Attributes::VendorID::TypeInfo>(this, BasicVendorCallback,
AttributeReadFailure);
}
break;
case CommissioningStage::kReadProductId: {
ChipLogProgress(Controller, "Reading product ID");
BasicCluster basic;
SetupCluster(basic, proxy, endpoint, timeout);
basic.ReadAttribute<chip::app::Clusters::Basic::Attributes::ProductID::TypeInfo>(this, BasicProductCallback,
AttributeReadFailure);
}
break;
case CommissioningStage::kReadSoftwareVersion: {
ChipLogProgress(Controller, "Reading software version");
BasicCluster basic;
SetupCluster(basic, proxy, endpoint, timeout);
basic.ReadAttribute<chip::app::Clusters::Basic::Attributes::SoftwareVersion::TypeInfo>(this, BasicSoftwareCallback,
AttributeReadFailure);
}
break;
case CommissioningStage::kArmFailsafe: {
ChipLogProgress(Controller, "Arming failsafe");
// TODO: should get the endpoint information from the descriptor cluster.
Expand All @@ -1675,11 +1775,29 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
break;
case CommissioningStage::kGetNetworkTechnology: {
ChipLogProgress(Controller, "Sending request for network cluster feature map");
NetworkCommissioningCluster netCom;
// TODO: swap to given endpoint once that PR is merged
netCom.Associate(proxy, 0);
netCom.ReadAttribute<app::Clusters::NetworkCommissioning::Attributes::FeatureMap::TypeInfo>(this, OnFeatureMapSuccess,
AttributeReadFailure);
app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
app::ReadPrepareParams readParams(proxy->GetSecureSession().Value());
app::AttributePathParams readPath(app::Clusters::NetworkCommissioning::Id,
app::Clusters::NetworkCommissioning::Attributes::FeatureMap::Id);
readParams.mpAttributePathParamsList = &readPath;
readParams.mAttributePathParamsListSize = 1;
if (timeout.HasValue())
{
readParams.mTimeout = timeout.Value();
}
auto attributeCache = Platform::MakeUnique<app::AttributeCache>(*this);
auto readClient = chip::Platform::MakeUnique<app::ReadClient>(
engine, proxy->GetExchangeManager(), attributeCache->GetBufferedCallback(), app::ReadClient::InteractionType::Read);
CHIP_ERROR err = readClient->SendRequest(readParams);
if (err != CHIP_NO_ERROR)
{
ChipLogError(Controller, "Failed to send read request for networking clusters");
CommissioningStageComplete(err);
return;
}
mAttributeCache = std::move(attributeCache);
mReadClient = std::move(readClient);
return;
}
break;
case CommissioningStage::kConfigRegulatory: {
Expand Down
Loading

0 comments on commit 1ee9366

Please sign in to comment.