Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / util / attribute-table.cpp
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  *
20  *    Copyright (c) 2020 Silicon Labs
21  *
22  *    Licensed under the Apache License, Version 2.0 (the "License");
23  *    you may not use this file except in compliance with the License.
24  *    You may obtain a copy of the License at
25  *
26  *        http://www.apache.org/licenses/LICENSE-2.0
27  *
28  *    Unless required by applicable law or agreed to in writing, software
29  *    distributed under the License is distributed on an "AS IS" BASIS,
30  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
31  *    See the License for the specific language governing permissions and
32  *    limitations under the License.
33  */
34 /***************************************************************************/
35 /**
36  * @file
37  * @brief This file contains the code to manipulate
38  *the Smart Energy attribute table.  This handles
39  *external calls to read/write the table, as well as
40  *internal ones.
41  *******************************************************************************
42  ******************************************************************************/
43
44 // this file contains all the common includes for clusters in the zcl-util
45
46 #include "attribute-storage.h"
47
48 // for pulling in defines dealing with EITHER server or client
49 #include "af-main.h"
50 #include "app/util/common.h"
51 #include "gen/callback.h"
52
53 #include <app/reporting/reporting.h>
54
55 using namespace chip;
56
57 //------------------------------------------------------------------------------
58
59 //------------------------------------------------------------------------------
60 // External Declarations
61
62 //------------------------------------------------------------------------------
63 // Forward Declarations
64
65 //------------------------------------------------------------------------------
66 // Globals
67
68 EmberAfStatus emberAfWriteAttributeExternal(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
69                                             uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType)
70 {
71     EmberAfAttributeWritePermission extWritePermission =
72         emberAfAllowNetworkWriteAttributeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType);
73     switch (extWritePermission)
74     {
75     case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_DENY_WRITE:
76         return EMBER_ZCL_STATUS_FAILURE;
77     case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_NORMAL:
78     case EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY:
79         return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType,
80                                   (extWritePermission == EMBER_ZCL_ATTRIBUTE_WRITE_PERMISSION_ALLOW_WRITE_OF_READ_ONLY), false);
81     default:
82         return (EmberAfStatus) extWritePermission;
83     }
84 }
85
86 //@deprecated use emberAfWriteServerAttribute or emberAfWriteClientAttribute
87 EmberAfStatus emberAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
88                                     uint8_t * dataPtr, EmberAfAttributeType dataType)
89 {
90     return emAfWriteAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, dataType,
91                               true,   // override read-only?
92                               false); // just test?
93 }
94
95 EmberAfStatus emberAfWriteClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
96                                           EmberAfAttributeType dataType)
97 {
98     return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
99                               dataType,
100                               true,   // override read-only?
101                               false); // just test?
102 }
103
104 EmberAfStatus emberAfWriteServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
105                                           EmberAfAttributeType dataType)
106 {
107     return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
108                               dataType,
109                               true,   // override read-only?
110                               false); // just test?
111 }
112
113 EmberAfStatus emberAfWriteManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
114                                                               uint16_t manufacturerCode, uint8_t * dataPtr,
115                                                               EmberAfAttributeType dataType)
116 {
117     return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, dataType,
118                               true,   // override read-only?
119                               false); // just test?
120 }
121
122 EmberAfStatus emberAfWriteManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
123                                                               uint16_t manufacturerCode, uint8_t * dataPtr,
124                                                               EmberAfAttributeType dataType)
125 {
126     return emAfWriteAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, dataType,
127                               true,   // override read-only?
128                               false); // just test?
129 }
130
131 EmberAfStatus emberAfVerifyAttributeWrite(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
132                                           uint16_t manufacturerCode, uint8_t * dataPtr, EmberAfAttributeType dataType)
133 {
134     return emAfWriteAttribute(endpoint, cluster, attributeID, mask, manufacturerCode, dataPtr, dataType,
135                               false, // override read-only?
136                               true); // just test?
137 }
138
139 EmberAfStatus emberAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask, uint8_t * dataPtr,
140                                    uint8_t readLength, EmberAfAttributeType * dataType)
141 {
142     return emAfReadAttribute(endpoint, cluster, attributeID, mask, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr, readLength, dataType);
143 }
144
145 EmberAfStatus emberAfReadServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
146                                          uint8_t readLength)
147 {
148     return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
149                              readLength, NULL);
150 }
151
152 EmberAfStatus emberAfReadClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t * dataPtr,
153                                          uint8_t readLength)
154 {
155     return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, EMBER_AF_NULL_MANUFACTURER_CODE, dataPtr,
156                              readLength, NULL);
157 }
158
159 EmberAfStatus emberAfReadManufacturerSpecificServerAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
160                                                              uint16_t manufacturerCode, uint8_t * dataPtr, uint8_t readLength)
161 {
162     return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_SERVER, manufacturerCode, dataPtr, readLength, NULL);
163 }
164
165 EmberAfStatus emberAfReadManufacturerSpecificClientAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID,
166                                                              uint16_t manufacturerCode, uint8_t * dataPtr, uint8_t readLength)
167 {
168     return emAfReadAttribute(endpoint, cluster, attributeID, CLUSTER_MASK_CLIENT, manufacturerCode, dataPtr, readLength, NULL);
169 }
170
171 bool emberAfReadSequentialAttributesAddToResponse(EndpointId endpoint, ClusterId clusterId, AttributeId startAttributeId,
172                                                   uint8_t mask, uint16_t manufacturerCode, uint8_t maxAttributeIds,
173                                                   bool includeAccessControl)
174 {
175     uint16_t i;
176     uint16_t discovered = 0;
177     uint16_t skipped    = 0;
178     uint16_t total      = 0;
179
180     EmberAfCluster * cluster = emberAfFindClusterWithMfgCode(endpoint, clusterId, mask, manufacturerCode);
181
182     EmberAfAttributeSearchRecord record;
183     record.endpoint         = endpoint;
184     record.clusterId        = clusterId;
185     record.clusterMask      = mask;
186     record.attributeId      = startAttributeId;
187     record.manufacturerCode = manufacturerCode;
188
189     // If we don't have the cluster or it doesn't match the search, we're done.
190     if (cluster == NULL || !emAfMatchCluster(cluster, &record))
191     {
192         return true;
193     }
194
195     for (i = 0; i < cluster->attributeCount; i++)
196     {
197         EmberAfAttributeMetadata * metadata = &cluster->attributes[i];
198
199         // If the cluster is not manufacturer-specific, an attribute is considered
200         // only if its manufacturer code matches that of the command (which may be
201         // unset).
202         if (!emberAfClusterIsManufacturerSpecific(cluster))
203         {
204             record.attributeId = metadata->attributeId;
205             if (!emAfMatchAttribute(cluster, metadata, &record))
206             {
207                 continue;
208             }
209         }
210
211         if (metadata->attributeId < startAttributeId)
212         {
213             skipped++;
214         }
215         else if (discovered < maxAttributeIds)
216         {
217             emberAfPutInt16uInResp(metadata->attributeId);
218             emberAfPutInt8uInResp(metadata->attributeType);
219             if (includeAccessControl)
220             {
221                 // bit 0 : Readable <-- All our attributes are readable
222                 // bit 1 : Writable <-- The only thing we track in the attribute metadata mask
223                 // bit 2 : Reportable <-- All our attributes are reportable
224                 emberAfPutInt8uInResp((metadata->mask & ATTRIBUTE_MASK_WRITABLE) ? 0x07 : 0x05);
225             }
226             discovered++;
227         }
228         else
229         {
230             // MISRA requires ..else if.. to have terminating else.
231         }
232         total++;
233     }
234
235     // We are finished if there are no more attributes to find, which means the
236     // number of attributes discovered plus the number skipped equals the total
237     // attributes in the cluster.  For manufacturer-specific clusters, the total
238     // includes all attributes in the cluster.  For standard ZCL clusters, if the
239     // the manufacturer code is set, the total is the number of attributes that
240     // match the manufacturer code.  Otherwise, the total is the number of
241     // standard ZCL attributes in the cluster.
242     return (discovered + skipped == total);
243 }
244
245 static void emberAfAttributeDecodeAndPrintCluster(ClusterId cluster, uint16_t mfgCode)
246 {
247 #if defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES)
248     uint16_t index = emberAfFindClusterNameIndexWithMfgCode(cluster, mfgCode);
249     if (index != 0xFFFF)
250     {
251         emberAfAttributesPrintln("(%p)", zclClusterNames[index].name);
252     }
253     emberAfAttributesFlush();
254 #endif // defined(EMBER_AF_PRINT_ENABLE) && defined(EMBER_AF_PRINT_ATTRIBUTES)
255 }
256
257 void emberAfPrintAttributeTable(void)
258 {
259     uint8_t data[ATTRIBUTE_LARGEST];
260     decltype(emberAfEndpointCount()) endpointIndex;
261     decltype(EmberAfEndpointType::clusterCount) clusterIndex;
262     uint16_t attributeIndex;
263     EmberAfStatus status;
264     uint16_t mfgCode;
265     for (endpointIndex = 0; endpointIndex < emberAfEndpointCount(); endpointIndex++)
266     {
267         EmberAfDefinedEndpoint * ep = &(emAfEndpoints[endpointIndex]);
268         emberAfAttributesPrintln("ENDPOINT %x", ep->endpoint);
269         emberAfAttributesPrintln("clus / side / attr / mfg  /type(len)/ rw / storage / data (raw)");
270         emberAfAttributesFlush();
271         for (clusterIndex = 0; clusterIndex < ep->endpointType->clusterCount; clusterIndex++)
272         {
273             EmberAfCluster * cluster = &(ep->endpointType->cluster[clusterIndex]);
274
275             for (attributeIndex = 0; attributeIndex < cluster->attributeCount; attributeIndex++)
276             {
277                 EmberAfAttributeMetadata * metaData = &(cluster->attributes[attributeIndex]);
278
279                 // Depending on user config, this loop can take a very long time to
280                 // run and watchdog reset will  kick in. As a workaround, we'll
281                 // manually reset the watchdog.
282                 //        halResetWatchdog();
283
284                 emberAfAttributesPrint("%2x / %p / %2x / ", cluster->clusterId,
285                                        (emberAfAttributeIsClient(metaData) ? "clnt" : "srvr"), metaData->attributeId);
286                 mfgCode = emAfGetManufacturerCodeForAttribute(cluster, metaData);
287                 if (mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE)
288                 {
289                     emberAfAttributesPrint("----");
290                 }
291                 else
292                 {
293                     emberAfAttributesPrint("%2x", mfgCode);
294                 }
295                 emberAfAttributesPrint(" / %x (%x) / %p / %p / ", metaData->attributeType, emberAfAttributeSize(metaData),
296                                        (emberAfAttributeIsReadOnly(metaData) ? "RO" : "RW"),
297                                        (emberAfAttributeIsTokenized(metaData)
298                                             ? " token "
299                                             : (emberAfAttributeIsExternal(metaData) ? "extern " : "  RAM  ")));
300                 emberAfAttributesFlush();
301                 status = emAfReadAttribute(ep->endpoint, cluster->clusterId, metaData->attributeId,
302                                            (emberAfAttributeIsClient(metaData) ? CLUSTER_MASK_CLIENT : CLUSTER_MASK_SERVER),
303                                            mfgCode, data, ATTRIBUTE_LARGEST, NULL);
304                 if (status == EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE)
305                 {
306                     emberAfAttributesPrintln("Unsupported");
307                 }
308                 else
309                 {
310                     uint16_t length;
311                     if (emberAfIsStringAttributeType(metaData->attributeType))
312                     {
313                         length = static_cast<uint16_t>(emberAfStringLength(data) + 1);
314                     }
315                     else if (emberAfIsLongStringAttributeType(metaData->attributeType))
316                     {
317                         length = static_cast<uint16_t>(emberAfLongStringLength(data) + 2);
318                     }
319                     else
320                     {
321                         length = emberAfAttributeSize(metaData);
322                     }
323                     UNUSED_VAR(length);
324                     emberAfAttributesPrintBuffer(data, length, true);
325                     emberAfAttributesFlush();
326                     emberAfAttributeDecodeAndPrintCluster(cluster->clusterId, mfgCode);
327                 }
328             }
329         }
330         emberAfAttributesFlush();
331     }
332 }
333
334 // given a clusterId and an attribute to read, this crafts the response
335 // and places it in the response buffer. Response is one of two items:
336 // 1) unsupported: [attrId:2] [status:1]
337 // 2) supported:   [attrId:2] [status:1] [type:1] [data:n]
338 //
339 void emberAfRetrieveAttributeAndCraftResponse(EndpointId endpoint, ClusterId clusterId, AttributeId attrId, uint8_t mask,
340                                               uint16_t manufacturerCode, uint16_t readLength)
341 {
342     EmberAfStatus status;
343     uint8_t data[ATTRIBUTE_LARGEST];
344     uint8_t dataType;
345     uint16_t dataLen;
346
347     // account for at least one byte of data
348     if (readLength < 5)
349     {
350         return;
351     }
352
353     emberAfAttributesPrintln("OTA READ: ep:%x cid:%2x attid:%2x msk:%x mfcode:%2x", endpoint, clusterId, attrId, mask,
354                              manufacturerCode);
355
356     // lookup the attribute in our table
357     status = emAfReadAttribute(endpoint, clusterId, attrId, mask, manufacturerCode, data, ATTRIBUTE_LARGEST, &dataType);
358     if (status == EMBER_ZCL_STATUS_SUCCESS)
359     {
360         dataLen = emberAfAttributeValueSize(dataType, data);
361         if ((readLength - 4) < dataLen)
362         { // Not enough space for attribute.
363             return;
364         }
365     }
366     else
367     {
368         emberAfPutInt16uInResp(attrId);
369         emberAfPutInt8uInResp(status);
370         emberAfAttributesPrintln("READ: clus %2x, attr %2x failed %x", clusterId, attrId, status);
371         emberAfAttributesFlush();
372         return;
373     }
374
375     // put attribute in least sig byte first
376     emberAfPutInt16uInResp(attrId);
377
378     // attribute is found, so copy in the status and the data type
379     emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS);
380     emberAfPutInt8uInResp(dataType);
381
382     if (dataLen < (EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength))
383     {
384 #if (BIGENDIAN_CPU)
385         // strings go over the air as length byte and then in human
386         // readable format. These should not be flipped. Other attributes
387         // need to be flipped so they go little endian OTA
388         if (isThisDataTypeSentLittleEndianOTA(dataType))
389         {
390             uint8_t i;
391             for (i = 0; i < dataLen; i++)
392             {
393                 appResponseData[appResponseLength + i] = data[dataLen - i - 1];
394             }
395         }
396         else
397         {
398             memmove(&(appResponseData[appResponseLength]), data, dataLen);
399         }
400 #else  //(BIGENDIAN_CPU)
401         memmove(&(appResponseData[appResponseLength]), data, dataLen);
402 #endif //(BIGENDIAN_CPU)
403        // TODO: How do we know this does not overflow?
404         appResponseLength = static_cast<uint16_t>(appResponseLength + dataLen);
405     }
406
407     emberAfAttributesPrintln("READ: clus %2x, attr %2x, dataLen: %x, OK", clusterId, attrId, dataLen);
408     emberAfAttributesFlush();
409 }
410
411 // This function appends the attribute report fields for the given endpoint,
412 // cluster, and attribute to the buffer starting at the index.  If there is
413 // insufficient space in the buffer or an error occurs, buffer and bufIndex will
414 // remain unchanged.  Otherwise, bufIndex will be incremented appropriately and
415 // the fields will be written to the buffer.
416 EmberAfStatus emberAfAppendAttributeReportFields(EndpointId endpoint, ClusterId clusterId, AttributeId attributeId, uint8_t mask,
417                                                  uint8_t * buffer, uint8_t bufLen, uint8_t * bufIndex)
418 {
419     EmberAfStatus status;
420     EmberAfAttributeType type;
421     uint16_t size;
422     uint16_t bufLen16 = (uint16_t) bufLen;
423     uint8_t data[ATTRIBUTE_LARGEST];
424
425     status = emberAfReadAttribute(endpoint, clusterId, attributeId, mask, data, sizeof(data), &type);
426     if (status != EMBER_ZCL_STATUS_SUCCESS)
427     {
428         goto kickout;
429     }
430
431     size = emberAfAttributeValueSize(type, data);
432     if (bufLen16 - *bufIndex < 3 || size > bufLen16 - (*bufIndex + 3))
433     {
434         status = EMBER_ZCL_STATUS_INSUFFICIENT_SPACE;
435         goto kickout;
436     }
437
438     buffer[(*bufIndex)++] = EMBER_LOW_BYTE(attributeId);
439     buffer[(*bufIndex)++] = EMBER_HIGH_BYTE(attributeId);
440     buffer[(*bufIndex)++] = type;
441 #if (BIGENDIAN_CPU)
442     if (isThisDataTypeSentLittleEndianOTA(type))
443     {
444         emberReverseMemCopy(buffer + *bufIndex, data, size);
445     }
446     else
447     {
448         memmove(buffer + *bufIndex, data, size);
449     }
450 #else
451     memmove(buffer + *bufIndex, data, size);
452 #endif
453     *bufIndex = static_cast<uint8_t>(*bufIndex + size);
454
455 kickout:
456     emberAfAttributesPrintln("REPORT: clus 0x%2x, attr 0x%2x: 0x%x", clusterId, attributeId, status);
457     emberAfAttributesFlush();
458
459     return status;
460 }
461
462 //------------------------------------------------------------------------------
463 // Internal Functions
464
465 // writes an attribute (identified by clusterID and attrID to the given value.
466 // this returns:
467 // - EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE: if attribute isnt supported by the device (the
468 //           device is not found in the attribute table)
469 // - EMBER_ZCL_STATUS_INVALID_DATA_TYPE: if the data type passed in doesnt match the type
470 //           stored in the attribute table
471 // - EMBER_ZCL_STATUS_READ_ONLY: if the attribute isnt writable
472 // - EMBER_ZCL_STATUS_INVALID_VALUE: if the value is set out of the allowable range for
473 //           the attribute
474 // - EMBER_ZCL_STATUS_SUCCESS: if the attribute was found and successfully written
475 //
476 // if true is passed in for overrideReadOnlyAndDataType then the data type is
477 // not checked and the read-only flag is ignored. This mode is meant for
478 // testing or setting the initial value of the attribute on the device.
479 //
480 // if true is passed for justTest, then the type is not written but all
481 // checks are done to see if the type could be written
482 // reads the attribute specified, returns false if the attribute is not in
483 // the table or the data is too large, returns true and writes to dataPtr
484 // if the attribute is supported and the readLength specified is less than
485 // the length of the data.
486 EmberAfStatus emAfWriteAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
487                                  uint16_t manufacturerCode, uint8_t * data, EmberAfAttributeType dataType,
488                                  bool overrideReadOnlyAndDataType, bool justTest)
489 {
490     EmberAfAttributeMetadata * metadata = NULL;
491     EmberAfAttributeSearchRecord record;
492     record.endpoint         = endpoint;
493     record.clusterId        = cluster;
494     record.clusterMask      = mask;
495     record.attributeId      = attributeID;
496     record.manufacturerCode = manufacturerCode;
497     emAfReadOrWriteAttribute(&record, &metadata,
498                              NULL,   // buffer
499                              0,      // buffer size
500                              false); // write?
501
502     // if we dont support that attribute
503     if (metadata == NULL)
504     {
505         emberAfAttributesPrintln("%pep %x clus %2x attr %2x not supported", "WRITE ERR: ", endpoint, cluster, attributeID);
506         emberAfAttributesFlush();
507         return EMBER_ZCL_STATUS_UNSUPPORTED_ATTRIBUTE;
508     }
509
510     // if the data type specified by the caller is incorrect
511     if (!(overrideReadOnlyAndDataType))
512     {
513         if (dataType != metadata->attributeType)
514         {
515             emberAfAttributesPrintln("%pinvalid data type", "WRITE ERR: ");
516             emberAfAttributesFlush();
517             return EMBER_ZCL_STATUS_INVALID_DATA_TYPE;
518         }
519
520         if (emberAfAttributeIsReadOnly(metadata))
521         {
522             emberAfAttributesPrintln("%pattr not writable", "WRITE ERR: ");
523             emberAfAttributesFlush();
524             return EMBER_ZCL_STATUS_READ_ONLY;
525         }
526     }
527
528     // if the value the attribute is being set to is out of range
529     // return EMBER_ZCL_STATUS_INVALID_VALUE
530     if ((metadata->mask & ATTRIBUTE_MASK_MIN_MAX) != 0U)
531     {
532         EmberAfDefaultAttributeValue minv = metadata->defaultValue.ptrToMinMaxValue->minValue;
533         EmberAfDefaultAttributeValue maxv = metadata->defaultValue.ptrToMinMaxValue->maxValue;
534         bool isAttributeSigned            = emberAfIsTypeSigned(metadata->attributeType);
535         uint8_t dataLen                   = emberAfAttributeSize(metadata);
536         if (dataLen <= 2)
537         {
538             int8_t minR, maxR;
539             uint8_t * minI = (uint8_t *) &(minv.defaultValue);
540             uint8_t * maxI = (uint8_t *) &(maxv.defaultValue);
541 // On big endian cpu with length 1 only the second byte counts
542 #if (BIGENDIAN_CPU)
543             if (dataLen == 1)
544             {
545                 minI++;
546                 maxI++;
547             }
548 #endif // BIGENDIAN_CPU
549             minR = emberAfCompareValues(minI, data, dataLen, isAttributeSigned);
550             maxR = emberAfCompareValues(maxI, data, dataLen, isAttributeSigned);
551             if ((minR == 1) || (maxR == -1))
552             {
553                 return EMBER_ZCL_STATUS_INVALID_VALUE;
554             }
555         }
556         else
557         {
558             if ((emberAfCompareValues(minv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == 1) ||
559                 (emberAfCompareValues(maxv.ptrToDefaultValue, data, dataLen, isAttributeSigned) == -1))
560             {
561                 return EMBER_ZCL_STATUS_INVALID_VALUE;
562             }
563         }
564     }
565
566     // write the data unless this is only a test
567     if (!justTest)
568     {
569         // Pre write attribute callback for all attribute changes,
570         // regardless of cluster.
571         EmberAfStatus status = emberAfPreAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
572                                                                  emberAfAttributeSize(metadata), data);
573         if (status != EMBER_ZCL_STATUS_SUCCESS)
574         {
575             return status;
576         }
577
578         // Pre-write attribute callback specific
579         // to the cluster that the attribute lives in.
580         status = emAfClusterPreAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
581                                                         emberAfAttributeSize(metadata), data);
582         if (status != EMBER_ZCL_STATUS_SUCCESS)
583         {
584             return status;
585         }
586
587         // write the attribute
588         status = emAfReadOrWriteAttribute(&record,
589                                           NULL, // metadata
590                                           data,
591                                           0,     // buffer size - unused
592                                           true); // write?
593
594         if (status != EMBER_ZCL_STATUS_SUCCESS)
595         {
596             return status;
597         }
598
599         // Save the attribute to token if needed
600         // Function itself will weed out tokens that are not tokenized.
601         emAfSaveAttributeToToken(data, endpoint, cluster, metadata);
602
603         emberAfReportingAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType, data);
604
605         // Post write attribute callback for all attributes changes, regardless
606         // of cluster.
607         emberAfPostAttributeChangeCallback(endpoint, cluster, attributeID, mask, manufacturerCode, dataType,
608                                            emberAfAttributeSize(metadata), data);
609
610         // Post-write attribute callback specific
611         // to the cluster that the attribute lives in.
612         emAfClusterAttributeChangedCallback(endpoint, cluster, attributeID, mask, manufacturerCode);
613     }
614     else
615     {
616         // bug: 11618, we are not handling properly external attributes
617         // in this case... We need to do something. We don't really
618         // know if it will succeed.
619         emberAfAttributesPrintln("WRITE: no write, just a test");
620         emberAfAttributesFlush();
621     }
622
623     return EMBER_ZCL_STATUS_SUCCESS;
624 }
625
626 // If dataPtr is NULL, no data is copied to the caller.
627 // readLength should be 0 in that case.
628
629 EmberAfStatus emAfReadAttribute(EndpointId endpoint, ClusterId cluster, AttributeId attributeID, uint8_t mask,
630                                 uint16_t manufacturerCode, uint8_t * dataPtr, uint16_t readLength, EmberAfAttributeType * dataType)
631 {
632     EmberAfAttributeMetadata * metadata = NULL;
633     EmberAfAttributeSearchRecord record;
634     EmberAfStatus status;
635     record.endpoint         = endpoint;
636     record.clusterId        = cluster;
637     record.clusterMask      = mask;
638     record.attributeId      = attributeID;
639     record.manufacturerCode = manufacturerCode;
640     status                  = emAfReadOrWriteAttribute(&record, &metadata, dataPtr, readLength,
641                                       false); // write?
642
643     if (status == EMBER_ZCL_STATUS_SUCCESS)
644     {
645         // It worked!  If the user asked for the type, set it before returning.
646         if (dataType != NULL)
647         {
648             (*dataType) = metadata->attributeType;
649         }
650     }
651     else
652     { // failed, print debug info
653         if (status == EMBER_ZCL_STATUS_INSUFFICIENT_SPACE)
654         {
655             emberAfAttributesPrintln("READ: attribute size too large for caller");
656             emberAfAttributesFlush();
657         }
658     }
659
660     return status;
661 }