Line data Source code
1 : /** 2 : * 3 : * Copyright (c) 2020 Project CHIP Authors 4 : * 5 : * Licensed under the Apache License, Version 2.0 (the "License"); 6 : * you may not use this file except in compliance with the License. 7 : * You may obtain a copy of the License at 8 : * 9 : * http://www.apache.org/licenses/LICENSE-2.0 10 : * 11 : * Unless required by applicable law or agreed to in writing, software 12 : * distributed under the License is distributed on an "AS IS" BASIS, 13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 : * See the License for the specific language governing permissions and 15 : * limitations under the License. 16 : */ 17 : 18 : /** 19 : * @file Basic implementation of a binding table. 20 : */ 21 : 22 : #include <app/util/binding-table.h> 23 : #include <app/util/config.h> 24 : 25 : namespace chip { 26 : 27 : BindingTable BindingTable::sInstance; 28 : 29 1169 : BindingTable::BindingTable() 30 : { 31 59 : memset(mNextIndex, kNextNullIndex, sizeof(mNextIndex)); 32 59 : } 33 : 34 89 : CHIP_ERROR BindingTable::Add(const EmberBindingTableEntry & entry) 35 : { 36 89 : if (entry.type == MATTER_UNUSED_BINDING) 37 : { 38 1 : return CHIP_ERROR_INVALID_ARGUMENT; 39 : } 40 88 : uint8_t newIndex = GetNextAvaiableIndex(); 41 88 : if (newIndex >= MATTER_BINDING_TABLE_SIZE) 42 : { 43 1 : return CHIP_ERROR_NO_MEMORY; 44 : } 45 87 : mBindingTable[newIndex] = entry; 46 87 : CHIP_ERROR error = SaveEntryToStorage(newIndex, kNextNullIndex); 47 87 : if (error == CHIP_NO_ERROR) 48 : { 49 86 : if (mTail == kNextNullIndex) 50 : { 51 6 : error = SaveListInfo(newIndex); 52 : } 53 : else 54 : { 55 80 : error = SaveEntryToStorage(mTail, newIndex); 56 : } 57 86 : if (error != CHIP_NO_ERROR) 58 : { 59 0 : mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(newIndex).KeyName()); 60 : } 61 : } 62 87 : if (error != CHIP_NO_ERROR) 63 : { 64 : // Roll back 65 1 : mBindingTable[newIndex].type = MATTER_UNUSED_BINDING; 66 1 : return error; 67 : } 68 : 69 86 : if (mTail == kNextNullIndex) 70 : { 71 6 : mTail = newIndex; 72 6 : mHead = newIndex; 73 : } 74 : else 75 : { 76 80 : mNextIndex[mTail] = newIndex; 77 80 : mNextIndex[newIndex] = kNextNullIndex; 78 80 : mTail = newIndex; 79 : } 80 : 81 86 : mSize++; 82 86 : return CHIP_NO_ERROR; 83 : } 84 : 85 56 : const EmberBindingTableEntry & BindingTable::GetAt(uint8_t index) 86 : { 87 56 : return mBindingTable[index]; 88 : } 89 : 90 170 : CHIP_ERROR BindingTable::SaveEntryToStorage(uint8_t index, uint8_t nextIndex) 91 : { 92 170 : EmberBindingTableEntry & entry = mBindingTable[index]; 93 170 : uint8_t buffer[kEntryStorageSize] = { 0 }; 94 170 : TLV::TLVWriter writer; 95 170 : writer.Init(buffer); 96 : TLV::TLVType container; 97 170 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::TLVType::kTLVType_Structure, container)); 98 170 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagFabricIndex), entry.fabricIndex)); 99 170 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagLocalEndpoint), entry.local)); 100 170 : if (entry.clusterId.HasValue()) 101 : { 102 82 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagCluster), entry.clusterId.Value())); 103 : } 104 170 : if (entry.type == MATTER_UNICAST_BINDING) 105 : { 106 167 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagRemoteEndpoint), entry.remote)); 107 167 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagNodeId), entry.nodeId)); 108 : } 109 : else 110 : { 111 3 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagGroupId), entry.groupId)); 112 : } 113 170 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagNextEntry), nextIndex)); 114 170 : ReturnErrorOnFailure(writer.EndContainer(container)); 115 170 : ReturnErrorOnFailure(writer.Finalize()); 116 340 : return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(index).KeyName(), buffer, 117 340 : static_cast<uint16_t>(writer.GetLengthWritten())); 118 : } 119 : 120 30 : CHIP_ERROR BindingTable::SaveListInfo(uint8_t head) 121 : { 122 30 : uint8_t buffer[kListInfoStorageSize] = { 0 }; 123 30 : TLV::TLVWriter writer; 124 30 : writer.Init(buffer); 125 : TLV::TLVType container; 126 30 : ReturnErrorOnFailure(writer.StartContainer(TLV::AnonymousTag(), TLV::TLVType::kTLVType_Structure, container)); 127 30 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagStorageVersion), kStorageVersion)); 128 30 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagHead), head)); 129 30 : ReturnErrorOnFailure(writer.EndContainer(container)); 130 30 : ReturnErrorOnFailure(writer.Finalize()); 131 60 : return mStorage->SyncSetKeyValue(DefaultStorageKeyAllocator::BindingTable().KeyName(), buffer, 132 60 : static_cast<uint16_t>(writer.GetLengthWritten())); 133 : } 134 : 135 6 : CHIP_ERROR BindingTable::LoadFromStorage() 136 : { 137 6 : VerifyOrReturnError(mStorage != nullptr, CHIP_ERROR_INCORRECT_STATE); 138 6 : uint8_t buffer[kListInfoStorageSize] = { 0 }; 139 6 : uint16_t size = sizeof(buffer); 140 : CHIP_ERROR error; 141 : 142 6 : ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::BindingTable().KeyName(), buffer, size)); 143 : TLV::TLVReader reader; 144 6 : reader.Init(buffer, size); 145 : 146 6 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); 147 : 148 : TLV::TLVType container; 149 6 : ReturnErrorOnFailure(reader.EnterContainer(container)); 150 : 151 6 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagStorageVersion))); 152 : uint32_t version; 153 6 : ReturnErrorOnFailure(reader.Get(version)); 154 6 : VerifyOrReturnError(version == kStorageVersion, CHIP_ERROR_VERSION_MISMATCH); 155 6 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagHead))); 156 : uint8_t index; 157 6 : ReturnErrorOnFailure(reader.Get(index)); 158 6 : mHead = index; 159 27 : while (index != kNextNullIndex) 160 : { 161 : uint8_t nextIndex; 162 21 : error = LoadEntryFromStorage(index, nextIndex); 163 21 : if (error != CHIP_NO_ERROR) 164 : { 165 0 : mHead = kNextNullIndex; 166 0 : mTail = kNextNullIndex; 167 0 : return error; 168 : } 169 21 : mTail = index; 170 21 : index = nextIndex; 171 21 : mSize++; 172 : } 173 6 : error = reader.ExitContainer(container); 174 6 : if (error != CHIP_NO_ERROR) 175 : { 176 0 : mHead = kNextNullIndex; 177 0 : mTail = kNextNullIndex; 178 : } 179 6 : return error; 180 : } 181 : 182 21 : CHIP_ERROR BindingTable::LoadEntryFromStorage(uint8_t index, uint8_t & nextIndex) 183 : { 184 21 : uint8_t buffer[kEntryStorageSize] = { 0 }; 185 21 : uint16_t size = sizeof(buffer); 186 21 : EmberBindingTableEntry entry; 187 : 188 21 : ReturnErrorOnFailure(mStorage->SyncGetKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(index).KeyName(), buffer, size)); 189 : TLV::TLVReader reader; 190 21 : reader.Init(buffer, size); 191 : 192 21 : ReturnErrorOnFailure(reader.Next(TLV::kTLVType_Structure, TLV::AnonymousTag())); 193 : 194 : TLV::TLVType container; 195 21 : ReturnErrorOnFailure(reader.EnterContainer(container)); 196 21 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagFabricIndex))); 197 21 : ReturnErrorOnFailure(reader.Get(entry.fabricIndex)); 198 21 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagLocalEndpoint))); 199 21 : ReturnErrorOnFailure(reader.Get(entry.local)); 200 21 : ReturnErrorOnFailure(reader.Next()); 201 21 : if (reader.GetTag() == TLV::ContextTag(kTagCluster)) 202 : { 203 : ClusterId clusterId; 204 12 : ReturnErrorOnFailure(reader.Get(clusterId)); 205 12 : entry.clusterId.SetValue(clusterId); 206 12 : ReturnErrorOnFailure(reader.Next()); 207 : } 208 : else 209 : { 210 9 : entry.clusterId = NullOptional; 211 : } 212 21 : if (reader.GetTag() == TLV::ContextTag(kTagRemoteEndpoint)) 213 : { 214 10 : entry.type = MATTER_UNICAST_BINDING; 215 10 : ReturnErrorOnFailure(reader.Get(entry.remote)); 216 10 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagNodeId))); 217 10 : ReturnErrorOnFailure(reader.Get(entry.nodeId)); 218 : } 219 : else 220 : { 221 11 : entry.type = MATTER_MULTICAST_BINDING; 222 11 : ReturnErrorCodeIf(reader.GetTag() != TLV::ContextTag(kTagGroupId), CHIP_ERROR_INVALID_TLV_TAG); 223 11 : ReturnErrorOnFailure(reader.Get(entry.groupId)); 224 : } 225 21 : ReturnErrorOnFailure(reader.Next(TLV::ContextTag(kTagNextEntry))); 226 21 : ReturnErrorOnFailure(reader.Get(nextIndex)); 227 21 : ReturnErrorOnFailure(reader.ExitContainer(container)); 228 21 : mBindingTable[index] = entry; 229 21 : mNextIndex[index] = nextIndex; 230 21 : return CHIP_NO_ERROR; 231 21 : } 232 : 233 27 : CHIP_ERROR BindingTable::RemoveAt(Iterator & iter) 234 : { 235 : CHIP_ERROR error; 236 27 : if (iter.mTable != this || iter.mIndex == kNextNullIndex) 237 : { 238 0 : return CHIP_ERROR_INVALID_ARGUMENT; 239 : } 240 27 : if (iter.mIndex == mTail) 241 : { 242 2 : mTail = iter.mPrevIndex; 243 : } 244 27 : uint8_t next = mNextIndex[iter.mIndex]; 245 27 : if (iter.mIndex != mHead) 246 : { 247 3 : error = SaveEntryToStorage(iter.mPrevIndex, next); 248 3 : if (error == CHIP_NO_ERROR) 249 : { 250 2 : mNextIndex[iter.mPrevIndex] = next; 251 : } 252 : } 253 : else 254 : { 255 24 : error = SaveListInfo(next); 256 24 : if (error == CHIP_NO_ERROR) 257 : { 258 23 : mHead = next; 259 : } 260 : } 261 27 : if (error == CHIP_NO_ERROR) 262 : { 263 : // The remove is considered "submitted" once the change on prev node takes effect 264 25 : if (mStorage->SyncDeleteKeyValue(DefaultStorageKeyAllocator::BindingTableEntry(iter.mIndex).KeyName()) != CHIP_NO_ERROR) 265 : { 266 0 : ChipLogError(AppServer, "Failed to remove binding table entry %u from storage", iter.mIndex); 267 : } 268 25 : mBindingTable[iter.mIndex].type = MATTER_UNUSED_BINDING; 269 25 : mNextIndex[iter.mIndex] = kNextNullIndex; 270 25 : mSize--; 271 : } 272 27 : iter.mIndex = next; 273 27 : return error; 274 : } 275 : 276 88 : BindingTable::Iterator BindingTable::begin() 277 : { 278 : Iterator iter; 279 88 : iter.mTable = this; 280 88 : iter.mPrevIndex = kNextNullIndex; 281 88 : iter.mIndex = mHead; 282 88 : return iter; 283 : } 284 : 285 635 : BindingTable::Iterator BindingTable::end() 286 : { 287 : Iterator iter; 288 635 : iter.mTable = this; 289 635 : iter.mIndex = kNextNullIndex; 290 635 : return iter; 291 : } 292 : 293 88 : uint8_t BindingTable::GetNextAvaiableIndex() 294 : { 295 879 : for (uint8_t i = 0; i < MATTER_BINDING_TABLE_SIZE; i++) 296 : { 297 878 : if (mBindingTable[i].type == MATTER_UNUSED_BINDING) 298 : { 299 87 : return i; 300 : } 301 : } 302 1 : return MATTER_BINDING_TABLE_SIZE; 303 : } 304 : 305 558 : BindingTable::Iterator BindingTable::Iterator::operator++() 306 : { 307 558 : if (mIndex != kNextNullIndex) 308 : { 309 558 : mPrevIndex = mIndex; 310 558 : mIndex = mTable->mNextIndex[mIndex]; 311 : } 312 558 : return *this; 313 : } 314 : 315 : } // namespace chip