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 1327 : BindingTable::BindingTable()
30 : {
31 67 : memset(mNextIndex, kNextNullIndex, sizeof(mNextIndex));
32 67 : }
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.has_value())
101 : {
102 82 : ReturnErrorOnFailure(writer.Put(TLV::ContextTag(kTagCluster), *entry.clusterId));
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 6 : 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 21 : 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.emplace(clusterId);
206 12 : ReturnErrorOnFailure(reader.Next());
207 : }
208 : else
209 : {
210 9 : entry.clusterId = std::nullopt;
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 : VerifyOrReturnError(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 : }
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 601 : BindingTable::Iterator BindingTable::end()
286 : {
287 : Iterator iter;
288 601 : iter.mTable = this;
289 601 : iter.mIndex = kNextNullIndex;
290 601 : 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
|