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

Add a way to run custom cluster logic for attribute reads #9667

Merged
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 examples/bridge-app/esp32/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ idf_component_register(PRIV_INCLUDE_DIRS
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/basic"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/level-control"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/diagnostic-logs-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ethernet_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software_diagnostics_server"
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/descriptor"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/on-off-server"
Expand Down
99 changes: 0 additions & 99 deletions examples/lighting-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@
#include <platform/CHIPDeviceLayer.h>
#include <platform/PlatformManager.h>

#include <app-common/zap-generated/attribute-id.h>
#include <app-common/zap-generated/cluster-id.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/chip-zcl-zpro-codec.h>
Expand Down Expand Up @@ -89,103 +87,6 @@ void emberAfOnOffClusterInitCallback(EndpointId endpoint)
// TODO: implement any additional Cluster Server init actions
}

EmberAfStatus HandleReadEthernetNetworkDiagnosticsAttribute(chip::AttributeId attributeId, uint8_t * buffer, uint16_t maxReadLength)
{
EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE;

switch (attributeId)
{
case ZCL_PACKET_RX_COUNT_ATTRIBUTE_ID:
if (maxReadLength == sizeof(uint64_t))
{
uint64_t packetRxCount;

if (ConnectivityMgr().GetEthPacketRxCount(packetRxCount) == CHIP_NO_ERROR)
{
memcpy(buffer, &packetRxCount, maxReadLength);
ret = EMBER_ZCL_STATUS_SUCCESS;
}
}
break;
case ZCL_PACKET_TX_COUNT_ATTRIBUTE_ID:
if (maxReadLength == sizeof(uint64_t))
{
uint64_t packetTxCount;

if (ConnectivityMgr().GetEthPacketTxCount(packetTxCount) == CHIP_NO_ERROR)
{
memcpy(buffer, &packetTxCount, maxReadLength);
ret = EMBER_ZCL_STATUS_SUCCESS;
}
}
break;
case ZCL_TX_ERR_COUNT_ATTRIBUTE_ID:
if (maxReadLength == sizeof(uint64_t))
{
uint64_t txErrCount;

if (ConnectivityMgr().GetEthTxErrCount(txErrCount) == CHIP_NO_ERROR)
{
memcpy(buffer, &txErrCount, maxReadLength);
ret = EMBER_ZCL_STATUS_SUCCESS;
}
}
break;
case ZCL_COLLISION_COUNT_ATTRIBUTE_ID:
if (maxReadLength == sizeof(uint64_t))
{
uint64_t collisionCount;

if (ConnectivityMgr().GetEthCollisionCount(collisionCount) == CHIP_NO_ERROR)
{
memcpy(buffer, &collisionCount, maxReadLength);
ret = EMBER_ZCL_STATUS_SUCCESS;
}
}
break;
case ZCL_ETHERNET_OVERRUN_COUNT_ATTRIBUTE_ID:
if (maxReadLength == sizeof(uint64_t))
{
uint64_t overrunCount;

if (ConnectivityMgr().GetEthOverrunCount(overrunCount) == CHIP_NO_ERROR)
{
memcpy(buffer, &overrunCount, maxReadLength);
ret = EMBER_ZCL_STATUS_SUCCESS;
}
}
break;
default:
ChipLogProgress(Zcl, "Unhandled attribute ID: %d", attributeId);
break;
}

return ret;
}

EmberAfStatus emberAfExternalAttributeReadCallback(EndpointId endpoint, ClusterId clusterId,
EmberAfAttributeMetadata * attributeMetadata, uint16_t manufacturerCode,
uint8_t * buffer, uint16_t maxReadLength, int32_t index)
{
EmberAfStatus ret = EMBER_ZCL_STATUS_FAILURE;

ChipLogProgress(Zcl,
"emberAfExternalAttributeReadCallback - Cluster ID: '0x%04x', EndPoint ID: '0x%02x', Attribute ID: '0x%04x'",
clusterId, endpoint, attributeMetadata->attributeId);

switch (clusterId)
{
case ZCL_ETHERNET_NETWORK_DIAGNOSTICS_CLUSTER_ID:
ret = HandleReadEthernetNetworkDiagnosticsAttribute(attributeMetadata->attributeId, buffer, maxReadLength);
break;
default:
ChipLogError(Zcl, "Unhandled cluster ID: %d", clusterId);
break;
}

return ret;
}

int main(int argc, char * argv[])
{
if (ChipLinuxAppInit(argc, argv) != 0)
Expand Down
8 changes: 8 additions & 0 deletions examples/lock-app/esp32/main/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ idf_component_register(INCLUDE_DIRS
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/diagnostic-logs-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ethernet_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software_diagnostics_server"
PRIV_REQUIRES bt chip QRCode)
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved

idf_component_get_property(chip_lib chip COMPONENT_LIB)
Expand Down Expand Up @@ -131,6 +135,10 @@ idf_component_register(PRIV_INCLUDE_DIRS
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/basic"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/bindings"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/diagnostic-logs-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ethernet_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/on-off-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server"
Expand Down
6 changes: 5 additions & 1 deletion examples/lock-app/mbed/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,12 @@ target_sources(${APP_TARGET} PRIVATE
${CHIP_ROOT}/src/app/server/CommissionManager.cpp
${CHIP_ROOT}/src/app/clusters/administrator-commissioning-server/administrator-commissioning-server.cpp
${CHIP_ROOT}/src/app/clusters/basic/basic.cpp
${CHIP_ROOT}/src/app/clusters/diagnostic-logs-server/diagnostic-logs-server.cpp
${CHIP_ROOT}/src/app/clusters/bindings/bindings.cpp
${CHIP_ROOT}/src/app/clusters/diagnostic-logs-server/diagnostic-logs-server.cpp
${CHIP_ROOT}/src/app/clusters/ethernet_network_diagnostics_server/ethernet_network_diagnostics_server.cpp
${CHIP_ROOT}/src/app/clusters/thread_network_diagnostics_server/thread_network_diagnostics_server.cpp
${CHIP_ROOT}/src/app/clusters/wifi_network_diagnostics_server/wifi_network_diagnostics_server.cpp
${CHIP_ROOT}/src/app/clusters/software_diagnostics_server/software_diagnostics_server.cpp
${CHIP_ROOT}/src/app/clusters/general-commissioning-server/general-commissioning-server.cpp
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning-ember.cpp
${CHIP_ROOT}/src/app/clusters/network-commissioning/network-commissioning.cpp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ idf_component_register(PRIV_INCLUDE_DIRS
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/basic"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/bindings"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/diagnostic-logs-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/ethernet_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/thread_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/wifi_network_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/software_diagnostics_server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/general-commissioning-server"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/network-commissioning"
"${CMAKE_SOURCE_DIR}/third_party/connectedhomeip/src/app/clusters/operational-credentials-server"
Expand Down
109 changes: 109 additions & 0 deletions src/app/AttributeAccessInterface.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
*
* Copyright (c) 2021 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.
*/

#pragma once

#include <app/ClusterInfo.h>
#include <app/util/basic-types.h>
#include <lib/core/CHIPTLV.h>
#include <lib/core/Optional.h>

/**
* Callback class that clusters can implement in order to interpose custom
* attribute-handling logic. An AttributeAccessInterface instance is associated
* with some specific cluster. A single instance may be used for a specific
* endpoint or for all endpoints.
*
* Instances of AttributeAccessInterface that are registered via
* registerAttributeAccessOverride will be consulted before taking the normal
* attribute access codepath and can use that codepath as a fallback if desired.
*/
namespace chip {
namespace app {

class AttributeAccessInterface
{
public:
/**
* aEndpointId can be Missing to indicate that this object is meant to be
* used with all endpoints.
*/
AttributeAccessInterface(Optional<EndpointId> aEndpointId, ClusterId aClusterId) :
mEndpointId(aEndpointId), mClusterId(aClusterId)
{}
virtual ~AttributeAccessInterface() {}

/**
* Callback for reading attributes.
*
* @param [in] aClusterInfo indicates which exact data is being read.
* @param [in] aTLVWriter the tlv writer to put the data into. The data
* must use the AttributeDataElement::kCsTag_Data
* context tag.
* @param [out] aDataRead whether we actually tried to provide data. If
* this function returns success and aDataRead is
* false, the AttributeAccessInterface did not try
* to provide any data. In this case, normal
* attribute access will happen for the read. This
* may involve reading from the attribute store or
* external attribute callbacks.
*/
virtual CHIP_ERROR Read(ClusterInfo & aClusterInfo, TLV::TLVWriter * aWriter, bool * aDataRead) = 0;

/**
* Mechanism for keeping track of a chain of AttributeAccessInterfaces.
*/
void SetNext(AttributeAccessInterface * aNext) { mNext = aNext; }
AttributeAccessInterface * GetNext() const { return mNext; }

/**
* Check whether a this AttributeAccessInterface is relevant for a
* particular endpoint+cluster. An AttributeAccessInterface will be used
* for a read from a particular cluster only when this function returns
* true.
*/
bool Matches(EndpointId aEndpointId, ClusterId aClusterId) const
{
return (!mEndpointId.HasValue() || mEndpointId.Value() == aEndpointId) && mClusterId == aClusterId;
}

/**
* Check whether an AttributeAccessInterface is relevant for a particular
* specific endpoint. This is used to clean up overrides registered for an
* endpoint that becomes disabled.
*/
bool MatchesExactly(EndpointId aEndpointId) const { return mEndpointId.HasValue() && mEndpointId.Value() == aEndpointId; }

/**
* Check whether another AttributeAccessInterface wants to handle the same set of
* attributes as we do.
*/
bool Matches(const AttributeAccessInterface & aOther) const
{
return mClusterId == aOther.mClusterId &&
(!mEndpointId.HasValue() || !aOther.mEndpointId.HasValue() || mEndpointId.Value() == aOther.mEndpointId.Value());
}

private:
Optional<EndpointId> mEndpointId;
ClusterId mClusterId;
AttributeAccessInterface * mNext = nullptr;
};

} // namespace app
} // namespace chip
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,89 @@
*/

#include <app-common/zap-generated/attributes/Accessors.h>
#include <app-common/zap-generated/ids/Attributes.h>
#include <app-common/zap-generated/ids/Clusters.h>
#include <app/AttributeAccessInterface.h>
#include <app/CommandHandler.h>
#include <app/MessageDef/AttributeDataElement.h>
#include <app/util/af.h>
#include <app/util/attribute-storage.h>
#include <lib/core/Optional.h>
#include <platform/ConnectivityManager.h>

using namespace chip;
using namespace chip::app;
using namespace chip::app::Clusters;
using namespace chip::app::Clusters::EthernetNetworkDiagnostics::Attributes;
using chip::DeviceLayer::ConnectivityManager;

namespace {

class EthernetDiagosticsAttrAccess : public AttributeAccessInterface
{
public:
// Register for the EthernetNetworkDiagnostics cluster on all endpoints.
EthernetDiagosticsAttrAccess() : AttributeAccessInterface(Optional<EndpointId>::Missing(), EthernetNetworkDiagnostics::Id) {}

CHIP_ERROR Read(ClusterInfo & aClusterInfo, TLV::TLVWriter * aWriter, bool * aDataRead) override;

private:
CHIP_ERROR ReadIfSupported(CHIP_ERROR (ConnectivityManager::*getter)(uint64_t &), TLV::TLVWriter * aWriter);
};

EthernetDiagosticsAttrAccess gAttrAccess;

CHIP_ERROR EthernetDiagosticsAttrAccess::Read(ClusterInfo & aClusterInfo, TLV::TLVWriter * aWriter, bool * aDataRead)
{
if (aClusterInfo.mClusterId != EthernetNetworkDiagnostics::Id)
{
// We shouldn't have been called at all.
return CHIP_ERROR_INVALID_ARGUMENT;
}

*aDataRead = true;
switch (aClusterInfo.mFieldId)
{
case Ids::PacketRxCount: {
return ReadIfSupported(&ConnectivityManager::GetEthPacketRxCount, aWriter);
}
case Ids::PacketTxCount: {
return ReadIfSupported(&ConnectivityManager::GetEthPacketTxCount, aWriter);
}
case Ids::TxErrCount: {
return ReadIfSupported(&ConnectivityManager::GetEthTxErrCount, aWriter);
}
case Ids::CollisionCount: {
return ReadIfSupported(&ConnectivityManager::GetEthCollisionCount, aWriter);
}
case Ids::OverrunCount: {
return ReadIfSupported(&ConnectivityManager::GetEthOverrunCount, aWriter);
}
default: {
*aDataRead = false;
break;
}
}
return CHIP_NO_ERROR;
}

CHIP_ERROR EthernetDiagosticsAttrAccess::ReadIfSupported(CHIP_ERROR (ConnectivityManager::*getter)(uint64_t &),
TLV::TLVWriter * aWriter)
{
uint64_t data;
CHIP_ERROR err = (DeviceLayer::ConnectivityMgr().*getter)(data);
if (err == CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE)
{
data = 0;
}
else if (err != CHIP_NO_ERROR)
{
return err;
}

return aWriter->Put(TLV::ContextTag(AttributeDataElement::kCsTag_Data), data);
}
} // anonymous namespace

bool emberAfEthernetNetworkDiagnosticsClusterResetCountsCallback(EndpointId endpoint, app::CommandHandler * commandObj)
{
Expand All @@ -43,3 +121,13 @@ bool emberAfEthernetNetworkDiagnosticsClusterResetCountsCallback(EndpointId endp
emberAfSendImmediateDefaultResponse(status);
return true;
}

void emberAfEthernetNetworkDiagnosticsClusterServerInitCallback(EndpointId endpoint)
{
static bool attrAccessRegistered = false;
if (!attrAccessRegistered)
{
registerAttributeAccessOverride(&gAttrAccess);
attrAccessRegistered = true;
}
}
Loading