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 : // this file contains all the common includes for clusters in the zcl-util
19 :
20 : #include <app/util/attribute-storage.h>
21 :
22 : // for pulling in defines dealing with EITHER server or client
23 : #include "app/util/common.h"
24 : #include <app/util/config.h>
25 : #include <app/util/generic-callbacks.h>
26 : #include <app/util/odd-sized-integers.h>
27 : #include <lib/core/CHIPConfig.h>
28 :
29 : #include <app/reporting/reporting.h>
30 : #include <protocols/interaction_model/Constants.h>
31 :
32 : using chip::Protocols::InteractionModel::Status;
33 :
34 : using namespace chip;
35 :
36 0 : Status emAfWriteAttributeExternal(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
37 : EmberAfAttributeType dataType)
38 : {
39 0 : return emAfWriteAttribute(endpoint, cluster, attributeID, dataPtr, dataType, false /* override read-only */);
40 : }
41 :
42 0 : Status emberAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
43 : EmberAfAttributeType dataType)
44 : {
45 0 : return emAfWriteAttribute(endpoint, cluster, attributeID, dataPtr, dataType, true /* override read-only */);
46 : }
47 :
48 : //------------------------------------------------------------------------------
49 : // Internal Functions
50 :
51 : // Helper for determining whether a value is a null value.
52 : template <typename T>
53 0 : static bool IsNullValue(const uint8_t * data)
54 : {
55 : using Traits = app::NumericAttributeTraits<T>;
56 : // We don't know how data is aligned, so safely copy it over to the relevant
57 : // StorageType value.
58 : typename Traits::StorageType val;
59 0 : memcpy(&val, data, sizeof(val));
60 0 : return Traits::IsNullValue(val);
61 : }
62 :
63 0 : static bool IsNullValue(const uint8_t * data, uint16_t dataLen, bool isAttributeSigned)
64 : {
65 0 : if (dataLen > 4)
66 : {
67 : // We don't support this, just like emberAfCompareValues does not.
68 0 : return false;
69 : }
70 :
71 0 : switch (dataLen)
72 : {
73 0 : case 1: {
74 0 : if (isAttributeSigned)
75 : {
76 0 : return IsNullValue<int8_t>(data);
77 : }
78 0 : return IsNullValue<uint8_t>(data);
79 : }
80 0 : case 2: {
81 0 : if (isAttributeSigned)
82 : {
83 0 : return IsNullValue<int16_t>(data);
84 : }
85 0 : return IsNullValue<uint16_t>(data);
86 : }
87 0 : case 3: {
88 0 : if (isAttributeSigned)
89 : {
90 0 : return IsNullValue<app::OddSizedInteger<3, true>>(data);
91 : }
92 0 : return IsNullValue<app::OddSizedInteger<3, false>>(data);
93 : }
94 0 : case 4: {
95 0 : if (isAttributeSigned)
96 : {
97 0 : return IsNullValue<int32_t>(data);
98 : }
99 0 : return IsNullValue<uint32_t>(data);
100 : }
101 : }
102 :
103 : // Not reached.
104 0 : return false;
105 : }
106 :
107 0 : Status emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * data,
108 : EmberAfAttributeType dataType, bool overrideReadOnlyAndDataType)
109 : {
110 0 : const EmberAfAttributeMetadata * metadata = nullptr;
111 : EmberAfAttributeSearchRecord record;
112 0 : record.endpoint = endpoint;
113 0 : record.clusterId = cluster;
114 0 : record.attributeId = attributeID;
115 0 : Status status = emAfReadOrWriteAttribute(&record, &metadata,
116 : nullptr, // buffer
117 : 0, // buffer size
118 : false); // write?
119 :
120 : // if we dont support that attribute
121 0 : if (metadata == nullptr)
122 : {
123 0 : ChipLogProgress(Zcl, "%p ep %x clus " ChipLogFormatMEI " attr " ChipLogFormatMEI " not supported", "WRITE ERR: ", endpoint,
124 : ChipLogValueMEI(cluster), ChipLogValueMEI(attributeID));
125 0 : return status;
126 : }
127 :
128 : // if the data type specified by the caller is incorrect
129 0 : if (!(overrideReadOnlyAndDataType))
130 : {
131 0 : if (dataType != metadata->attributeType)
132 : {
133 0 : ChipLogProgress(Zcl, "%p invalid data type", "WRITE ERR: ");
134 0 : return Status::InvalidDataType;
135 : }
136 :
137 0 : if (metadata->IsReadOnly())
138 : {
139 0 : ChipLogProgress(Zcl, "%p attr not writable", "WRITE ERR: ");
140 0 : return Status::UnsupportedWrite;
141 : }
142 : }
143 :
144 : // if the value the attribute is being set to is out of range
145 : // return Status::ConstraintError
146 0 : if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
147 : {
148 0 : EmberAfDefaultAttributeValue minv = metadata->defaultValue.ptrToMinMaxValue->minValue;
149 0 : EmberAfDefaultAttributeValue maxv = metadata->defaultValue.ptrToMinMaxValue->maxValue;
150 0 : uint16_t dataLen = emberAfAttributeSize(metadata);
151 : const uint8_t * minBytes;
152 : const uint8_t * maxBytes;
153 0 : if (dataLen <= 2)
154 : {
155 : static_assert(sizeof(minv.defaultValue) == 2, "if statement relies on size of minv.defaultValue being 2");
156 : static_assert(sizeof(maxv.defaultValue) == 2, "if statement relies on size of maxv.defaultValue being 2");
157 0 : minBytes = reinterpret_cast<const uint8_t *>(&(minv.defaultValue));
158 0 : maxBytes = reinterpret_cast<const uint8_t *>(&(maxv.defaultValue));
159 : // On big endian cpu with length 1 only the second byte counts
160 : #if (CHIP_CONFIG_BIG_ENDIAN_TARGET)
161 : if (dataLen == 1)
162 : {
163 : minBytes++;
164 : maxBytes++;
165 : }
166 : #endif // CHIP_CONFIG_BIG_ENDIAN_TARGET
167 : }
168 : else
169 : {
170 0 : minBytes = minv.ptrToDefaultValue;
171 0 : maxBytes = maxv.ptrToDefaultValue;
172 : }
173 :
174 0 : bool isAttributeSigned = emberAfIsTypeSigned(metadata->attributeType);
175 0 : bool isOutOfRange = emberAfCompareValues(minBytes, data, dataLen, isAttributeSigned) == 1 ||
176 0 : emberAfCompareValues(maxBytes, data, dataLen, isAttributeSigned) == -1;
177 :
178 0 : if (isOutOfRange &&
179 : // null value is always in-range for a nullable attribute.
180 0 : (!metadata->IsNullable() || !IsNullValue(data, dataLen, isAttributeSigned)))
181 : {
182 0 : return Status::ConstraintError;
183 : }
184 : }
185 :
186 0 : const app::ConcreteAttributePath attributePath(endpoint, cluster, attributeID);
187 :
188 : // Pre write attribute callback for all attribute changes,
189 : // regardless of cluster.
190 : Protocols::InteractionModel::Status imStatus =
191 0 : MatterPreAttributeChangeCallback(attributePath, dataType, emberAfAttributeSize(metadata), data);
192 0 : if (imStatus != Protocols::InteractionModel::Status::Success)
193 : {
194 0 : return imStatus;
195 : }
196 :
197 : // Pre-write attribute callback specific
198 : // to the cluster that the attribute lives in.
199 0 : status = emAfClusterPreAttributeChangedCallback(attributePath, dataType, emberAfAttributeSize(metadata), data);
200 :
201 : // Ignore the following write operation and return success
202 0 : if (status == Status::WriteIgnored)
203 : {
204 0 : return Status::Success;
205 : }
206 :
207 0 : if (status != Status::Success)
208 : {
209 0 : return status;
210 : }
211 :
212 : // write the attribute
213 0 : status = emAfReadOrWriteAttribute(&record,
214 : nullptr, // metadata
215 : data,
216 : 0, // buffer size - unused
217 : true); // write?
218 :
219 0 : if (status != Status::Success)
220 : {
221 0 : return status;
222 : }
223 :
224 : // Save the attribute to persistent storage if needed
225 : // The callee will weed out attributes that do not need to be stored.
226 0 : emAfSaveAttributeToStorageIfNeeded(data, endpoint, cluster, metadata);
227 :
228 0 : MatterReportingAttributeChangeCallback(endpoint, cluster, attributeID);
229 :
230 : // Post write attribute callback for all attributes changes, regardless
231 : // of cluster.
232 0 : MatterPostAttributeChangeCallback(attributePath, dataType, emberAfAttributeSize(metadata), data);
233 :
234 : // Post-write attribute callback specific
235 : // to the cluster that the attribute lives in.
236 0 : emAfClusterAttributeChangedCallback(attributePath);
237 :
238 0 : return Status::Success;
239 : }
240 :
241 0 : Status emberAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr, uint16_t readLength)
242 : {
243 0 : const EmberAfAttributeMetadata * metadata = nullptr;
244 : EmberAfAttributeSearchRecord record;
245 : Status status;
246 0 : record.endpoint = endpoint;
247 0 : record.clusterId = cluster;
248 0 : record.attributeId = attributeID;
249 0 : status = emAfReadOrWriteAttribute(&record, &metadata, dataPtr, readLength,
250 : false); // write?
251 :
252 : // failed, print debug info
253 0 : if (status == Status::ResourceExhausted)
254 : {
255 0 : ChipLogProgress(Zcl, "READ: attribute size too large for caller");
256 : }
257 :
258 0 : return status;
259 : }
|