Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / util / process-global-message.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 function that processes
38  *global ZCL message.
39  *******************************************************************************
40  ******************************************************************************/
41
42 #include "af.h"
43
44 #include <app/clusters/ias-zone-client/ias-zone-client.h>
45 #include <app/reporting/reporting.h>
46 #include <app/util/common.h>
47
48 #include "gen/attribute-id.h"
49 #include "gen/attribute-type.h"
50 #include "gen/callback.h"
51 #include "gen/cluster-id.h"
52 #include "gen/command-id.h"
53
54 #ifdef EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_SUB_GHZ
55 #include "app/framework/plugin/comms-hub-function-sub-ghz/comms-hub-function-sub-ghz.h"
56 #endif
57
58 #include <support/CodeUtils.h>
59
60 using namespace chip;
61
62 // flag to keep track of the fact that we just sent a read attr for time and
63 // we should set our time to the result of the read attr response.
64 bool emAfSyncingTime = false;
65
66 #ifdef EMBER_AF_GBCS_COMPATIBLE
67 // Some GBCS use cases (e.g. GCS15e, GCS21f) require that ReadAttributesResponse
68 // should be send back with Disable Default Response flag set. The only pattern
69 // is that the decision is based on the cluster and attribute IDs requested.
70 // To reduce the possibility of false positives, we disable default response
71 // only for responses containing at least the specified minimum of attributes.
72 #define MIN_MATCHING_ATTR_IDS_TO_DISABLE_DEFAULT_RESPONSE 3
73 #endif
74
75 #define DISC_ATTR_RSP_MAX_ATTRIBUTES                                                                                               \
76     (((EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH - EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD /* max ZCL header size */                 \
77        - 1)                                                                              /* discovery is complete boolean */       \
78       / 3)        /* size of one discover attributes response entry */                                                             \
79      % UINT8_MAX) /* make count fit in an 8 bit integer */
80 #define DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES                                                                                           \
81     (((EMBER_AF_MAXIMUM_APS_PAYLOAD_LENGTH - EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD /* max ZCL header size */                 \
82        - 1)                                                                              /* discovery is complete boolean */       \
83       / 4)        /* size of one discover attributes extended response entry */                                                    \
84      % UINT8_MAX) /* make count fit in an 8 bit integer */
85
86 #if defined(EMBER_AF_SUPPORT_COMMAND_DISCOVERY)
87 static void printDiscoverCommandsResponse(bool generated, ClusterId clusterId, bool discoveryComplete, uint8_t * buffer,
88                                           uint16_t length)
89 {
90     uint16_t i;
91     emberAfServiceDiscoveryPrint("Discover Commands response (complete: %c), %p IDs: ", (discoveryComplete ? 'y' : 'n'),
92                                  (generated ? "Generated" : "Received"));
93     for (i = 0; i < length; i++)
94     {
95         emberAfServiceDiscoveryPrint("0x%X ", buffer[i]);
96     }
97     emberAfServiceDiscoveryPrintln("");
98 }
99 #endif
100
101 bool emAfProcessGlobalCommand(EmberAfClusterCommand * cmd)
102 {
103     AttributeId attrId;
104     uint8_t frameControl;
105     // This is a little clumsy but easier to read and port
106     // from earlier implementation.
107     ClusterId clusterId      = cmd->apsFrame->clusterId;
108     CommandId zclCmd         = cmd->commandId;
109     uint8_t * message        = cmd->buffer;
110     uint16_t msgLen          = cmd->bufLen;
111     uint16_t msgIndex        = cmd->payloadStartIndex;
112     uint8_t clientServerMask = (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER ? CLUSTER_MASK_SERVER : CLUSTER_MASK_CLIENT);
113
114     // If we are disabled then we can only respond to read or write commands
115     // or identify cluster (see device enabled attr of basic cluster)
116     if (!emberAfIsDeviceEnabled(cmd->apsFrame->destinationEndpoint) && zclCmd != ZCL_READ_ATTRIBUTES_COMMAND_ID &&
117         zclCmd != ZCL_WRITE_ATTRIBUTES_COMMAND_ID && zclCmd != ZCL_WRITE_ATTRIBUTES_UNDIVIDED_COMMAND_ID &&
118         zclCmd != ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID && clusterId != ZCL_IDENTIFY_CLUSTER_ID)
119     {
120         emberAfCorePrintln("disabled");
121         emberAfDebugPrintln("%pd, dropping global cmd:%x", "disable", zclCmd);
122         emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_FAILURE);
123         return true;
124     }
125
126     // If a manufacturer-specific command arrives using our special internal "not
127     // manufacturer specific" code, we need to reject it outright without letting
128     // it pass through to the rest of the code.  The internal read and write APIs
129     // would interpret it as a standard attribute or cluster and return incorrect
130     // results.
131     if (cmd->mfgSpecific && cmd->mfgCode == EMBER_AF_NULL_MANUFACTURER_CODE)
132     {
133         goto kickout;
134     }
135
136     // Clear out the response buffer by setting its length to zero
137     appResponseLength = 0;
138
139     // Make the ZCL header for the response
140     // note: cmd byte is set below
141     frameControl = static_cast<uint8_t>(ZCL_GLOBAL_COMMAND |
142                                         (cmd->direction == ZCL_DIRECTION_CLIENT_TO_SERVER
143                                              ? ZCL_FRAME_CONTROL_SERVER_TO_CLIENT | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES
144                                              : ZCL_FRAME_CONTROL_CLIENT_TO_SERVER | EMBER_AF_DEFAULT_RESPONSE_POLICY_RESPONSES));
145     if (cmd->mfgSpecific)
146     {
147         frameControl |= ZCL_MANUFACTURER_SPECIFIC_MASK;
148     }
149     emberAfPutInt8uInResp(frameControl);
150     if (cmd->mfgSpecific)
151     {
152         emberAfPutInt16uInResp(cmd->mfgCode);
153     }
154     emberAfPutInt8uInResp(cmd->seqNum);
155
156     switch (zclCmd)
157     {
158     // The format of the read attributes cmd is:
159     // ([attr ID:2]) * N
160     // The format of the read attributes response is:
161     // ([attr ID:2] [status:1] [data type:0/1] [data:0/N]) * N
162     case ZCL_READ_ATTRIBUTES_COMMAND_ID: {
163         emberAfAttributesPrintln("%p: clus %2x", "READ_ATTR", clusterId);
164         // Set the cmd byte - this is byte 3 index 2, but since we have
165         // already incremented past the 3 byte ZCL header (our index is at 3),
166         // this gets written to "-1" since 3 - 1 = 2.
167         emberAfPutInt8uInResp(ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID);
168
169         // This message contains N 2-byte attr IDs after the 3 byte ZCL header,
170         // for each one we need to look it up and make a response
171         while (msgIndex + 2 <= msgLen)
172         {
173             // Get the attribute ID and store it in the response buffer
174             // least significant byte is first OTA
175             attrId = emberAfGetInt16u(message, msgIndex, msgLen);
176
177 #ifdef EMBER_AF_GBCS_COMPATIBLE
178             // GBCS explicitly lists some commands that need to be sent with "disable
179             // default response" flag set, including some ReadAttributes responses.
180             // We make it conditional on GBCS so it does not affect standard SE apps.
181             {
182                 static const struct
183                 {
184                     ClusterId clusterId;
185                     AttributeId attrId;
186                 } noDefaultResponseSet[] = {
187                     { ZCL_PRICE_CLUSTER_ID, ZCL_THRESHOLD_MULTIPLIER_ATTRIBUTE_ID },
188                     { ZCL_PRICE_CLUSTER_ID, ZCL_THRESHOLD_DIVISOR_ATTRIBUTE_ID },
189                     { ZCL_PRICE_CLUSTER_ID, ZCL_STANDING_CHARGE_ATTRIBUTE_ID },
190                     { ZCL_PRICE_CLUSTER_ID, ZCL_TARIFF_UNIT_OF_MEASURE_ATTRIBUTE_ID },
191                     { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_UNIT_OF_MEASURE_ATTRIBUTE_ID },
192                     { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_MULTIPLIER_ATTRIBUTE_ID },
193                     { ZCL_SIMPLE_METERING_CLUSTER_ID, ZCL_DIVISOR_ATTRIBUTE_ID },
194                 };
195                 uint8_t i;
196                 uint8_t foundMatchingAttrIdsCount = 0;
197
198                 for (i = 0; i < sizeof noDefaultResponseSet / sizeof noDefaultResponseSet[0]; ++i)
199                 {
200                     if (noDefaultResponseSet[i].clusterId == clusterId && noDefaultResponseSet[i].attrId == attrId)
201                     {
202                         if (++foundMatchingAttrIdsCount >= MIN_MATCHING_ATTR_IDS_TO_DISABLE_DEFAULT_RESPONSE)
203                         {
204                             emberAfSetDisableDefaultResponse(EMBER_AF_DISABLE_DEFAULT_RESPONSE_ONE_SHOT);
205                             break;
206                         }
207                     }
208                 }
209             }
210
211 #ifdef EMBER_AF_PLUGIN_COMMS_HUB_FUNCTION_SUB_GHZ
212             // This plugin sets channel change notification flags and needs to know
213             // when those flags have been read.
214             if (clientServerMask == CLUSTER_MASK_SERVER)
215             {
216                 emAfCommsHubFunctionSubGhzReadAttributeNotification(cmd->source, clusterId, attrId);
217             }
218 #endif
219 #endif
220
221             // This function reads the attribute and creates the correct response
222             // in the response buffer
223             emberAfRetrieveAttributeAndCraftResponse(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask,
224                                                      cmd->mfgCode,
225                                                      static_cast<uint16_t>(EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength));
226             // Go to next attrID
227             msgIndex = static_cast<uint16_t>(msgIndex + 2);
228         }
229     }
230
231         emberAfSendResponse();
232         return true;
233
234     // Write undivided means all attributes must be written in order to write
235     // any of them. So first do a check. If the check fails, send back a fail
236     // response. If it works, fall through to the normal write attr code.
237     // write attr responses are the same for undivided and normal writes.
238     case ZCL_WRITE_ATTRIBUTES_UNDIVIDED_COMMAND_ID: {
239         uint8_t numFailures = 0;
240         uint8_t dataType;
241         uint16_t dataSize;
242         EmberAfStatus status;
243
244         emberAfPutInt8uInResp(ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID);
245
246         // Go through the message until there are no more attrID/type/data
247         while (msgIndex < msgLen - 3)
248         {
249             attrId   = emberAfGetInt16u(message, msgIndex, msgLen);
250             dataType = emberAfGetInt8u(message, msgIndex + 2, msgLen);
251
252             dataSize = emberAfAttributeValueSize(dataType, message + msgIndex + 3);
253
254             // Check to see if there are dataSize bytes left in the message if it is a string
255             if (emberAfIsThisDataTypeAStringType(dataType) && (dataSize < msgLen - (msgIndex + 3)))
256             {
257                 // This command is malformed
258                 status = EMBER_ZCL_STATUS_MALFORMED_COMMAND;
259             }
260             else
261             {
262                 status = emberAfVerifyAttributeWrite(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask,
263                                                      cmd->mfgCode, &(message[msgIndex + 3]), dataType);
264             }
265
266             if (status != EMBER_ZCL_STATUS_SUCCESS)
267             {
268                 numFailures++;
269                 // Write to the response buffer - status and then attrID
270                 emberAfPutInt8uInResp(status);
271                 emberAfPutInt16uInResp(attrId);
272
273                 emberAfAttributesPrintln("WRITE: clus %2x attr %2x ", clusterId, attrId);
274                 emberAfAttributesPrintln("FAIL %x", status);
275                 emberAfCoreFlush();
276                 if (status == EMBER_ZCL_STATUS_MALFORMED_COMMAND)
277                 {
278                     // this attribute is malformed, terminate attribute processing.
279                     break;
280                 }
281             }
282
283             // Increment past the attribute id (two bytes), the type (one byte), and
284             // the data (N bytes, including the length byte for strings).
285             msgIndex = static_cast<uint16_t>(msgIndex + 3 + dataSize);
286         }
287         // If there are any failures, send the response and exit
288         if (numFailures > 0)
289         {
290             emberAfSendResponse();
291             return true;
292         }
293     }
294         // Reset message back to start
295         msgIndex          = cmd->payloadStartIndex;
296         appResponseLength = (cmd->mfgSpecific ? 4 : 2);
297         FALLTHROUGH;
298         /* fall through */
299     // DO NOT BREAK from this case
300
301     // the format of the write attributes cmd is:
302     // ([attr ID:2] [data type:1] [data:N]) * N
303     // the format of the write attributes response is:
304     // ([status 1] [attr ID 2]) * n
305     // ONLY errors are reported unless all are successful then a single success
306     // is sent. write attr no response is handled by just executing the same
307     // code but not setting the flag that sends the response at the end.
308     case ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID:
309     case ZCL_WRITE_ATTRIBUTES_COMMAND_ID: {
310         uint8_t numFailures = 0;
311         uint8_t numSuccess  = 0;
312         uint8_t dataType;
313         uint16_t dataSize;
314 #if (BIGENDIAN_CPU)
315         uint8_t writeData[ATTRIBUTE_LARGEST];
316 #endif //(BIGENDIAN_CPU)
317         EmberAfStatus status;
318
319         // set the cmd byte - this is byte 3 index 2, but since we have
320         // already incremented past the 3 byte ZCL header (our index is at 3),
321         // this gets written to "-1" since 3 - 1 = 2.
322         emberAfPutInt8uInResp(ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID);
323
324         // go through the message until there are no more attrID/type/data
325         while (msgLen > msgIndex + 3)
326         {
327             attrId   = emberAfGetInt16u(message, msgIndex, msgLen);
328             dataType = emberAfGetInt8u(message, msgIndex + 2, msgLen);
329
330             dataSize = emberAfAttributeValueSize(dataType, message + msgIndex + 3);
331
332             // the data is sent little endian over-the-air, it needs to be
333             // inserted into the table big endian for the EM250 and little
334             // endian for the EZSP hosts. This means for the EM250 the data
335             // needs to be reversed before sending to writeAttributes
336 #if (BIGENDIAN_CPU)
337             if (dataSize <= msgLen - (msgIndex + 3) && dataSize <= ATTRIBUTE_LARGEST)
338             {
339                 // strings go over the air as length byte and then in human
340                 // readable format. These should not be flipped.
341                 if (emberAfIsThisDataTypeAStringType(dataType))
342                 {
343                     memmove(writeData, message + msgIndex + 3, dataSize);
344                 }
345                 else
346                 {
347                     // the data is sent little endian over-the-air, it needs to be
348                     // inserted into the table big endian
349                     uint16_t i;
350                     for (i = 0; i < dataSize; i++)
351                     {
352                         writeData[i] = message[msgIndex + 3 + dataSize - i - 1];
353                     }
354                 }
355 #else  //(BIGENDIAN_CPU)
356             if (dataSize <= msgLen - (msgIndex + 3))
357             {
358 #endif //(BIGENDIAN_CPU)
359
360                 status = emberAfWriteAttributeExternal(cmd->apsFrame->destinationEndpoint, clusterId, attrId, clientServerMask,
361                                                        cmd->mfgCode,
362 #if (BIGENDIAN_CPU)
363                                                        writeData,
364 #else  //(BIGENDIAN_CPU)
365                                                        &(message[msgIndex + 3]),
366 #endif //(BIGENDIAN_CPU)
367                                                        dataType);
368                 emberAfAttributesPrint("WRITE: clus %2x attr %2x ", clusterId, attrId);
369                 if (status == EMBER_ZCL_STATUS_SUCCESS)
370                 {
371                     numSuccess++;
372                     emberAfAttributesPrintln("OK");
373                 }
374                 else
375                 {
376                     numFailures++;
377                     // write to the response buffer - status and then attrID
378                     emberAfPutInt8uInResp(status);
379                     emberAfPutInt16uInResp(attrId);
380                     emberAfAttributesPrintln("FAIL %x", status);
381                 }
382                 emberAfCoreFlush();
383
384                 // Increment past the attribute id (two bytes), the type (one byte), and
385                 // the data (N bytes, including the length byte for strings).
386                 msgIndex = static_cast<uint16_t>(msgIndex + 3 + dataSize);
387             }
388             else
389             {
390                 numFailures++;
391                 status = EMBER_ZCL_STATUS_INVALID_VALUE;
392                 // write to the response buffer - status and then attrID
393                 emberAfPutInt8uInResp(status);
394                 emberAfPutInt16uInResp(attrId);
395                 emberAfAttributesPrintln("FAIL %x", status);
396                 // size exceeds buffer, terminate loop
397                 break;
398             }
399         }
400
401         // always send a response unless the cmd requested no response
402         if (zclCmd == ZCL_WRITE_ATTRIBUTES_NO_RESPONSE_COMMAND_ID)
403         {
404             return true;
405         }
406
407         if (numFailures == 0)
408         {
409             // if no failures and no success this means the packet was too short
410             // print an error message but still return true as we consumed the
411             // message
412             if (numSuccess == 0)
413             {
414                 emberAfAttributesPrintln("WRITE: too short");
415                 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_MALFORMED_COMMAND);
416                 return true;
417             }
418             // if no failures and at least one success, write a success status
419             // that means everything worked
420             else
421             {
422                 emberAfPutInt8uInResp(EMBER_ZCL_STATUS_SUCCESS);
423             }
424         }
425         emberAfSendResponse();
426         return true;
427     }
428
429     // the format of discover is: [start attr ID:2] [max attr IDs:1]
430     // the format of the response is: [done:1] ([attrID:2] [type:1]) * N
431     case ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID:
432     case ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID: {
433         AttributeId startingAttributeId;
434         uint8_t numberAttributes;
435         uint8_t * complete;
436
437         emberAfAttributesPrintln("%p%p: clus %2x", "DISC_ATTR",
438                                  (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID ? "_EXT" : ""), clusterId);
439
440         // set the cmd byte - this is byte 3 index 2, but since we have
441         // already incremented past the 3 byte ZCL header (our index is at 3),
442         // this gets written to "-1" since 3 - 1 = 2.
443         emberAfPutInt8uInResp((zclCmd == ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID
444                                    ? ZCL_DISCOVER_ATTRIBUTES_RESPONSE_COMMAND_ID
445                                    : ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID));
446
447         // get the attrId to start on and the max count
448         startingAttributeId = emberAfGetInt16u(message, msgIndex, msgLen);
449         numberAttributes    = emberAfGetInt8u(message, msgIndex + 2, msgLen);
450
451         // BUGZID: EMAPPFWKV2-828, EMAPPFWKV2-1401
452         if (zclCmd == ZCL_DISCOVER_ATTRIBUTES_COMMAND_ID && numberAttributes > DISC_ATTR_RSP_MAX_ATTRIBUTES)
453         {
454             numberAttributes = DISC_ATTR_RSP_MAX_ATTRIBUTES;
455         }
456         else if (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID && numberAttributes > DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES)
457         {
458             numberAttributes = DISC_ATTR_EXT_RSP_MAX_ATTRIBUTES;
459         }
460         else
461         {
462             // MISRA requires ..else if.. to have terminating else.
463         }
464
465         // The response has a one-byte field indicating whether discovery is
466         // complete.  We can't populate that field until we've finished going
467         // through all the attributes, so save a placeholder, write a temporary
468         // value for now (so that the offset moves forward), and write the real
469         // value when we're done.
470         complete = &(appResponseData[appResponseLength]);
471         emberAfPutInt8uInResp(false);
472         *complete = emberAfReadSequentialAttributesAddToResponse(cmd->apsFrame->destinationEndpoint, clusterId, startingAttributeId,
473                                                                  clientServerMask, cmd->mfgCode, numberAttributes,
474                                                                  (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_COMMAND_ID));
475         emberAfSendResponse();
476         return true;
477     }
478
479     case ZCL_CONFIGURE_REPORTING_COMMAND_ID:
480         if (emberAfConfigureReportingCommandCallback(cmd))
481         {
482             return true;
483         }
484         break;
485
486     case ZCL_READ_REPORTING_CONFIGURATION_COMMAND_ID:
487         if (emberAfReadReportingConfigurationCommandCallback(cmd))
488         {
489             return true;
490         }
491         break;
492
493     // ([attribute id:2] [status:1] [type:0/1] [value:0/V])+
494     case ZCL_READ_ATTRIBUTES_RESPONSE_COMMAND_ID:
495         // The "timesync" command in the CLI sends a Read Attributes command for the
496         // Time attribute on another device and then sets a flag.  If that flag is
497         // set and a Read Attributes Response command for the time comes in, we set
498         // the time to the value in the message.
499         if (clusterId == ZCL_TIME_CLUSTER_ID)
500         {
501             if (emAfSyncingTime && !cmd->mfgSpecific && msgLen - msgIndex == 8 // attr:2 status:1 type:1 data:4
502                 && (emberAfGetInt16u(message, msgIndex, msgLen) == ZCL_TIME_ATTRIBUTE_ID) &&
503                 (emberAfGetInt8u(message, msgIndex + 2, msgLen) == EMBER_ZCL_STATUS_SUCCESS) &&
504                 (emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_UTC_TIME_ATTRIBUTE_TYPE))
505             {
506                 // emberAfSetTime(emberAfGetInt32u(message, msgIndex + 4, msgLen));
507                 // emberAfDebugPrintln("time sync ok, time: %4x", emberAfGetCurrentTime());
508                 emAfSyncingTime = false;
509             }
510 #ifdef EMBER_AF_PLUGIN_SMART_ENERGY_REGISTRATION_TIME_SOURCE_REQUIRED
511             emAfPluginSmartEnergyRegistrationReadAttributesResponseCallback(message + msgIndex, msgLen - msgIndex);
512 #endif // EMBER_AF_PLUGIN_SMART_ENERGY_REGISTRATION_TIME_SOURCE_REQUIRED
513 #ifdef EMBER_AF_PLUGIN_WWAH_SERVER_SILABS
514             emAfPluginSlWwahReadAttributesResponseCallback(clusterId, message, msgLen);
515 #endif
516         }
517
518 #ifdef EMBER_AF_PLUGIN_TRUST_CENTER_KEEPALIVE
519         if (clusterId == ZCL_KEEPALIVE_CLUSTER_ID && !cmd->mfgSpecific)
520         {
521             emAfPluginTrustCenterKeepaliveReadAttributesResponseCallback(message + msgIndex, msgLen - msgIndex);
522         }
523 #endif // EMBER_AF_PLUGIN_TRUST_CENTER_KEEPALIVE
524
525 #if defined(EMBER_AF_PLUGIN_KEY_ESTABLISHMENT)
526         if (clusterId == ZCL_KEY_ESTABLISHMENT_CLUSTER_ID && !cmd->mfgSpecific &&
527             msgLen - msgIndex == 6 // attr:2 status:1 type:1 data:2
528             && (emberAfGetInt16u(message, msgIndex, msgLen) == ZCL_KEY_ESTABLISHMENT_SUITE_CLIENT_ATTRIBUTE_ID) &&
529             (emberAfGetInt8u(message, msgIndex + 2, msgLen) == EMBER_ZCL_STATUS_SUCCESS) &&
530             ((emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_ENUM16_ATTRIBUTE_TYPE) ||
531              (emberAfGetInt8u(message, msgIndex + 3, msgLen) == ZCL_BITMAP16_ATTRIBUTE_TYPE)))
532         {
533             uint16_t suite = emberAfGetInt16u(message, msgIndex + 4, msgLen);
534             emberAfPluginKeyEstablishmentReadAttributesCallback(suite);
535         }
536 #endif
537
538 #if defined(EMBER_AF_PLUGIN_TEST_HARNESS)
539         emberAfPluginTestHarnessReadAttributesResponseCallback(clusterId, message + msgIndex, msgLen - msgIndex);
540 #endif
541
542 #if defined(EMBER_AF_PLUGIN_IAS_ZONE_CLIENT)
543         emberAfPluginIasZoneClientReadAttributesResponseCallback(clusterId, message + msgIndex,
544                                                                  static_cast<uint16_t>(msgLen - msgIndex));
545 #endif
546
547 #if defined(EMBER_AF_PLUGIN_SIMPLE_METERING_SERVER)
548         emberAfPluginSimpleMeteringClusterReadAttributesResponseCallback(clusterId, message + msgIndex,
549                                                                          static_cast<uint16_t>(msgLen - msgIndex));
550 #endif
551
552         if (!emberAfReadAttributesResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
553         {
554             emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
555         }
556         return true;
557
558     // ([status:1] [attribute id:2])+
559     case ZCL_WRITE_ATTRIBUTES_RESPONSE_COMMAND_ID:
560
561 #if defined(EMBER_AF_PLUGIN_TEST_HARNESS)
562         emberAfPluginTestHarnessWriteAttributesResponseCallback(clusterId, message + msgIndex,
563                                                                 static_cast<uint16_t>(msgLen - msgIndex));
564 #endif
565
566 #if defined(EMBER_AF_PLUGIN_IAS_ZONE_CLIENT)
567         emberAfPluginIasZoneClientWriteAttributesResponseCallback(clusterId, message + msgIndex,
568                                                                   static_cast<uint16_t>(msgLen - msgIndex));
569 #endif
570
571         if (!emberAfWriteAttributesResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
572         {
573             emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
574         }
575         return true;
576
577     // ([status:1] [direction:1] [attribute id:2])+
578     case ZCL_CONFIGURE_REPORTING_RESPONSE_COMMAND_ID:
579         if (!emberAfConfigureReportingResponseCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
580         {
581             emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
582         }
583         return true;
584
585     // ([status:1] [direction:1] [attribute id:2] [type:0/1] ...
586     // ... [min interval:0/2] [max interval:0/2] [reportable change:0/V] ...
587     // ... [timeout:0/2])+
588     case ZCL_READ_REPORTING_CONFIGURATION_RESPONSE_COMMAND_ID:
589         if (!emberAfReadReportingConfigurationResponseCallback(clusterId, message + msgIndex,
590                                                                static_cast<uint16_t>(msgLen - msgIndex)))
591         {
592             emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
593         }
594         return true;
595
596     // ([attribute id:2] [type:1] [data:V])+
597     case ZCL_REPORT_ATTRIBUTES_COMMAND_ID:
598         if (!emberAfReportAttributesCallback(clusterId, message + msgIndex, static_cast<uint16_t>(msgLen - msgIndex)))
599         {
600             emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
601         }
602         return true;
603
604     // [command id:1] [status:1]
605     case ZCL_DEFAULT_RESPONSE_COMMAND_ID: {
606         EmberAfStatus status;
607         CommandId commandId;
608         commandId = emberAfGetInt8u(message, msgIndex, msgLen);
609         msgIndex++;
610         status = (EmberAfStatus) emberAfGetInt8u(message, msgIndex, msgLen);
611
612         emberAfClusterDefaultResponseWithMfgCodeCallback(cmd->apsFrame->destinationEndpoint, clusterId, commandId, status,
613                                                          clientServerMask, cmd->mfgCode);
614         emberAfDefaultResponseCallback(clusterId, commandId, status);
615         return true;
616     }
617
618     // [discovery complete:1] ([attribute id:2] [type:1])*
619     case ZCL_DISCOVER_ATTRIBUTES_RESPONSE_COMMAND_ID:
620     case ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID: {
621         bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen);
622         msgIndex++;
623         if (!emberAfDiscoverAttributesResponseCallback(clusterId, discoveryComplete, message + msgIndex,
624                                                        static_cast<uint16_t>(msgLen - msgIndex),
625                                                        (zclCmd == ZCL_DISCOVER_ATTRIBUTES_EXTENDED_RESPONSE_COMMAND_ID)))
626         {
627             emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
628         }
629         return true;
630     }
631
632 #ifdef EMBER_AF_SUPPORT_COMMAND_DISCOVERY
633     // Command discovery takes a bit of flash because we need to add structs
634     // for commands into the generated hader. Hence it's all configurable.
635     case ZCL_DISCOVER_COMMANDS_RECEIVED_COMMAND_ID:
636     case ZCL_DISCOVER_COMMANDS_GENERATED_COMMAND_ID: {
637         uint8_t startCommandIdentifier    = emberAfGetInt8u(message, msgIndex, msgLen);
638         uint8_t maximumCommandIdentifiers = emberAfGetInt8u(message, msgIndex + 1, msgLen);
639         uint16_t savedIndex;
640         bool flag;
641
642         // Ok. This is the command that matters.
643         if (zclCmd == ZCL_DISCOVER_COMMANDS_RECEIVED_COMMAND_ID)
644         {
645             emberAfPutInt8uInResp(ZCL_DISCOVER_COMMANDS_RECEIVED_RESPONSE_COMMAND_ID);
646             flag = false;
647         }
648         else
649         {
650             emberAfPutInt8uInResp(ZCL_DISCOVER_COMMANDS_GENERATED_RESPONSE_COMMAND_ID);
651             flag = true;
652         }
653         savedIndex                  = appResponseLength;
654         flag                        = emberAfExtractCommandIds(flag, cmd, clusterId, appResponseData + appResponseLength + 1,
655                                         static_cast<uint16_t>(EMBER_AF_RESPONSE_BUFFER_LEN - appResponseLength - 1),
656                                         &appResponseLength, startCommandIdentifier, maximumCommandIdentifiers);
657         appResponseData[savedIndex] = (flag ? 1 : 0);
658         appResponseLength++;
659         emberAfSendResponse();
660         return true;
661     }
662     case ZCL_DISCOVER_COMMANDS_RECEIVED_RESPONSE_COMMAND_ID: {
663         bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen);
664         msgIndex++;
665         if (msgIndex <= msgLen)
666         {
667             printDiscoverCommandsResponse(false, // is ZCL command generated?
668                                           clusterId, discoveryComplete, message + msgIndex,
669                                           static_cast<uint16_t>(msgLen - msgIndex));
670             if (!emberAfDiscoverCommandsReceivedResponseCallback(clusterId, cmd->mfgCode, discoveryComplete, message + msgIndex,
671                                                                  static_cast<uint16_t>(msgLen - msgIndex)))
672             {
673                 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
674             }
675             return true;
676         }
677         else
678         {
679             return false;
680         }
681     }
682     case ZCL_DISCOVER_COMMANDS_GENERATED_RESPONSE_COMMAND_ID: {
683         bool discoveryComplete = emberAfGetInt8u(message, msgIndex, msgLen);
684         msgIndex++;
685         if (msgIndex <= msgLen)
686         {
687             printDiscoverCommandsResponse(true, // is ZCL command generated?
688                                           clusterId, discoveryComplete, message + msgIndex,
689                                           static_cast<uint16_t>(msgLen - msgIndex));
690             if (!emberAfDiscoverCommandsGeneratedResponseCallback(clusterId, cmd->mfgCode, discoveryComplete, message + msgIndex,
691                                                                   static_cast<uint16_t>(msgLen - msgIndex)))
692             {
693                 emberAfSendDefaultResponse(cmd, EMBER_ZCL_STATUS_SUCCESS);
694             }
695             return true;
696         }
697         else
698         {
699             return false;
700         }
701     }
702
703 #endif
704     default:
705         // MISRA requires default case.
706         break;
707     }
708
709 kickout:
710     emberAfSendDefaultResponse(
711         cmd, (cmd->mfgSpecific ? EMBER_ZCL_STATUS_UNSUP_MANUF_GENERAL_COMMAND : EMBER_ZCL_STATUS_UNSUP_GENERAL_COMMAND));
712     return true;
713 }