Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ember compatibility layer - split out GlobalAttributeAccessInterface and the I/O buffer #33396

Merged
merged 9 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,10 @@ jobs:
--known-failure app/util/DataModelHandler.h \
--known-failure app/util/ember-compatibility-functions.cpp \
--known-failure app/util/ember-compatibility-functions.h \
--known-failure app/util/ember-global-attribute-access-interface.cpp \
--known-failure app/util/ember-global-attribute-access-interface.h \
--known-failure app/util/ember-io-storage.cpp \
--known-failure app/util/ember-io-storage.h \
--known-failure app/util/endpoint-config-api.h \
--known-failure app/util/generic-callbacks.h \
--known-failure app/util/generic-callback-stubs.cpp \
Expand Down
2 changes: 2 additions & 0 deletions src/app/chip_data_model.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ function(chip_configure_data_model APP_TARGET)
${CHIP_APP_BASE_DIR}/icd/server/ICDConfigurationData.cpp
${CHIP_APP_BASE_DIR}/util/DataModelHandler.cpp
${CHIP_APP_BASE_DIR}/util/ember-compatibility-functions.cpp
${CHIP_APP_BASE_DIR}/util/ember-global-attribute-access-interface.cpp
${CHIP_APP_BASE_DIR}/util/ember-io-storage.cpp
${CHIP_APP_BASE_DIR}/util/generic-callback-stubs.cpp
${CHIP_APP_BASE_DIR}/util/privilege-storage.cpp
${CHIP_APP_BASE_DIR}/util/util.cpp
Expand Down
2 changes: 2 additions & 0 deletions src/app/chip_data_model.gni
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ template("chip_data_model") {
"${_app_root}/util/attribute-storage.cpp",
"${_app_root}/util/attribute-table.cpp",
"${_app_root}/util/ember-compatibility-functions.cpp",
"${_app_root}/util/ember-global-attribute-access-interface.cpp",
"${_app_root}/util/ember-io-storage.cpp",
"${_app_root}/util/util.cpp",
]
}
Expand Down
1 change: 0 additions & 1 deletion src/app/tests/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ static_library("helpers") {
"${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp",
"AppTestContext.cpp",
"AppTestContext.h",
"integration/RequiredPrivilegeStubs.cpp",
]

cflags = [ "-Wconversion" ]
Expand Down
2 changes: 0 additions & 2 deletions src/app/tests/integration/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ source_set("common") {
executable("chip-im-initiator") {
sources = [
"${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp",
"RequiredPrivilegeStubs.cpp",
"chip_im_initiator.cpp",
]

Expand All @@ -61,7 +60,6 @@ executable("chip-im-responder") {
"${chip_root}/src/app/reporting/tests/MockReportScheduler.cpp",
"MockEvents.cpp",
"MockEvents.h",
"RequiredPrivilegeStubs.cpp",
"chip_im_responder.cpp",
]

Expand Down
286 changes: 27 additions & 259 deletions src/app/util/ember-compatibility-functions.cpp

Large diffs are not rendered by default.

136 changes: 136 additions & 0 deletions src/app/util/ember-global-attribute-access-interface.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright (c) 2021-2024 Project CHIP Authors
*
* 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 <app/util/ember-global-attribute-access-interface.h>

#include <app/GlobalAttributes.h>
#include <app/InteractionModelEngine.h>

namespace chip {
namespace app {
namespace Compatibility {

CHIP_ERROR GlobalAttributeReader::Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder)
{
using namespace Clusters::Globals::Attributes;
switch (aPath.mAttributeId)
{
case AttributeList::Id:
return aEncoder.EncodeList([this](const auto & encoder) {
const size_t count = mCluster->attributeCount;
bool addedExtraGlobals = false;
for (size_t i = 0; i < count; ++i)
{
AttributeId id = mCluster->attributes[i].attributeId;
constexpr auto lastGlobalId = GlobalAttributesNotInMetadata[ArraySize(GlobalAttributesNotInMetadata) - 1];
#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
// The GlobalAttributesNotInMetadata shouldn't have any gaps in their ids here.
static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata) - 1,
"Ids in GlobalAttributesNotInMetadata not consecutive");
#else
// If EventList is not supported. The GlobalAttributesNotInMetadata is missing one id here.
static_assert(lastGlobalId - GlobalAttributesNotInMetadata[0] == ArraySize(GlobalAttributesNotInMetadata),
"Ids in GlobalAttributesNotInMetadata not consecutive (except EventList)");
#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
if (!addedExtraGlobals && id > lastGlobalId)
{
for (const auto & globalId : GlobalAttributesNotInMetadata)
{
ReturnErrorOnFailure(encoder.Encode(globalId));
}
addedExtraGlobals = true;
}
ReturnErrorOnFailure(encoder.Encode(id));
}
if (!addedExtraGlobals)
{
for (const auto & globalId : GlobalAttributesNotInMetadata)
{
ReturnErrorOnFailure(encoder.Encode(globalId));
}
}
return CHIP_NO_ERROR;
});
#if CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
case EventList::Id:
return aEncoder.EncodeList([this](const auto & encoder) {
for (size_t i = 0; i < mCluster->eventCount; ++i)
{
ReturnErrorOnFailure(encoder.Encode(mCluster->eventList[i]));
}
return CHIP_NO_ERROR;
});
#endif // CHIP_CONFIG_ENABLE_EVENTLIST_ATTRIBUTE
case AcceptedCommandList::Id:
return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateAcceptedCommands,
mCluster->acceptedCommandList);
case GeneratedCommandList::Id:
return EncodeCommandList(aPath, aEncoder, &CommandHandlerInterface::EnumerateGeneratedCommands,
mCluster->generatedCommandList);
default:
// This function is only called if attributeCluster is non-null in
// ReadSingleClusterData, which only happens for attributes listed in
// GlobalAttributesNotInMetadata. If we reach this code, someone added
// a global attribute to that list but not the above switch.
VerifyOrDieWithMsg(false, DataManagement, "Unexpected global attribute: " ChipLogFormatMEI,
ChipLogValueMEI(aPath.mAttributeId));
return CHIP_NO_ERROR;
}
}

CHIP_ERROR GlobalAttributeReader::EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder,
GlobalAttributeReader::CommandListEnumerator aEnumerator,
const CommandId * aClusterCommandList)
{
return aEncoder.EncodeList([&](const auto & encoder) {
auto * commandHandler =
InteractionModelEngine::GetInstance()->FindCommandHandler(aClusterPath.mEndpointId, aClusterPath.mClusterId);
if (commandHandler)
{
struct Context
{
decltype(encoder) & commandIdEncoder;
CHIP_ERROR err;
} context{ encoder, CHIP_NO_ERROR };
CHIP_ERROR err = (commandHandler->*aEnumerator)(
aClusterPath,
[](CommandId command, void * closure) -> Loop {
auto * ctx = static_cast<Context *>(closure);
ctx->err = ctx->commandIdEncoder.Encode(command);
if (ctx->err != CHIP_NO_ERROR)
{
return Loop::Break;
}
return Loop::Continue;
},
&context);
if (err != CHIP_ERROR_NOT_IMPLEMENTED)
{
return context.err;
}
// Else fall through to the list in aClusterCommandList.
}

for (const CommandId * cmd = aClusterCommandList; cmd != nullptr && *cmd != kInvalidCommandId; cmd++)
{
ReturnErrorOnFailure(encoder.Encode(*cmd));
}
return CHIP_NO_ERROR;
});
}

} // namespace Compatibility
} // namespace app
} // namespace chip
56 changes: 56 additions & 0 deletions src/app/util/ember-global-attribute-access-interface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2021-2024 Project CHIP Authors
*
* 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.
*/
#pragma once

#include <app/AttributeAccessInterface.h>
#include <app/CommandHandlerInterface.h>
#include <app/util/af-types.h>

namespace chip {
namespace app {
namespace Compatibility {

// This reader should never actually be registered; we do manual dispatch to it
// for the one attribute it handles.
class MandatoryGlobalAttributeReader : public AttributeAccessInterface
{
public:
MandatoryGlobalAttributeReader(const EmberAfCluster * aCluster) :
AttributeAccessInterface(MakeOptional(kInvalidEndpointId), kInvalidClusterId), mCluster(aCluster)
{}

protected:
const EmberAfCluster * mCluster;
};

class GlobalAttributeReader : public MandatoryGlobalAttributeReader
{
public:
GlobalAttributeReader(const EmberAfCluster * aCluster) : MandatoryGlobalAttributeReader(aCluster) {}

CHIP_ERROR Read(const ConcreteReadAttributePath & aPath, AttributeValueEncoder & aEncoder) override;

private:
typedef CHIP_ERROR (CommandHandlerInterface::*CommandListEnumerator)(const ConcreteClusterPath & cluster,
CommandHandlerInterface::CommandIdCallback callback,
void * context);
static CHIP_ERROR EncodeCommandList(const ConcreteClusterPath & aClusterPath, AttributeValueEncoder & aEncoder,
CommandListEnumerator aEnumerator, const CommandId * aClusterCommandList);
};

} // namespace Compatibility
} // namespace app
} // namespace chip
126 changes: 126 additions & 0 deletions src/app/util/ember-io-storage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Copyright (c) 2024 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 <app/util/ember-io-storage.h>

#include <app-common/zap-generated/attribute-type.h>
#include <zap-generated/endpoint_config.h>

#include <cstddef>

namespace chip {
namespace app {
namespace Compatibility {
namespace Internal {

// On some apps, ATTRIBUTE_LARGEST can as small as 3, making compiler unhappy since data[kAttributeReadBufferSize] cannot hold
// uint64_t. Make kAttributeReadBufferSize at least 8 so it can fit all basic types.
constexpr size_t kAttributeReadBufferSize = (ATTRIBUTE_LARGEST >= 8 ? ATTRIBUTE_LARGEST : 8);
uint8_t attributeIOBuffer[kAttributeReadBufferSize];

MutableByteSpan gEmberAttributeIOBufferSpan(attributeIOBuffer);

EmberAfAttributeType AttributeBaseType(EmberAfAttributeType type)
{
switch (type)
{
case ZCL_ACTION_ID_ATTRIBUTE_TYPE: // Action Id
case ZCL_FABRIC_IDX_ATTRIBUTE_TYPE: // Fabric Index
case ZCL_BITMAP8_ATTRIBUTE_TYPE: // 8-bit bitmap
case ZCL_ENUM8_ATTRIBUTE_TYPE: // 8-bit enumeration
case ZCL_STATUS_ATTRIBUTE_TYPE: // Status Code
case ZCL_PERCENT_ATTRIBUTE_TYPE: // Percentage
static_assert(std::is_same<chip::Percent, uint8_t>::value,
"chip::Percent is expected to be uint8_t, change this when necessary");
return ZCL_INT8U_ATTRIBUTE_TYPE;

case ZCL_ENDPOINT_NO_ATTRIBUTE_TYPE: // Endpoint Number
case ZCL_GROUP_ID_ATTRIBUTE_TYPE: // Group Id
case ZCL_VENDOR_ID_ATTRIBUTE_TYPE: // Vendor Id
case ZCL_ENUM16_ATTRIBUTE_TYPE: // 16-bit enumeration
case ZCL_BITMAP16_ATTRIBUTE_TYPE: // 16-bit bitmap
case ZCL_PERCENT100THS_ATTRIBUTE_TYPE: // 100ths of a percent
static_assert(std::is_same<chip::EndpointId, uint16_t>::value,
"chip::EndpointId is expected to be uint16_t, change this when necessary");
static_assert(std::is_same<chip::GroupId, uint16_t>::value,
"chip::GroupId is expected to be uint16_t, change this when necessary");
static_assert(std::is_same<chip::Percent100ths, uint16_t>::value,
"chip::Percent100ths is expected to be uint16_t, change this when necessary");
return ZCL_INT16U_ATTRIBUTE_TYPE;

case ZCL_CLUSTER_ID_ATTRIBUTE_TYPE: // Cluster Id
case ZCL_ATTRIB_ID_ATTRIBUTE_TYPE: // Attribute Id
case ZCL_FIELD_ID_ATTRIBUTE_TYPE: // Field Id
case ZCL_EVENT_ID_ATTRIBUTE_TYPE: // Event Id
case ZCL_COMMAND_ID_ATTRIBUTE_TYPE: // Command Id
case ZCL_TRANS_ID_ATTRIBUTE_TYPE: // Transaction Id
case ZCL_DEVTYPE_ID_ATTRIBUTE_TYPE: // Device Type Id
case ZCL_DATA_VER_ATTRIBUTE_TYPE: // Data Version
case ZCL_BITMAP32_ATTRIBUTE_TYPE: // 32-bit bitmap
case ZCL_EPOCH_S_ATTRIBUTE_TYPE: // Epoch Seconds
case ZCL_ELAPSED_S_ATTRIBUTE_TYPE: // Elapsed Seconds
static_assert(std::is_same<chip::ClusterId, uint32_t>::value,
"chip::Cluster is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
"chip::AttributeId is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::AttributeId, uint32_t>::value,
"chip::AttributeId is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::EventId, uint32_t>::value,
"chip::EventId is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::CommandId, uint32_t>::value,
"chip::CommandId is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::TransactionId, uint32_t>::value,
"chip::TransactionId is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::DeviceTypeId, uint32_t>::value,
"chip::DeviceTypeId is expected to be uint32_t, change this when necessary");
static_assert(std::is_same<chip::DataVersion, uint32_t>::value,
"chip::DataVersion is expected to be uint32_t, change this when necessary");
return ZCL_INT32U_ATTRIBUTE_TYPE;

case ZCL_AMPERAGE_MA_ATTRIBUTE_TYPE: // Amperage milliamps
case ZCL_ENERGY_MWH_ATTRIBUTE_TYPE: // Energy milliwatt-hours
case ZCL_POWER_MW_ATTRIBUTE_TYPE: // Power milliwatts
case ZCL_VOLTAGE_MV_ATTRIBUTE_TYPE: // Voltage millivolts
return ZCL_INT64S_ATTRIBUTE_TYPE;

case ZCL_EVENT_NO_ATTRIBUTE_TYPE: // Event Number
case ZCL_FABRIC_ID_ATTRIBUTE_TYPE: // Fabric Id
case ZCL_NODE_ID_ATTRIBUTE_TYPE: // Node Id
case ZCL_BITMAP64_ATTRIBUTE_TYPE: // 64-bit bitmap
case ZCL_EPOCH_US_ATTRIBUTE_TYPE: // Epoch Microseconds
case ZCL_POSIX_MS_ATTRIBUTE_TYPE: // POSIX Milliseconds
case ZCL_SYSTIME_MS_ATTRIBUTE_TYPE: // System time Milliseconds
case ZCL_SYSTIME_US_ATTRIBUTE_TYPE: // System time Microseconds
static_assert(std::is_same<chip::EventNumber, uint64_t>::value,
"chip::EventNumber is expected to be uint64_t, change this when necessary");
static_assert(std::is_same<chip::FabricId, uint64_t>::value,
"chip::FabricId is expected to be uint64_t, change this when necessary");
static_assert(std::is_same<chip::NodeId, uint64_t>::value,
"chip::NodeId is expected to be uint64_t, change this when necessary");
return ZCL_INT64U_ATTRIBUTE_TYPE;

case ZCL_TEMPERATURE_ATTRIBUTE_TYPE: // Temperature
return ZCL_INT16S_ATTRIBUTE_TYPE;

default:
return type;
}
}

} // namespace Internal
} // namespace Compatibility
} // namespace app
} // namespace chip
Loading
Loading