Skip to content

Commit

Permalink
Add regulatory location read and defaults. (#15368)
Browse files Browse the repository at this point in the history
Per 11.9.6.4 we should be checking the location capability.
Per 11.9.6.3 the device has a valid default value that should be
used when we call SetRegulatoryLocation.

Also adds the ability for the commissioner to pass in the desired
location. Setting this from the config manager is actually incorrect
because the device may be in a different location than the
commissioner.
  • Loading branch information
cecille authored and pull[bot] committed Aug 10, 2023
1 parent 28234b5 commit 1768660
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 26 deletions.
6 changes: 4 additions & 2 deletions src/controller/AutoCommissioner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,8 +323,10 @@ CHIP_ERROR AutoCommissioner::CommissioningStepFinished(CHIP_ERROR err, Commissio
{
mParams.SetFailsafeTimerSeconds(mDeviceCommissioningInfo.general.recommendedFailsafe);
}
mParams.SetRemoteVendorId(report.Get<ReadCommissioningInfo>().basic.vendorId);
mParams.SetRemoteProductId(report.Get<ReadCommissioningInfo>().basic.productId);
mParams.SetRemoteVendorId(mDeviceCommissioningInfo.basic.vendorId)
.SetRemoteProductId(mDeviceCommissioningInfo.basic.productId)
.SetDefaultRegulatoryLocation(mDeviceCommissioningInfo.general.currentRegulatoryLocation)
.SetLocationCapability(mDeviceCommissioningInfo.general.locationCapability);
break;
case CommissioningStage::kSendPAICertificateRequest:
SetPAI(report.Get<RequestedCertificate>().certificate);
Expand Down
99 changes: 75 additions & 24 deletions src/controller/CHIPDeviceController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1563,15 +1563,38 @@ void DeviceCommissioner::OnDone()
// Using ForEachAttribute because this attribute can be queried on any endpoint.
err = mAttributeCache->ForEachAttribute(
app::Clusters::GeneralCommissioning::Id, [this, &info](const app::ConcreteAttributePath & path) {
if (path.mAttributeId != app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::Id)
switch (path.mAttributeId)
{
case app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::Id: {
app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::TypeInfo::DecodableType basicInfo;
ReturnErrorOnFailure(
this->mAttributeCache->Get<app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::TypeInfo>(
path, basicInfo));
info.general.recommendedFailsafe = basicInfo.failSafeExpiryLengthSeconds;
}
break;
case app::Clusters::GeneralCommissioning::Attributes::RegulatoryConfig::Id: {
ReturnErrorOnFailure(
this->mAttributeCache->Get<app::Clusters::GeneralCommissioning::Attributes::RegulatoryConfig::TypeInfo>(
path, info.general.currentRegulatoryLocation));
}
break;
case app::Clusters::GeneralCommissioning::Attributes::LocationCapability::Id: {
ReturnErrorOnFailure(
this->mAttributeCache->Get<app::Clusters::GeneralCommissioning::Attributes::LocationCapability::TypeInfo>(
path, info.general.locationCapability));
}
break;
case app::Clusters::GeneralCommissioning::Attributes::Breadcrumb::Id: {
ReturnErrorOnFailure(
this->mAttributeCache->Get<app::Clusters::GeneralCommissioning::Attributes::Breadcrumb::TypeInfo>(
path, info.general.breadcrumb));
}
break;
default:
return CHIP_NO_ERROR;
}
app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::TypeInfo::DecodableType basicInfo;
ReturnErrorOnFailure(
this->mAttributeCache->Get<app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::TypeInfo>(
path, basicInfo));
info.general.recommendedFailsafe = basicInfo.failSafeExpiryLengthSeconds;

return CHIP_NO_ERROR;
});

Expand Down Expand Up @@ -1734,22 +1757,29 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
app::InteractionModelEngine * engine = app::InteractionModelEngine::GetInstance();
app::ReadPrepareParams readParams(proxy->GetSecureSession().Value());

app::AttributePathParams readPaths[5];
app::AttributePathParams readPaths[8];
// Read all the feature maps for all the networking clusters on any endpoint to determine what is supported
readPaths[0] = app::AttributePathParams(app::Clusters::NetworkCommissioning::Id,
app::Clusters::NetworkCommissioning::Attributes::FeatureMap::Id);
// Get the basic commissioning info from the general commissioning cluster on this endpoint (recommended failsafe time)
// Get required general commissioning attributes on this endpoint (recommended failsafe time, regulatory location
// info, breadcrumb)
readPaths[1] = app::AttributePathParams(endpoint, app::Clusters::GeneralCommissioning::Id,
app::Clusters::GeneralCommissioning::Attributes::Breadcrumb::Id);
readPaths[2] = app::AttributePathParams(endpoint, app::Clusters::GeneralCommissioning::Id,
app::Clusters::GeneralCommissioning::Attributes::BasicCommissioningInfo::Id);
readPaths[3] = app::AttributePathParams(endpoint, app::Clusters::GeneralCommissioning::Id,
app::Clusters::GeneralCommissioning::Attributes::RegulatoryConfig::Id);
readPaths[4] = app::AttributePathParams(endpoint, app::Clusters::GeneralCommissioning::Id,
app::Clusters::GeneralCommissioning::Attributes::LocationCapability::Id);
// Read attributes from the basic info cluster (vendor id / product id / software version)
readPaths[2] = app::AttributePathParams(endpoint, app::Clusters::Basic::Id, app::Clusters::Basic::Attributes::VendorID::Id);
readPaths[3] =
readPaths[5] = app::AttributePathParams(endpoint, app::Clusters::Basic::Id, app::Clusters::Basic::Attributes::VendorID::Id);
readPaths[6] =
app::AttributePathParams(endpoint, app::Clusters::Basic::Id, app::Clusters::Basic::Attributes::ProductID::Id);
readPaths[4] =
readPaths[7] =
app::AttributePathParams(endpoint, app::Clusters::Basic::Id, app::Clusters::Basic::Attributes::SoftwareVersion::Id);

readParams.mpAttributePathParamsList = readPaths;
readParams.mAttributePathParamsListSize = 5;
readParams.mAttributePathParamsListSize = 8;
if (timeout.HasValue())
{
readParams.mTimeout = timeout.Value();
Expand Down Expand Up @@ -1778,25 +1808,46 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
// TODO(cecille): Worthwhile to keep this around as part of the class?
// TODO(cecille): Where is the country config actually set?
ChipLogProgress(Controller, "Setting Regulatory Config");
uint8_t regulatoryLocation = to_underlying(app::Clusters::GeneralCommissioning::RegulatoryLocationType::kOutdoor);
#if CONFIG_DEVICE_LAYER
CHIP_ERROR status = DeviceLayer::ConfigurationMgr().GetRegulatoryLocation(regulatoryLocation);
#else
CHIP_ERROR status = CHIP_ERROR_NOT_IMPLEMENTED;
#endif
if (status != CHIP_NO_ERROR)
auto capability =
params.GetLocationCapability().ValueOr(app::Clusters::GeneralCommissioning::RegulatoryLocationType::kOutdoor);
app::Clusters::GeneralCommissioning::RegulatoryLocationType regulatoryLocation;
// Value is only switchable on the devices with indoor/outdoor capability
if (capability == app::Clusters::GeneralCommissioning::RegulatoryLocationType::kIndoorOutdoor)
{
ChipLogError(Controller, "Unable to find regulatory location, defaulting to outdoor");
// If the device supports indoor and outdoor configs, use the setting from the commissioner, otherwise fall back to the
// current device setting then to outdoor (most restrictive)
if (params.GetDeviceRegulatoryLocation().HasValue())
{
regulatoryLocation = params.GetDeviceRegulatoryLocation().Value();
ChipLogProgress(Controller, "Setting regulatory location to %u from commissioner override",
static_cast<uint8_t>(regulatoryLocation));
}
else if (params.GetDefaultRegulatoryLocation().HasValue())
{
regulatoryLocation = params.GetDefaultRegulatoryLocation().Value();
ChipLogProgress(Controller, "No regulatory location supplied by controller, leaving as device default (%u)",
static_cast<uint8_t>(regulatoryLocation));
}
else
{
regulatoryLocation = app::Clusters::GeneralCommissioning::RegulatoryLocationType::kOutdoor;
ChipLogProgress(Controller, "No overrride or device regulatory location supplied, setting to outdoor");
}
}
else
{
ChipLogProgress(Controller, "Device does not support configurable regulatory location");
regulatoryLocation = capability;
}

static constexpr size_t kMaxCountryCodeSize = 3;
char countryCodeStr[kMaxCountryCodeSize] = "XX";
size_t actualCountryCodeSize = 2;

#if CONFIG_DEVICE_LAYER
status = DeviceLayer::ConfigurationMgr().GetCountryCode(countryCodeStr, kMaxCountryCodeSize, actualCountryCodeSize);
CHIP_ERROR status =
DeviceLayer::ConfigurationMgr().GetCountryCode(countryCodeStr, kMaxCountryCodeSize, actualCountryCodeSize);
#else
status = CHIP_ERROR_NOT_IMPLEMENTED;
CHIP_ERROR status = CHIP_ERROR_NOT_IMPLEMENTED;
#endif
if (status != CHIP_NO_ERROR)
{
Expand All @@ -1805,7 +1856,7 @@ void DeviceCommissioner::PerformCommissioningStep(DeviceProxy * proxy, Commissio
chip::CharSpan countryCode(countryCodeStr, actualCountryCodeSize);

GeneralCommissioning::Commands::SetRegulatoryConfig::Type request;
request.location = static_cast<GeneralCommissioning::RegulatoryLocationType>(regulatoryLocation);
request.location = regulatoryLocation;
request.countryCode = countryCode;
request.breadcrumb = breadcrumb;
request.timeoutMs = kCommandTimeoutMs;
Expand Down
39 changes: 39 additions & 0 deletions src/controller/CommissioningDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ class CommissioningParameters
static constexpr size_t kMaxCredentialsLen = 64;

const Optional<uint16_t> GetFailsafeTimerSeconds() const { return mFailsafeTimerSeconds; }
const Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationType> GetDeviceRegulatoryLocation() const
{
return mDeviceRegulatoryLocation;
}
const Optional<ByteSpan> GetCSRNonce() const { return mCSRNonce; }
const Optional<ByteSpan> GetAttestationNonce() const { return mAttestationNonce; }
const Optional<WiFiCredentials> GetWiFiCredentials() const { return mWiFiCreds; }
Expand All @@ -97,6 +101,14 @@ class CommissioningParameters
const Optional<ByteSpan> GetDAC() const { return mDAC; }
const Optional<VendorId> GetRemoteVendorId() const { return mRemoteVendorId; }
const Optional<uint16_t> GetRemoteProductId() const { return mRemoteProductId; }
const Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationType> GetDefaultRegulatoryLocation() const
{
return mDefaultRegulatoryLocation;
}
const Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationType> GetLocationCapability() const
{
return mLocationCapability;
}
CHIP_ERROR GetCompletionStatus() { return completionStatus; }

CommissioningParameters & SetFailsafeTimerSeconds(uint16_t seconds)
Expand All @@ -105,6 +117,12 @@ class CommissioningParameters
return *this;
}

CommissioningParameters & SetDeviceRegulatoryLocation(app::Clusters::GeneralCommissioning::RegulatoryLocationType location)
{
mDeviceRegulatoryLocation.SetValue(location);
return *this;
}

// The lifetime of the buffer csrNonce is pointing to, should exceed the lifetime of CommissioningParameters object.
CommissioningParameters & SetCSRNonce(ByteSpan csrNonce)
{
Expand Down Expand Up @@ -195,10 +213,22 @@ class CommissioningParameters
mRemoteProductId = MakeOptional(id);
return *this;
}
CommissioningParameters & SetDefaultRegulatoryLocation(app::Clusters::GeneralCommissioning::RegulatoryLocationType location)
{
mDefaultRegulatoryLocation = MakeOptional(location);
return *this;
}
CommissioningParameters & SetLocationCapability(app::Clusters::GeneralCommissioning::RegulatoryLocationType capability)
{
mLocationCapability = MakeOptional(capability);
return *this;
}
void SetCompletionStatus(CHIP_ERROR err) { completionStatus = err; }

private:
// Items that can be set by the commissioner
Optional<uint16_t> mFailsafeTimerSeconds;
Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationType> mDeviceRegulatoryLocation;
Optional<ByteSpan> mCSRNonce; ///< CSR Nonce passed by the commissioner
Optional<ByteSpan> mAttestationNonce; ///< Attestation Nonce passed by the commissioner
Optional<WiFiCredentials> mWiFiCreds;
Expand All @@ -209,12 +239,15 @@ class CommissioningParameters
Optional<ByteSpan> mIcac;
Optional<AesCcm128Key> mIpk;
Optional<NodeId> mAdminSubject;
// Items that come from the device in commissioning steps
Optional<ByteSpan> mAttestationElements;
Optional<ByteSpan> mAttestationSignature;
Optional<ByteSpan> mPAI;
Optional<ByteSpan> mDAC;
Optional<VendorId> mRemoteVendorId;
Optional<uint16_t> mRemoteProductId;
Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationType> mDefaultRegulatoryLocation;
Optional<app::Clusters::GeneralCommissioning::RegulatoryLocationType> mLocationCapability;
CHIP_ERROR completionStatus = CHIP_NO_ERROR;
};

Expand Down Expand Up @@ -265,7 +298,13 @@ struct BasicClusterInfo
};
struct GeneralCommissioningInfo
{
uint64_t breadcrumb = 0;
uint16_t recommendedFailsafe = 0;
app::Clusters::GeneralCommissioning::RegulatoryLocationType currentRegulatoryLocation =
app::Clusters::GeneralCommissioning::RegulatoryLocationType::kIndoorOutdoor;
app::Clusters::GeneralCommissioning::RegulatoryLocationType locationCapability =
app::Clusters::GeneralCommissioning::RegulatoryLocationType::kIndoorOutdoor;
;
};

struct ReadCommissioningInfo
Expand Down

0 comments on commit 1768660

Please sign in to comment.