blob: 75ed984e10e1b4626306eb1bed0c924cbbafa2d7 [file] [log] [blame]
/*
* Copyright (c) 2025 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <data-model-providers/codegen/ClusterIntegration.h>
#include <app/util/attribute-storage-null-handling.h>
#include <app/util/attribute-storage.h>
#include <app/util/attribute-table.h>
#include <app/util/endpoint-config-api.h>
#include <data-model-providers/codegen/CodegenDataModelProvider.h>
#include <data-model-providers/codegen/CodegenProcessingConfig.h>
#include <limits>
namespace chip::app {
namespace {
bool findEndpointWithLog(EndpointId endpointId, ClusterId clusterId, uint16_t fixedClusterServerEndpointCount,
uint16_t maxEndpointCount, uint16_t & emberEndpointIndex)
{
emberEndpointIndex = emberAfGetClusterServerEndpointIndex(endpointId, clusterId, fixedClusterServerEndpointCount);
if (emberEndpointIndex >= maxEndpointCount)
{
#if CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
ChipLogError(AppServer,
"Could not find a valid endpoint index for endpoint %u/" ChipLogFormatMEI " (Index %u was not valid)",
endpointId, ChipLogValueMEI(clusterId), emberEndpointIndex);
#endif // CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
return false;
}
return true;
}
/// Fetch the featuremap from ember for the given endpoint/cluster
///
/// on error 0 is returned
uint32_t LoadFeatureMap(EndpointId endpointId, ClusterId clusterId)
{
using Traits = NumericAttributeTraits<uint32_t>;
Traits::StorageType temp;
uint8_t * readable = Traits::ToAttributeStoreRepresentation(temp);
Protocols::InteractionModel::Status status =
emberAfReadAttribute(endpointId, clusterId, Clusters::Globals::Attributes::FeatureMap::Id, readable, sizeof(temp));
if (status != Protocols::InteractionModel::Status::Success)
{
#if CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
ChipLogError(AppServer, "Failed to load feature map for %u/" ChipLogFormatMEI " (Status %d)", endpointId,
ChipLogValueMEI(clusterId), static_cast<int>(status));
#endif // CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
return 0;
}
// note: we do not try to check if value is representable: all the uint32_t values are representable
return Traits::StorageToWorking(temp);
}
} // namespace
void CodegenClusterIntegration::RegisterServer(const RegisterServerOptions & options, Delegate & delegate)
{
uint16_t emberEndpointIndex;
if (!findEndpointWithLog(options.endpointId, options.clusterId, options.fixedClusterServerEndpointCount,
options.maxEndpointCount, emberEndpointIndex))
{
return;
}
uint32_t featureMap = 0;
if (options.fetchFeatureMap)
{
featureMap = LoadFeatureMap(options.endpointId, options.clusterId);
}
// NOTE: we fetch low ID attributes only here as a convenience/speedup for the very frequent cluster case
// where attributes are few and with low IDS.
//
// This is NOT a general rule. Specific examples are:
// - LevelControl::StartupCurrentLevel has ID 0x4000
// - OnOff has several: GlobalSceneControl, OnTime, OffWaitTime, StartupOnOff with id >= 0x4000
// - Thermostat and DoorLock have more than 32 attributes in general
// - ColorControl has a lot of high-ID attributes
//
// The above examples however are few compared to the large number of clusters that matter supports,
// so this optimization is considered worth it at this time.
uint32_t optionalAttributes = 0;
if (options.fetchOptionalAttributes)
{
for (AttributeId attributeId = 0; attributeId < std::numeric_limits<uint32_t>::digits; attributeId++)
{
if (emberAfContainsAttribute(options.endpointId, options.clusterId, attributeId))
{
optionalAttributes |= 1u << attributeId;
}
}
}
CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Register(
delegate.CreateRegistration(options.endpointId, emberEndpointIndex, optionalAttributes, featureMap));
if (err != CHIP_NO_ERROR)
{
#if CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
ChipLogError(AppServer, "Failed to register cluster %u/" ChipLogFormatMEI ": %" CHIP_ERROR_FORMAT, options.endpointId,
ChipLogValueMEI(options.clusterId), err.Format());
#endif // CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
}
}
void CodegenClusterIntegration::UnregisterServer(const UnregisterServerOptions & options, Delegate & delegate)
{
uint16_t emberEndpointIndex;
if (!findEndpointWithLog(options.endpointId, options.clusterId, options.fixedClusterServerEndpointCount,
options.maxEndpointCount, emberEndpointIndex))
{
return;
}
CHIP_ERROR err = CodegenDataModelProvider::Instance().Registry().Unregister(&delegate.FindRegistration(emberEndpointIndex));
if (err != CHIP_NO_ERROR)
{
#if CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
ChipLogError(AppServer, "Failed to unregister cluster %u/" ChipLogFormatMEI ": %" CHIP_ERROR_FORMAT, options.endpointId,
ChipLogValueMEI(options.clusterId), err.Format());
#endif // CHIP_CODEGEN_CONFIG_ENABLE_CODEGEN_INTEGRATION_LOOKUP_ERRORS
// NOTE: There is no sane way to handle this failure:
// - Returning here means we never free resources and a future registration will fail.
// - Not returning (as we do now) will free resources, but it is unclear why unregistration failed (is it still in use?).
//
// For now, assume that unregistration failed due to "already missing", so it is safe to delete.
// However, this should never happen in practice.
}
delegate.ReleaseRegistration(emberEndpointIndex);
}
ServerClusterInterface * CodegenClusterIntegration::GetClusterForEndpointIndex(const GetClusterForEndpointIndexOptions & options,
Delegate & delegate)
{
uint16_t emberEndpointIndex;
if (!findEndpointWithLog(options.endpointId, options.clusterId, options.fixedClusterServerEndpointCount,
options.maxEndpointCount, emberEndpointIndex))
{
return nullptr;
}
return &delegate.FindRegistration(emberEndpointIndex);
}
} // namespace chip::app