| /* |
| * 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. |
| */ |
| |
| /** |
| * @file |
| * This file defines the Matter Group message counters of remote nodes for groups. |
| * |
| */ |
| |
| #include <lib/support/DefaultStorageKeyAllocator.h> |
| #include <transport/GroupPeerMessageCounter.h> |
| |
| namespace chip { |
| namespace Transport { |
| |
| CHIP_ERROR GroupPeerTable::FindOrAddPeer(FabricIndex fabricIndex, NodeId nodeId, bool isControl, |
| chip::Transport::PeerMessageCounter *& counter) |
| { |
| if (fabricIndex == kUndefinedFabricIndex || nodeId == kUndefinedNodeId) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| for (auto & groupFabric : mGroupFabrics) |
| { |
| if (groupFabric.mFabricIndex == kUndefinedFabricIndex) |
| { |
| // Already iterated through all known fabricIndex |
| // Add the new peer to save some processing time |
| groupFabric.mFabricIndex = fabricIndex; |
| if (isControl) |
| { |
| groupFabric.mControlGroupSenders[0].mNodeId = nodeId; |
| counter = &(groupFabric.mControlGroupSenders[0].msgCounter); |
| groupFabric.mControlPeerCount++; |
| } |
| else |
| { |
| groupFabric.mDataGroupSenders[0].mNodeId = nodeId; |
| counter = &(groupFabric.mDataGroupSenders[0].msgCounter); |
| groupFabric.mDataPeerCount++; |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| if (fabricIndex == groupFabric.mFabricIndex) |
| { |
| if (isControl) |
| { |
| for (auto & node : groupFabric.mControlGroupSenders) |
| { |
| if (node.mNodeId == kUndefinedNodeId) |
| { |
| // Already iterated through all known NodeIds |
| // Add the new peer to save some processing time |
| node.mNodeId = nodeId; |
| counter = &(node.msgCounter); |
| groupFabric.mControlPeerCount++; |
| return CHIP_NO_ERROR; |
| } |
| |
| if (node.mNodeId == nodeId) |
| { |
| counter = &(node.msgCounter); |
| return CHIP_NO_ERROR; |
| } |
| } |
| } |
| else |
| { |
| for (auto & node : groupFabric.mDataGroupSenders) |
| { |
| if (node.mNodeId == kUndefinedNodeId) |
| { |
| // Already iterated through all known NodeIds |
| // Add the new peer to save some processing time |
| node.mNodeId = nodeId; |
| counter = &(node.msgCounter); |
| groupFabric.mDataPeerCount++; |
| return CHIP_NO_ERROR; |
| } |
| |
| if (node.mNodeId == nodeId) |
| { |
| counter = &(node.msgCounter); |
| return CHIP_NO_ERROR; |
| } |
| } |
| } |
| // Exceeded the Max number of Group peers |
| return CHIP_ERROR_TOO_MANY_PEER_NODES; |
| } |
| } |
| |
| // Exceeded the Max number of Group peers |
| return CHIP_ERROR_TOO_MANY_PEER_NODES; |
| } |
| |
| // Used in case of MCSP failure |
| CHIP_ERROR GroupPeerTable::RemovePeer(FabricIndex fabricIndex, NodeId nodeId, bool isControl) |
| { |
| CHIP_ERROR err = CHIP_ERROR_NOT_FOUND; |
| uint32_t fabricIt = CHIP_CONFIG_MAX_FABRICS; |
| |
| if (fabricIndex == kUndefinedFabricIndex || nodeId == kUndefinedNodeId) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| for (uint32_t it = 0; it < CHIP_CONFIG_MAX_FABRICS; it++) |
| { |
| if (fabricIndex == mGroupFabrics[it].mFabricIndex) |
| { |
| if (isControl) |
| { |
| if (RemoveSpecificPeer(mGroupFabrics[it].mControlGroupSenders, nodeId, CHIP_CONFIG_MAX_GROUP_CONTROL_PEERS)) |
| { |
| fabricIt = it; |
| mGroupFabrics[it].mControlPeerCount--; |
| err = CHIP_NO_ERROR; |
| } |
| } |
| else |
| { |
| if (RemoveSpecificPeer(mGroupFabrics[it].mDataGroupSenders, nodeId, CHIP_CONFIG_MAX_GROUP_DATA_PEERS)) |
| { |
| fabricIt = it; |
| mGroupFabrics[it].mDataPeerCount--; |
| err = CHIP_NO_ERROR; |
| } |
| } |
| break; |
| } |
| } |
| |
| // Remove Fabric entry from PeerTable if empty |
| if (fabricIt < CHIP_CONFIG_MAX_FABRICS) |
| { |
| if (mGroupFabrics[fabricIt].mDataPeerCount == 0 && mGroupFabrics[fabricIt].mControlPeerCount == 0) |
| { |
| RemoveAndCompactFabric(fabricIt); |
| } |
| } |
| |
| // Cannot find Peer to remove |
| return err; |
| } |
| |
| CHIP_ERROR GroupPeerTable::FabricRemoved(FabricIndex fabricIndex) |
| { |
| CHIP_ERROR err = CHIP_ERROR_NOT_FOUND; |
| |
| if (fabricIndex == kUndefinedFabricIndex) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| for (uint32_t it = 0; it < CHIP_CONFIG_MAX_FABRICS; it++) |
| { |
| if (fabricIndex == mGroupFabrics[it].mFabricIndex) |
| { |
| RemoveAndCompactFabric(it); |
| return CHIP_NO_ERROR; |
| } |
| } |
| |
| // Cannot find Fabric to remove |
| return err; |
| } |
| |
| bool GroupPeerTable::RemoveSpecificPeer(GroupSender * list, NodeId nodeId, uint32_t size) |
| { |
| bool removed = false; |
| for (uint32_t nodeIt = 0; nodeIt < size; nodeIt++) |
| { |
| if (list[nodeIt].mNodeId == nodeId) |
| { |
| list[nodeIt].mNodeId = kUndefinedNodeId; |
| list[nodeIt].msgCounter.Reset(); |
| removed = true; |
| break; |
| } |
| } |
| |
| if (removed) |
| { |
| CompactPeers(list, size); |
| } |
| |
| return removed; |
| } |
| |
| void GroupPeerTable::CompactPeers(GroupSender * list, uint32_t size) |
| { |
| if (list == nullptr || size == 0) |
| { |
| return; |
| } |
| |
| for (uint32_t peerIndex = 0; peerIndex < size; peerIndex++) |
| { |
| if (list[peerIndex].mNodeId != kUndefinedNodeId) |
| { |
| continue; |
| } |
| |
| for (uint32_t i = (size - 1); i > peerIndex; i--) |
| { |
| if (list[i].mNodeId != kUndefinedNodeId) |
| { |
| // Logic works since all buffer are static |
| // move it up front |
| new (&list[peerIndex]) GroupSender(list[i]); |
| new (&list[i]) GroupSender(); |
| break; |
| } |
| } |
| } |
| } |
| |
| void GroupPeerTable::RemoveAndCompactFabric(uint32_t tableIndex) |
| { |
| if (tableIndex >= CHIP_CONFIG_MAX_FABRICS) |
| { |
| return; |
| } |
| mGroupFabrics[tableIndex].mFabricIndex = kUndefinedFabricIndex; |
| new (&mGroupFabrics[tableIndex]) GroupFabric(); |
| |
| // To maintain logic integrity Fabric array cannot have empty slot in between data |
| // Find the last non empty element |
| for (uint32_t i = CHIP_CONFIG_MAX_FABRICS - 1; i > tableIndex; i--) |
| { |
| if (mGroupFabrics[i].mFabricIndex != kUndefinedFabricIndex) |
| { |
| // Logic works since all buffer are static |
| // move it up front |
| new (&mGroupFabrics[tableIndex]) GroupFabric(mGroupFabrics[i]); |
| new (&mGroupFabrics[i]) GroupFabric(); |
| break; |
| } |
| } |
| } |
| |
| GroupOutgoingCounters::GroupOutgoingCounters(chip::PersistentStorageDelegate * storage_delegate) |
| { |
| Init(storage_delegate); |
| } |
| |
| CHIP_ERROR GroupOutgoingCounters::Init(chip::PersistentStorageDelegate * storage_delegate) |
| { |
| |
| if (storage_delegate == nullptr) |
| { |
| return CHIP_ERROR_INVALID_ARGUMENT; |
| } |
| |
| // TODO Implement Logic for first time use / factory reset to be random |
| // Spec 4.5.1.3 |
| mStorage = storage_delegate; |
| uint16_t size = static_cast<uint16_t>(sizeof(uint32_t)); |
| uint32_t temp; |
| CHIP_ERROR err; |
| err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::GroupControlCounter().KeyName(), &temp, size); |
| if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| // might be the first time we retrieve the value |
| // TODO handle this case |
| mGroupControlCounter = 0; // TODO should be random |
| } |
| else if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| else |
| { |
| mGroupControlCounter = temp; |
| } |
| |
| err = mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::GroupDataCounter().KeyName(), &temp, size); |
| if (err == CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND) |
| { |
| // might be the first time we retrieve the value |
| // TODO handle this case |
| mGroupDataCounter = 0; // TODO should be random |
| } |
| else if (err != CHIP_NO_ERROR) |
| { |
| return err; |
| } |
| else |
| { |
| mGroupDataCounter = temp; |
| } |
| |
| temp = mGroupControlCounter + GROUP_MSG_COUNTER_MIN_INCREMENT; |
| size = static_cast<uint16_t>(sizeof(temp)); |
| ReturnErrorOnFailure(mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::GroupControlCounter().KeyName(), &temp, size)); |
| |
| temp = mGroupDataCounter + GROUP_MSG_COUNTER_MIN_INCREMENT; |
| |
| return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::GroupDataCounter().KeyName(), &temp, size); |
| } |
| |
| uint32_t GroupOutgoingCounters::GetCounter(bool isControl) |
| { |
| return (isControl) ? mGroupControlCounter : mGroupDataCounter; |
| } |
| |
| CHIP_ERROR GroupOutgoingCounters::IncrementCounter(bool isControl) |
| { |
| uint32_t temp = 0; |
| uint16_t size = static_cast<uint16_t>(sizeof(uint32_t)); |
| uint32_t value = 0; |
| |
| StorageKeyName key = StorageKeyName::Uninitialized(); |
| |
| if (isControl) |
| { |
| mGroupControlCounter++; |
| key = DefaultStorageKeyAllocator::GroupControlCounter(); |
| value = mGroupControlCounter; |
| } |
| else |
| { |
| mGroupDataCounter++; |
| key = DefaultStorageKeyAllocator::GroupDataCounter(); |
| value = mGroupDataCounter; |
| } |
| |
| if (mStorage == nullptr) |
| { |
| return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND; |
| } |
| |
| ReturnErrorOnFailure(mStorage->SyncGetKeyValue(key.KeyName(), &temp, size)); |
| if (temp == value) |
| { |
| temp = value + GROUP_MSG_COUNTER_MIN_INCREMENT; |
| return mStorage->SyncSetKeyValue(key.KeyName(), &temp, sizeof(uint32_t)); |
| } |
| return CHIP_NO_ERROR; |
| } |
| |
| } // namespace Transport |
| } // namespace chip |