diff --git a/src/app/AttributeCache.h b/src/app/AttributeCache.h index 97e3bd63166408..1e682d29527c93 100644 --- a/src/app/AttributeCache.h +++ b/src/app/AttributeCache.h @@ -279,6 +279,7 @@ class AttributeCache : protected ReadClient::Callback } } } + return CHIP_NO_ERROR; } /* diff --git a/src/controller/AutoCommissioner.cpp b/src/controller/AutoCommissioner.cpp index d14db401bc6739..e46f2772e26fca 100644 --- a/src/controller/AutoCommissioner.cpp +++ b/src/controller/AutoCommissioner.cpp @@ -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; @@ -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; } @@ -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; } @@ -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; } @@ -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; } @@ -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. @@ -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; } @@ -297,23 +315,17 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio { switch (report.stageCompleted) { + case CommissioningStage::kReadVendorId: + mVendorId = report.Get().vendorId; + break; + case CommissioningStage::kReadProductId: + mProductId = report.Get().productId; + break; + case CommissioningStage::kReadSoftwareVersion: + mSoftwareVersion = report.Get().softwareVersion; + break; case CommissioningStage::kGetNetworkTechnology: - mNetworkTechnology.SetRaw(report.Get().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().features); - err = CHIP_ERROR_INTEGRITY_CHECK_FAILED; - break; - } + mNetworkEndpoints = report.Get(); break; case CommissioningStage::kSendPAICertificateRequest: SetPAI(report.Get().certificate); @@ -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; @@ -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; } diff --git a/src/controller/AutoCommissioner.h b/src/controller/AutoCommissioner.h index 5849ae67a91573..8c262ad04370d9 100644 --- a/src/controller/AutoCommissioner.h +++ b/src/controller/AutoCommissioner.h @@ -50,19 +50,23 @@ class AutoCommissioner : public CommissioningDelegate CHIP_ERROR NOCChainGenerated(ByteSpan noc, ByteSpan icac, ByteSpan rcac, AesCcm128KeySpan ipk, NodeId adminSubject); Optional 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 mNetworkTechnology; + bool mNeedsNetworkSetup = false; + NetworkClusters mNetworkEndpoints; // TODO: Why were the nonces statically allocated, but the certs dynamically allocated? uint8_t * mDAC = nullptr; @@ -74,6 +78,5 @@ class AutoCommissioner : public CommissioningDelegate uint8_t mNOCertBuffer[Credentials::kMaxCHIPCertLength]; uint8_t mICACertBuffer[Credentials::kMaxCHIPCertLength]; }; - } // namespace Controller } // namespace chip diff --git a/src/controller/CHIPDeviceController.cpp b/src/controller/CHIPDeviceController.cpp index 2ca8476c93d7de..5014d44f37f1b1 100644 --- a/src/controller/CHIPDeviceController.cpp +++ b/src/controller/CHIPDeviceController.cpp @@ -36,6 +36,8 @@ #include #include +#include +#include #include #if CONFIG_DEVICE_LAYER @@ -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 * callback, + chip::Callback::Callback * callback, bool readVIDPIDAttributes) { mSetupPayload = SetupPayload(); @@ -781,8 +783,8 @@ CHIP_ERROR DeviceCommissioner::GetDeviceBeingCommissioned(NodeId deviceId, Commi return CHIP_NO_ERROR; } -CHIP_ERROR DeviceCommissioner::GetConnectedDevice(NodeId deviceId, Callback::Callback * onConnection, - Callback::Callback * onFailure) +CHIP_ERROR DeviceCommissioner::GetConnectedDevice(NodeId deviceId, chip::Callback::Callback * onConnection, + chip::Callback::Callback * onFailure) { return DeviceController::GetConnectedDevice(deviceId, onConnection, onFailure); } @@ -1582,15 +1584,31 @@ void DeviceCommissioner::OnDeviceConnectionFailureFn(void * context, PeerId peer void DeviceCommissioner::SetupCluster(ClusterBase & base, DeviceProxy * proxy, EndpointId endpoint, Optional 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(context); CommissioningDelegate::CommissioningReport report; - report.Set(value); + report.Set(vendorId); + commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report); +} + +void BasicProductCallback(void * context, uint16_t productId) +{ + DeviceCommissioner * commissioner = static_cast(context); + CommissioningDelegate::CommissioningReport report; + report.Set(productId); + commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report); +} + +void BasicSoftwareCallback(void * context, uint32_t softwareVersion) +{ + DeviceCommissioner * commissioner = static_cast(context); + CommissioningDelegate::CommissioningReport report; + report.Set(softwareVersion); commissioner->CommissioningStageComplete(CHIP_NO_ERROR, report); } @@ -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 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(clusters); + CommissioningStageComplete(err, report); +} void DeviceCommissioner::OnArmFailSafe(void * context, const GeneralCommissioning::Commands::ArmFailSafeResponse::DecodableType & data) { @@ -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(this, BasicVendorCallback, + AttributeReadFailure); + } + break; + case CommissioningStage::kReadProductId: { + ChipLogProgress(Controller, "Reading product ID"); + BasicCluster basic; + SetupCluster(basic, proxy, endpoint, timeout); + basic.ReadAttribute(this, BasicProductCallback, + AttributeReadFailure); + } + break; + case CommissioningStage::kReadSoftwareVersion: { + ChipLogProgress(Controller, "Reading software version"); + BasicCluster basic; + SetupCluster(basic, proxy, endpoint, timeout); + basic.ReadAttribute(this, BasicSoftwareCallback, + AttributeReadFailure); + } + break; case CommissioningStage::kArmFailsafe: { ChipLogProgress(Controller, "Arming failsafe"); // TODO: should get the endpoint information from the descriptor cluster. @@ -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(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(*this); + auto readClient = chip::Platform::MakeUnique( + 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: { diff --git a/src/controller/CHIPDeviceController.h b/src/controller/CHIPDeviceController.h index 1a9664097d90a9..80beefd4a48fc9 100644 --- a/src/controller/CHIPDeviceController.h +++ b/src/controller/CHIPDeviceController.h @@ -28,6 +28,7 @@ #pragma once +#include #include #include #include @@ -211,7 +212,7 @@ class DLL_EXPORT DeviceController : public SessionRecoveryDelegate * callback. If it fails to establish the connection, it calls `onError` callback. */ virtual CHIP_ERROR GetConnectedDevice(NodeId deviceId, Callback::Callback * onConnection, - Callback::Callback * onFailure) + chip::Callback::Callback * onFailure) { VerifyOrReturnError(mState == State::Initialized && mFabricInfo != nullptr, CHIP_ERROR_INCORRECT_STATE); return mCASESessionManager->FindOrEstablishSession(mFabricInfo->GetPeerIdForNode(deviceId), onConnection, onFailure); @@ -446,7 +447,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, #if CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY // make this commissioner discoverable public Protocols::UserDirectedCommissioning::InstanceNameResolver, #endif - public SessionEstablishmentDelegate + public SessionEstablishmentDelegate, + public app::AttributeCache::Callback { public: DeviceCommissioner(); @@ -547,8 +549,8 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, CHIP_ERROR GetDeviceBeingCommissioned(NodeId deviceId, CommissioneeDeviceProxy ** device); - CHIP_ERROR GetConnectedDevice(NodeId deviceId, Callback::Callback * onConnection, - Callback::Callback * onFailure) override; + CHIP_ERROR GetConnectedDevice(NodeId deviceId, chip::Callback::Callback * onConnection, + chip::Callback::Callback * onFailure) override; /** * @brief @@ -676,6 +678,9 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, void RegisterPairingDelegate(DevicePairingDelegate * pairingDelegate) { mPairingDelegate = pairingDelegate; } + // AttributeCache::Callback impl + void OnDone() override; + private: DevicePairingDelegate * mPairingDelegate; @@ -849,18 +854,21 @@ class DLL_EXPORT DeviceCommissioner : public DeviceController, static CHIP_ERROR ConvertFromNodeOperationalCertStatus(uint8_t err); - Callback::Callback mOnDeviceConnectedCallback; - Callback::Callback mOnDeviceConnectionFailureCallback; + chip::Callback::Callback mOnDeviceConnectedCallback; + chip::Callback::Callback mOnDeviceConnectionFailureCallback; - Callback::Callback mDeviceAttestationInformationVerificationCallback; + chip::Callback::Callback mDeviceAttestationInformationVerificationCallback; - Callback::Callback mDeviceNOCChainCallback; + chip::Callback::Callback mDeviceNOCChainCallback; SetUpCodePairer mSetUpCodePairer; AutoCommissioner mAutoCommissioner; CommissioningDelegate * mDefaultCommissioner = nullptr; // Commissioning delegate to call when PairDevice / Commission functions are used CommissioningDelegate * mCommissioningDelegate = nullptr; // Commissioning delegate that issued the PerformCommissioningStep command + + Platform::UniquePtr mAttributeCache; + Platform::UniquePtr mReadClient; }; } // namespace Controller diff --git a/src/controller/CommissioningDelegate.h b/src/controller/CommissioningDelegate.h index 2425b37665caa6..16474ee08460c1 100644 --- a/src/controller/CommissioningDelegate.h +++ b/src/controller/CommissioningDelegate.h @@ -30,11 +30,14 @@ enum CommissioningStage : uint8_t { kError, kSecurePairing, + kReadVendorId, + kReadProductId, + kReadSoftwareVersion, + kGetNetworkTechnology, kArmFailsafe, // kConfigTime, // NOT YET IMPLEMENTED // kConfigTimeZone, // NOT YET IMPLEMENTED // kConfigDST, // NOT YET IMPLEMENTED - kGetNetworkTechnology, kConfigRegulatory, kSendPAICertificateRequest, kSendDACCertificateRequest, @@ -235,22 +238,41 @@ struct OperationalNodeFoundData OperationalNodeFoundData(OperationalDeviceProxy * proxy) : operationalProxy(proxy) {} OperationalDeviceProxy * operationalProxy; }; -struct FeatureMap + +struct BasicVendor { - FeatureMap(uint32_t featureBitmap) : features(featureBitmap) {} - uint32_t features; + BasicVendor(VendorId id) : vendorId(id) {} + VendorId vendorId; +}; + +struct BasicProduct +{ + BasicProduct(uint16_t id) : productId(id) {} + uint16_t productId; +}; + +struct BasicSoftware +{ + BasicSoftware(uint32_t version) : softwareVersion(version) {} + uint32_t softwareVersion; +}; + +struct NetworkClusters +{ + EndpointId wifi = kInvalidEndpointId; + EndpointId thread = kInvalidEndpointId; + EndpointId eth = kInvalidEndpointId; }; class CommissioningDelegate { public: virtual ~CommissioningDelegate(){}; - - struct CommissioningReport : Variant + struct CommissioningReport : Variant { CommissioningReport() : stageCompleted(CommissioningStage::kError) {} CommissioningStage stageCompleted; - // TODO: Add other things the delegate needs to know. }; virtual CHIP_ERROR SetCommissioningParameters(const CommissioningParameters & params) = 0; virtual void SetOperationalCredentialsDelegate(OperationalCredentialsDelegate * operationalCredentialsDelegate) = 0;