blob: a7299b2d1f4663905830539576a31a751af38f5a [file] [log] [blame]
/*
*
* Copyright (c) 2022 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 <assert.h>
#include <app/clusters/bindings/PendingNotificationMap.h>
#include <app/util/binding-table.h>
#include <app/util/config.h>
namespace chip {
CHIP_ERROR PendingNotificationMap::FindLRUConnectPeer(ScopedNodeId & nodeId)
{
// When entries are added to PendingNotificationMap, they are appended to the end.
// To find the LRU peer, we need to find the peer whose last entry in the map is closer
// to the start of the list than the last entry of any other peer.
// First, set up a way to easily track which entries correspond to the same peer.
uint8_t bindingWithSamePeer[MATTER_BINDING_TABLE_SIZE];
for (auto iter = BindingTable::GetInstance().begin(); iter != BindingTable::GetInstance().end(); ++iter)
{
if (iter->type != MATTER_UNICAST_BINDING)
{
continue;
}
for (auto checkIter = BindingTable::GetInstance().begin(); checkIter != BindingTable::GetInstance().end(); ++checkIter)
{
if (checkIter->type == MATTER_UNICAST_BINDING && checkIter->fabricIndex == iter->fabricIndex &&
checkIter->nodeId == iter->nodeId)
{
bindingWithSamePeer[iter.GetIndex()] = checkIter.GetIndex();
break;
}
}
}
uint16_t lastAppear[MATTER_BINDING_TABLE_SIZE];
for (uint16_t & value : lastAppear)
{
value = UINT16_MAX;
}
uint16_t appearIndex = 0;
for (PendingNotificationEntry pendingNotification : *this)
{
lastAppear[bindingWithSamePeer[pendingNotification.mBindingEntryId]] = appearIndex;
appearIndex++;
}
uint8_t lruBindingEntryIndex;
uint16_t minLastAppearValue = UINT16_MAX;
for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++)
{
if (lastAppear[i] < minLastAppearValue)
{
lruBindingEntryIndex = i;
minLastAppearValue = lastAppear[i];
}
}
if (minLastAppearValue < UINT16_MAX)
{
EmberBindingTableEntry entry = BindingTable::GetInstance().GetAt(static_cast<uint8_t>(lruBindingEntryIndex));
nodeId = ScopedNodeId(entry.nodeId, entry.fabricIndex);
return CHIP_NO_ERROR;
}
return CHIP_ERROR_NOT_FOUND;
}
CHIP_ERROR PendingNotificationMap::AddPendingNotification(uint8_t bindingEntryId, PendingNotificationContext * context)
{
RemoveEntry(bindingEntryId);
if (mNumEntries == MATTER_BINDING_TABLE_SIZE)
{
// We know that the RemoveEntry above did not do anything so we don't need to try restoring it.
return CHIP_ERROR_NO_MEMORY;
}
mPendingBindingEntries[mNumEntries] = bindingEntryId;
mPendingContexts[mNumEntries] = context;
if (context)
{
context->IncrementConsumersNumber();
}
mNumEntries++;
return CHIP_NO_ERROR;
}
void PendingNotificationMap::RemoveEntry(uint8_t bindingEntryId)
{
uint8_t newEntryCount = 0;
for (int i = 0; i < mNumEntries; i++)
{
if (mPendingBindingEntries[i] != bindingEntryId)
{
mPendingBindingEntries[newEntryCount] = mPendingBindingEntries[i];
mPendingContexts[newEntryCount] = mPendingContexts[i];
newEntryCount++;
}
else if (mPendingContexts[i] != nullptr)
{
mPendingContexts[i]->DecrementConsumersNumber();
}
}
mNumEntries = newEntryCount;
}
void PendingNotificationMap::RemoveAllEntriesForNode(const ScopedNodeId & nodeId)
{
uint8_t newEntryCount = 0;
for (int i = 0; i < mNumEntries; i++)
{
EmberBindingTableEntry entry = BindingTable::GetInstance().GetAt(mPendingBindingEntries[i]);
if (entry.fabricIndex != nodeId.GetFabricIndex() || entry.nodeId != nodeId.GetNodeId())
{
mPendingBindingEntries[newEntryCount] = mPendingBindingEntries[i];
mPendingContexts[newEntryCount] = mPendingContexts[i];
newEntryCount++;
}
else if (mPendingContexts[i] != nullptr)
{
mPendingContexts[i]->DecrementConsumersNumber();
}
}
mNumEntries = newEntryCount;
}
void PendingNotificationMap::RemoveAllEntriesForFabric(FabricIndex fabric)
{
uint8_t newEntryCount = 0;
for (int i = 0; i < mNumEntries; i++)
{
EmberBindingTableEntry entry = BindingTable::GetInstance().GetAt(mPendingBindingEntries[i]);
if (entry.fabricIndex != fabric)
{
mPendingBindingEntries[newEntryCount] = mPendingBindingEntries[i];
mPendingContexts[newEntryCount] = mPendingContexts[i];
newEntryCount++;
}
else if (mPendingContexts[i] != nullptr)
{
mPendingContexts[i]->DecrementConsumersNumber();
}
}
mNumEntries = newEntryCount;
}
} // namespace chip