Skip to content

Commit

Permalink
[app] add binding cluster support
Browse files Browse the repository at this point in the history
The change adds `BindingManager` class for managing the connections to
bound devices and forward events on bound clusters to the application.
  • Loading branch information
gjc13 committed Dec 14, 2021
1 parent 157de4f commit 2fab02a
Show file tree
Hide file tree
Showing 13 changed files with 521 additions and 44 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
*
* Copyright (c) 2021 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/clusters/bindings/BindingManager.h"

CHIP_ERROR InitBindingHandlers();
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
*
* Copyright (c) 2021 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 "binding-handler.h"

#include "app-common/zap-generated/attribute-id.h"
#include "app-common/zap-generated/cluster-id.h"
#include "app-common/zap-generated/command-id.h"
#include "app/CommandSender.h"
#include "app/clusters/bindings/BindingManager.h"
#include "app/server/Server.h"
#include "app/util/af.h"
#include "lib/core/CHIPError.h"

static void BoundDeviceChangedHandler(chip::EndpointId localEndpoint, chip::EndpointId remoteEndpoint, chip::ClusterId clusterId,
chip::OperationalDeviceProxy * peer_device)
{
using namespace chip;
using namespace chip::app;
// Unfortunately generating both cluster server and client code is not supported.
// We need to manually compose the packet here.
// TODO: investigate code generation issue for binding
if (localEndpoint == 1 && clusterId == ZCL_ON_OFF_CLUSTER_ID)
{
uint8_t onOffValue;
emberAfReadServerAttribute(localEndpoint, clusterId, ZCL_ON_OFF_ATTRIBUTE_ID, &onOffValue, sizeof(onOffValue));
CommandId command = onOffValue ? Clusters::OnOff::Commands::On::Id : Clusters::OnOff::Commands::Off::Id;
CommandPathParams cmdParams = { remoteEndpoint, /* group id */ 0, clusterId, command,
(chip::app::CommandPathFlags::kEndpointIdValid) };
CommandSender sender(nullptr, peer_device->GetExchangeManager());
sender.PrepareCommand(cmdParams);
sender.FinishCommand();
peer_device->SendCommands(&sender);
}
}

CHIP_ERROR InitBindingHandlers()
{
chip::BindingManager::GetInstance().SetAppServer(&chip::Server::GetInstance());
chip::BindingManager::GetInstance().RegisterBoundDeviceChangedHandler(BoundDeviceChangedHandler);
return CHIP_NO_ERROR;
}
1 change: 1 addition & 0 deletions examples/all-clusters-app/linux/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni")

executable("chip-all-clusters-app") {
sources = [
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/binding-handler.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/bridged-actions-stub.cpp",
"${chip_root}/examples/all-clusters-app/all-clusters-common/src/static-supported-modes-manager.cpp",
"include/tv-callbacks.cpp",
Expand Down
2 changes: 2 additions & 0 deletions examples/all-clusters-app/linux/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <app/util/af.h>

#include "AppMain.h"
#include "binding-handler.h"

bool emberAfBasicClusterMfgSpecificPingCallback(chip::app::CommandHandler * commandObj)
{
Expand Down Expand Up @@ -71,6 +72,7 @@ static Identify gIdentify1 = {
int main(int argc, char * argv[])
{
VerifyOrDie(ChipLinuxAppInit(argc, argv) == 0);
VerifyOrDie(InitBindingHandlers() == CHIP_NO_ERROR);
ChipLinuxAppMainLoop();
return 0;
}
2 changes: 2 additions & 0 deletions src/app/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ static_library("app") {
"AttributePathExpandIterator.h",
"AttributePathParams.h",
"BufferedReadCallback.cpp",
"BindingCallbackStub.cpp",
"CASEClient.h",
"CASEClient.cpp",
"CASEClient.h",
"CASEClientPool.h",
Expand Down
24 changes: 24 additions & 0 deletions src/app/BindingCallbackStub.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
*
* Copyright (c) 2021 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/ConcreteAttributePath.h>

// The stub callback for devices without binding server enabled.
// The functional callback is at app/clusters/bindings/bindings.cpp
void __attribute__((weak))
MatterBindingClusterServerAttributeChangedCallback(const chip::app::ConcreteAttributePath & attributePath)
{}
1 change: 1 addition & 0 deletions src/app/CASESessionManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ void CASESessionManager::OnSessionReleased(SessionHandle sessionHandle)
VerifyOrReturn(session != nullptr, ChipLogDetail(Controller, "OnSessionReleased was called for unknown device, ignoring it."));

session->OnSessionReleased(sessionHandle);
ReleaseSession(session);
}

OperationalDeviceProxy * CASESessionManager::FindSession(SessionHandle session)
Expand Down
6 changes: 6 additions & 0 deletions src/app/chip_data_model.gni
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,12 @@ template("chip_data_model") {
"${_app_root}/util/ContentApp.cpp",
"${_app_root}/util/ContentAppPlatform.cpp",
]
} else if (cluster == "bindings") {
sources += [
"${_app_root}/clusters/${cluster}/${cluster}.cpp",
"${_app_root}/clusters/${cluster}/BindingManager.cpp",
"${_app_root}/clusters/${cluster}/BindingManager.h",
]
} else {
sources += [ "${_app_root}/clusters/${cluster}/${cluster}.cpp" ]
}
Expand Down
193 changes: 193 additions & 0 deletions src/app/clusters/bindings/BindingManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
/*
*
* Copyright (c) 2021 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/clusters/bindings/BindingManager.h>
#include <app/util/binding-table.h>
#include <bits/stdint-uintn.h>

namespace chip {

BindingManager BindingManager::sBindingManager;

CHIP_ERROR BindingManager::CreateBinding(FabricIndex fabric, NodeId node, EndpointId localEndpoint, ClusterId cluster)
{
ReturnErrorCodeIf(mAppServer == nullptr, CHIP_ERROR_INCORRECT_STATE);

return EnqueueClusterAndConnect(fabric, node, localEndpoint, cluster);
}

CHIP_ERROR BindingManager::EnqueueClusterAndConnect(FabricIndex fabric, NodeId node, EndpointId endpoint, ClusterId cluster)
{
FabricInfo * fabricInfo = mAppServer->GetFabricTable().FindFabricWithIndex(fabric);
PeerId peer = fabricInfo->GetPeerIdForNode(node);
CHIP_ERROR error = mPendingClusterMap.AddPendingCluster(peer, endpoint, cluster);
// We shouldn't fail to create the PendingClusterEntry after Binding Table size check
if (error != CHIP_NO_ERROR)
{
ChipLogError(AppServer, "Failed to create PendingClusterEntry");
return error;
}

error = mAppServer->GetCASESessionManager()->FindOrEstablishSession(fabricInfo, node, &mOnConnectedCallback,
&mOnConnectionFailureCallback);
if (error == CHIP_ERROR_NO_MEMORY)
{
// Release the least recently used entry
PendingClusterEntry * entry = mPendingClusterMap.FindLRUEntry();
if (entry != nullptr)
{
mAppServer->GetCASESessionManager()->ReleaseSession(entry->mPeerId.GetNodeId());
mPendingClusterMap.RemoveEntry(entry);
// Now retry
error = mAppServer->GetCASESessionManager()->FindOrEstablishSession(fabricInfo, node, &mOnConnectedCallback,
&mOnConnectionFailureCallback);
}
}
return error;
}

void BindingManager::HandleDeviceConnected(void * context, OperationalDeviceProxy * device)
{
BindingManager * manager = static_cast<BindingManager *>(context);
manager->HandleDeviceConnected(device);
}

void BindingManager::HandleDeviceConnected(OperationalDeviceProxy * device)
{
mPendingClusterMap.ForEachActiveObject([&](PendingClusterEntry * entry) -> Loop {
if (entry->mPeerId == device->GetPeerId())
{
SyncPendingClustersToPeer(device, entry);
}

return Loop::Continue;
});
}

void BindingManager::SyncPendingClustersToPeer(OperationalDeviceProxy * device, PendingClusterEntry * pendingClusters)
{
for (uint8_t i = 0; i < pendingClusters->mNumPendingClusters; i++)
{
ClusterId cluster = pendingClusters->mPendingClusters[i].cluster;
EndpointId endpoint = pendingClusters->mPendingClusters[i].endpoint;
for (uint8_t j = 0; j < EMBER_BINDING_TABLE_SIZE; j++)
{
EmberBindingTableEntry entry;
if (emberGetBinding(j, &entry) == EMBER_SUCCESS && entry.type != EMBER_UNUSED_BINDING && entry.clusterId == cluster &&
entry.local == endpoint && mBoundDeviceChangedHandler)
{
mBoundDeviceChangedHandler(entry.local, entry.remote, cluster, device);
}
}
}
mPendingClusterMap.RemoveEntry(pendingClusters);
}

void BindingManager::HandleDeviceConnectionFailure(void * context, NodeId nodeId, CHIP_ERROR error)
{
BindingManager * manager = static_cast<BindingManager *>(context);
manager->HandleDeviceConnectionFailure(nodeId, error);
}

void BindingManager::HandleDeviceConnectionFailure(NodeId nodeId, CHIP_ERROR error)
{
// Simply release the entry, the connection will be re-established on need.
ChipLogError(AppServer, "Failed to establish connection to node 0x" ChipLogFormatX64, ChipLogValueX64(nodeId));
mAppServer->GetCASESessionManager()->ReleaseSession(nodeId);
}

CHIP_ERROR BindingManager::DisconnectDevice(FabricIndex fabric, NodeId node)
{
FabricInfo * fabricInfo = mAppServer->GetFabricTable().FindFabricWithIndex(fabric);
PendingClusterEntry * entry = mPendingClusterMap.FindEntry(fabricInfo->GetPeerIdForNode(node));

VerifyOrReturnError(entry != nullptr, CHIP_ERROR_NOT_FOUND);

mAppServer->GetCASESessionManager()->ReleaseSession(node);
mPendingClusterMap.RemoveEntry(entry);
return CHIP_NO_ERROR;
}

CHIP_ERROR BindingManager::NotifyBoundClusterChanged(EndpointId endpoint, ClusterId cluster)
{
for (uint8_t i = 0; i < EMBER_BINDING_TABLE_SIZE; i++)
{
EmberBindingTableEntry entry;
CHIP_ERROR error = CHIP_NO_ERROR;

if (emberGetBinding(i, &entry) == EMBER_SUCCESS && entry.type != EMBER_UNUSED_BINDING && entry.local == endpoint &&
entry.clusterId == cluster)
{
OperationalDeviceProxy * peerDevice = mAppServer->GetCASESessionManager()->FindExistingSession(entry.nodeId);
if (peerDevice != nullptr && mBoundDeviceChangedHandler)
{
// We already have an active connection
mBoundDeviceChangedHandler(entry.local, entry.remote, cluster, peerDevice);
}
else
{
// Enqueue pending cluster and establish connection
error = EnqueueClusterAndConnect(entry.fabricIndex, entry.nodeId, entry.local, entry.clusterId);
}
}
}
return CHIP_NO_ERROR;
}

BindingManager::PendingClusterEntry * BindingManager::PendingClusterMap::FindLRUEntry()
{
PendingClusterEntry * lruEntry = nullptr;
mPendingClusterMap.ForEachActiveObject([&](PendingClusterEntry * entry) {
if (lruEntry == nullptr || lruEntry->mLastUpdateTime > entry->mLastUpdateTime)
{
lruEntry = entry;
}
return Loop::Continue;
});
return lruEntry;
}

BindingManager::PendingClusterEntry * BindingManager::PendingClusterMap::FindEntry(PeerId peerId)
{
PendingClusterEntry * foundEntry = nullptr;
mPendingClusterMap.ForEachActiveObject([&](PendingClusterEntry * entry) {
if (entry->mPeerId == peerId)
{
foundEntry = entry;
return Loop::Break;
}
return Loop::Continue;
});
return foundEntry;
}

CHIP_ERROR BindingManager::PendingClusterMap::AddPendingCluster(PeerId peer, EndpointId endpoint, ClusterId cluster)
{
PendingClusterEntry * entry = FindEntry(peer);

if (entry == nullptr)
{
entry = mPendingClusterMap.CreateObject();
VerifyOrReturnError(entry != nullptr, CHIP_ERROR_NO_MEMORY);
entry->mPeerId = peer;
}
entry->AddPendingCluster(endpoint, cluster);
entry->mLastUpdateTime = System::SystemClock().GetMonotonicTimestamp();
return CHIP_NO_ERROR;
}

} // namespace chip
Loading

0 comments on commit 2fab02a

Please sign in to comment.