Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / app / util / af-main-common.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 Code common to both the Host and SOC (system
38  *on a chip) versions of the Application Framework.
39  *******************************************************************************
40  ******************************************************************************/
41
42 //#include PLATFORM_HEADER // Micro and compiler specific typedefs and macros
43
44 #if defined EZSP_HOST
45 #include "stack/include/ember-types.h"
46 #include "stack/include/error.h"
47 #include "stack/include/library.h"
48 #else
49 // Ember stack and related utilities
50 //#include "stack/include/cbke-crypto-engine.h"
51 //#include "stack/include/ember.h" // Main stack definitions
52 #endif
53
54 // HAL - hardware abstraction layer
55 //#include "hal/hal.h"
56 //#include "plugin/serial/serial.h" // Serial utility APIs
57
58 // CLI - command line interface
59 //#include "app/util/serial/command-interpreter2.h"
60
61 #if defined EZSP_HOST
62 // EZSP - ember serial host protocol
63 #include "app/util/ezsp/ezsp-protocol.h"
64 #include "app/util/ezsp/ezsp-utils.h"
65 #include "app/util/ezsp/ezsp.h"
66 #include "app/util/ezsp/serial-interface.h"
67 #endif
68
69 // Sub-GHz client, for a last-minute chance to block sending ZCL messgaes within the suspend period
70 #ifdef EMBER_AF_PLUGIN_SUB_GHZ_CLIENT
71 #include "app/framework/plugin/sub-ghz-client/sub-ghz-client.h"
72 #endif
73
74 // Sub-GHz server, for an automatic reply if a client attempt to communicate within the suspend period
75 #ifdef EMBER_AF_PLUGIN_SUB_GHZ_SERVER
76 #include "app/framework/plugin/sub-ghz-server/sub-ghz-server.h"
77 #endif
78
79 // Fragmentation.
80 #ifdef EMBER_AF_PLUGIN_FRAGMENTATION
81 #include "app/framework/plugin/fragmentation/fragmentation.h"
82 #endif
83
84 #ifdef EMBER_AF_PLUGIN_CRITICAL_MESSAGE_QUEUE
85 #include "app/framework/plugin/critical-message-queue/critical-message-queue.h"
86 #endif // EMBER_AF_PLUGIN_CRITICAL_MESSAGE_QUEUE
87
88 // Service discovery library
89 //#include "service-discovery.h"
90
91 // determines the number of in-clusters and out-clusters based on defines
92 // in config.h
93 #include "af-main.h"
94
95 //#include "app/framework/security/af-security.h"
96 //#include "app/framework/security/crypto-state.h"
97 #include "app/util/common.h"
98 #include "attribute-storage.h"
99 #include "attribute-table.h"
100 #include "config.h"
101 #include "gen/callback.h"
102 //#include "print.h"
103 #include "binding-table.h"
104 #include "chip-message-send.h"
105 #include "util.h"
106
107 using namespace chip;
108
109 // Querying the Ember Stack for what libraries are present.
110 //#include "app/util/common/library.h"
111
112 // ZDO - ZigBee Device Object
113 //#include "app/util/zigbee-framework/zigbee-device-common.h"
114
115 //#include "app/framework/plugin/ota-storage-common/ota-storage.h"
116 //#include "app/framework/plugin/partner-link-key-exchange/partner-link-key-exchange.h"
117
118 //------------------------------------------------------------------------------
119
120 #define INVALID_MESSAGE_TAG 0xFF
121
122 #if defined(EMBER_AF_HAS_COORDINATOR_NETWORK)
123 #if !defined(EMBER_AF_PLUGIN_CONCENTRATOR)
124 #error "A Coordinator device (Trust Center) MUST enable the concentrator plugin to function correctly."
125 #endif
126 #endif
127
128 #ifdef EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_DECLARATIONS
129 EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_DECLARATIONS
130 #endif
131
132 #ifdef EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_DECLARATIONS
133 EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_DECLARATIONS
134 #endif
135
136 #ifdef EMBER_AF_GENERATED_PLUGIN_ZDO_MESSAGE_RECEIVED_FUNCTION_DECLARATIONS
137 EMBER_AF_GENERATED_PLUGIN_ZDO_MESSAGE_RECEIVED_FUNCTION_DECLARATIONS
138 #endif
139
140 static CallbackTableEntry messageSentCallbacks[EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE];
141
142 // We declare this variable 'const' but NOT const.  Those functions that we may use
143 // this variable would also have to declare it const in order to function
144 // correctly, which is not the case (e.g. emberFindKeyTableEntry()).
145 const EmberEUI64 emberAfNullEui64 = { 0, 0, 0, 0, 0, 0, 0, 0 };
146
147 //------------------------------------------------------------------------------
148 // Forward declarations
149 static uint8_t getMessageSentCallbackIndex(void);
150 static void invalidateMessageSentCallbackEntry(uint8_t messageTag);
151 static EmberAfMessageSentFunction getMessageSentCallback(uint8_t tag);
152
153 static uint8_t getMessageSentCallbackIndex(void)
154 {
155     uint8_t i;
156     for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
157     {
158         if (messageSentCallbacks[i].tag == INVALID_MESSAGE_TAG)
159         {
160             return i;
161         }
162     }
163
164     return INVALID_MESSAGE_TAG;
165 }
166
167 static void invalidateMessageSentCallbackEntry(uint8_t tag)
168 {
169     uint8_t i;
170     for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
171     {
172         if (messageSentCallbacks[i].tag == tag)
173         {
174             messageSentCallbacks[i].tag      = INVALID_MESSAGE_TAG;
175             messageSentCallbacks[i].callback = NULL;
176             return;
177         }
178     }
179 }
180
181 static EmberAfMessageSentFunction getMessageSentCallback(uint8_t tag)
182 {
183     uint8_t i;
184     for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
185     {
186         if (messageSentCallbacks[i].tag == tag)
187         {
188             return messageSentCallbacks[i].callback;
189         }
190     }
191
192     return NULL;
193 }
194
195 void emAfInitializeMessageSentCallbackArray(void)
196 {
197     uint8_t i;
198     for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
199     {
200         messageSentCallbacks[i].tag      = INVALID_MESSAGE_TAG;
201         messageSentCallbacks[i].callback = NULL;
202     }
203 }
204
205 static EmberStatus send(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
206                         uint16_t messageLength, uint8_t * message, bool broadcast, EmberNodeId alias, uint8_t sequence,
207                         EmberAfMessageSentFunction callback)
208 {
209     EmberStatus status;
210     uint8_t index;
211     uint8_t messageSentIndex;
212     uint8_t messageTag = INVALID_MESSAGE_TAG;
213
214     // The send APIs only deal with ZCL messages, so they must at least contain
215     // the ZCL header.
216     if (messageLength < EMBER_AF_ZCL_OVERHEAD)
217     {
218         return EMBER_ERR_FATAL;
219     }
220     else if ((message[0] & ZCL_MANUFACTURER_SPECIFIC_MASK) != 0U)
221     {
222         if (messageLength < EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD)
223         {
224             return EMBER_ERR_FATAL;
225         }
226     }
227
228     messageSentIndex = getMessageSentCallbackIndex();
229     if (callback != NULL && messageSentIndex == INVALID_MESSAGE_TAG)
230     {
231         return EMBER_TABLE_FULL;
232     }
233
234     // The source endpoint in the APS frame MUST be valid at this point.  We use
235     // it to set the appropriate outgoing network in the APS frame.
236     EmberAfEndpointInfoStruct endpointInfo;
237     uint8_t networkIndex = 0;
238     if (emberAfGetEndpointInfoCallback(apsFrame->sourceEndpoint, &networkIndex, &endpointInfo))
239     {
240         //        status              = emberAfPushNetworkIndex(networkIndex);
241         //        if (status != EMBER_SUCCESS)
242         //        {
243         //            return status;
244         //        }
245     }
246     else
247     {
248         index = emberAfIndexFromEndpoint(apsFrame->sourceEndpoint);
249         if (index == 0xFF)
250         {
251             return EMBER_INVALID_ENDPOINT;
252         }
253         //        status = emberAfPushEndpointNetworkIndex(apsFrame->sourceEndpoint);
254         //        if (status != EMBER_SUCCESS)
255         //        {
256         //            return status;
257         //        }
258     }
259
260 #ifdef EMBER_AF_PLUGIN_SUB_GHZ_CLIENT
261     // If the Sub-GHz client is present and currently in the "suspended" state,
262     // block any outgoing message unless it comes from the Sub-GHz client itself.
263     if (emberAfPluginSubGhzClientIsSendingZclMessagesSuspended() && apsFrame->clusterId != ZCL_SUB_GHZ_CLUSTER_ID)
264     {
265         return EMBER_TRANSMISSION_SUSPENDED;
266     }
267 #endif
268
269     {
270         EmberAfMessageStruct messageStruct = {
271             callback, apsFrame, message, indexOrDestination, messageLength, type, broadcast,
272         };
273         // Called prior to fragmentation in case the mesasge does not go out over the
274         // Zigbee radio, and instead goes to some other transport that does not require
275         // low level ZigBee fragmentation.
276         if (emberAfPreMessageSendCallback(&messageStruct, &status))
277         {
278             return status;
279         }
280     }
281
282     // SE 1.4 requires an option to disable APS ACK and Default Response
283     emAfApplyDisableDefaultResponse(&message[0]);
284     emAfApplyRetryOverride(&apsFrame->options);
285
286     if (messageLength <= emberAfMaximumApsPayloadLength(type, indexOrDestination, apsFrame))
287     {
288         status = emAfSend(type, indexOrDestination, apsFrame, (uint8_t) messageLength, message, &messageTag, alias, sequence);
289 #ifdef EMBER_AF_PLUGIN_FRAGMENTATION
290     }
291     else if (!broadcast)
292     {
293         status = emAfFragmentationSendUnicast(type, indexOrDestination, apsFrame, message, messageLength, &messageTag);
294         emberAfDebugPrintln("%pstart:len=%d.", "Fragmentation:", messageLength);
295 #endif
296     }
297     else
298     {
299         status = EMBER_MESSAGE_TOO_LONG;
300     }
301
302 #ifdef EMBER_AF_PLUGIN_CRITICAL_MESSAGE_QUEUE
303     // If this was a critical message queue entry, fire the callback
304     if ((status != EMBER_SUCCESS) &&
305         (callback == emberAfPluginCriticalMessageQueueEnqueueCallback || callback == emAfPluginCriticalMessageQueueRetryCallback))
306     {
307         callback(type, indexOrDestination, apsFrame, messageLength, message, status);
308     }
309 #endif // EMBER_AF_PLUGIN_CRITICAL_MESSAGE_QUEUE
310
311     if (callback != NULL && status == EMBER_SUCCESS && messageTag != INVALID_MESSAGE_TAG &&
312         messageSentIndex < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE)
313     {
314         messageSentCallbacks[messageSentIndex].tag      = messageTag;
315         messageSentCallbacks[messageSentIndex].callback = callback;
316     }
317
318     if (status == EMBER_OPERATION_IN_PROGRESS && apsFrame->options & EMBER_APS_OPTION_DSA_SIGN)
319     {
320         // We consider "in progress" signed messages as being sent successfully.
321         // The stack will send the message after signing.
322         status = EMBER_SUCCESS;
323         // TODO: Can we actually hit this case in CHIP, or can this whole block
324         // go away?
325         // emAfSetCryptoOperationInProgress();
326     }
327
328     if (status == EMBER_SUCCESS)
329     {
330         emberAfAddToCurrentAppTasks(EMBER_AF_WAITING_FOR_DATA_ACK | EMBER_AF_WAITING_FOR_ZCL_RESPONSE);
331     }
332
333     // emberAfPopNetworkIndex();
334     return status;
335 }
336
337 EmberStatus emberAfSendMulticastWithAliasWithCallback(GroupId multicastId, EmberApsFrame * apsFrame, uint16_t messageLength,
338                                                       uint8_t * message, EmberNodeId alias, uint8_t sequence,
339                                                       EmberAfMessageSentFunction callback)
340 {
341     apsFrame->groupId = multicastId;
342     return send(EMBER_OUTGOING_MULTICAST_WITH_ALIAS, multicastId, apsFrame, messageLength, message,
343                 true, // broadcast
344                 alias, sequence, callback);
345 }
346
347 EmberStatus emberAfSendMulticastWithCallback(GroupId multicastId, EmberApsFrame * apsFrame, uint16_t messageLength,
348                                              uint8_t * message, EmberAfMessageSentFunction callback)
349 {
350     apsFrame->groupId = multicastId;
351     return send(EMBER_OUTGOING_MULTICAST, multicastId, apsFrame, messageLength, message,
352                 true, // broadcast?
353                 0,    // alias
354                 0,    // sequence
355                 callback);
356 }
357
358 EmberStatus emberAfSendMulticast(GroupId multicastId, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
359 {
360     return emberAfSendMulticastWithCallback(multicastId, apsFrame, messageLength, message, NULL);
361 }
362
363 EmberStatus emberAfSendMulticastToBindings(EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
364 {
365     EmberStatus status = EMBER_INVALID_BINDING_INDEX;
366     uint8_t i;
367     EmberBindingTableEntry binding;
368     GroupId groupDest;
369
370     if ((NULL == apsFrame) || (0 == messageLength) || (NULL == message))
371     {
372         return EMBER_BAD_ARGUMENT;
373     }
374
375     for (i = 0; i < EMBER_BINDING_TABLE_SIZE; i++)
376     {
377         status = emberGetBinding(i, &binding);
378         if (status != EMBER_SUCCESS)
379         {
380             return status;
381         }
382
383         if (binding.type == EMBER_MULTICAST_BINDING && binding.local == apsFrame->sourceEndpoint &&
384             binding.clusterId == apsFrame->clusterId)
385         {
386             groupDest                     = binding.groupId;
387             apsFrame->groupId             = groupDest;
388             apsFrame->destinationEndpoint = binding.remote;
389
390             status = emberAfSendMulticast(groupDest, // multicast ID
391                                           apsFrame, messageLength, message);
392
393             if (status != EMBER_SUCCESS)
394             {
395                 return status;
396             }
397         }
398     }
399
400     return status;
401 }
402
403 EmberStatus emberAfSendBroadcastWithCallback(EmberNodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength,
404                                              uint8_t * message, EmberAfMessageSentFunction callback)
405 {
406     return send(EMBER_OUTGOING_BROADCAST, destination, apsFrame, messageLength, message,
407                 true, // broadcast?
408                 0,    // alias
409                 0,    // sequence
410                 callback);
411 }
412 EmberStatus emberAfSendBroadcastWithAliasWithCallback(EmberNodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength,
413                                                       uint8_t * message, EmberNodeId alias, uint8_t sequence,
414                                                       EmberAfMessageSentFunction callback)
415 {
416     return send(EMBER_OUTGOING_BROADCAST_WITH_ALIAS, destination, apsFrame, messageLength, message,
417                 true,     // broadcast?
418                 alias,    // alias
419                 sequence, // sequence
420                 callback);
421 }
422
423 EmberStatus emberAfSendBroadcast(EmberNodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
424 {
425     return emberAfSendBroadcastWithCallback(destination, apsFrame, messageLength, message, NULL);
426 }
427
428 EmberStatus emberAfSendUnicastWithCallback(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
429                                            uint16_t messageLength, uint8_t * message, EmberAfMessageSentFunction callback)
430 {
431     // The source endpoint in the APS frame MAY NOT be valid at this point if the
432     // outgoing type is "via binding."
433     if (type == EMBER_OUTGOING_VIA_BINDING)
434     {
435         // If using binding, set the endpoints based on those in the binding.  The
436         // cluster in the binding is not used because bindings can be used to send
437         // messages with any cluster id, not just the one set in the binding.
438         EmberBindingTableEntry binding;
439         // TODO: This cast should go away once
440         // https://github.com/project-chip/connectedhomeip/issues/3584 is fixed.
441         EmberStatus status = emberGetBinding(static_cast<uint8_t>(indexOrDestination), &binding);
442         if (status != EMBER_SUCCESS)
443         {
444             return status;
445         }
446         apsFrame->sourceEndpoint      = binding.local;
447         apsFrame->destinationEndpoint = binding.remote;
448     }
449     return send(type, indexOrDestination, apsFrame, messageLength, message,
450                 false, // broadcast?
451                 0,     // alias
452                 0,     // sequence
453                 callback);
454 }
455
456 EmberStatus emberAfSendUnicast(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
457                                uint16_t messageLength, uint8_t * message)
458 {
459     return emberAfSendUnicastWithCallback(type, indexOrDestination, apsFrame, messageLength, message, NULL);
460 }
461
462 EmberStatus emberAfSendUnicastToBindingsWithCallback(EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message,
463                                                      EmberAfMessageSentFunction callback)
464 {
465     EmberStatus status = EMBER_INVALID_BINDING_INDEX;
466     uint8_t i;
467
468     for (i = 0; i < EMBER_BINDING_TABLE_SIZE; i++)
469     {
470         EmberBindingTableEntry binding;
471         status = emberGetBinding(i, &binding);
472         if (status != EMBER_SUCCESS)
473         {
474             return status;
475         }
476         if (binding.type == EMBER_UNICAST_BINDING && binding.local == apsFrame->sourceEndpoint &&
477             binding.clusterId == apsFrame->clusterId)
478         {
479             apsFrame->destinationEndpoint = binding.remote;
480             status                        = send(EMBER_OUTGOING_VIA_BINDING, i, apsFrame, messageLength, message,
481                           false, // broadcast?
482                           0,     // alias
483                           0,     // sequence
484                           callback);
485             if (status != EMBER_SUCCESS)
486             {
487                 return status;
488             }
489         }
490     }
491
492     return status;
493 }
494
495 EmberStatus emberAfSendUnicastToBindings(EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
496 {
497     return emberAfSendUnicastToBindingsWithCallback(apsFrame, messageLength, message, NULL);
498 }
499
500 EmberStatus emberAfSendInterPan(EmberPanId panId, const EmberEUI64 destinationLongId, EmberNodeId destinationShortId,
501                                 GroupId multicastId, ClusterId clusterId, uint16_t messageLength, uint8_t * messageBytes)
502 {
503     EmberAfInterpanHeader header;
504     memset(&header, 0, sizeof(EmberAfInterpanHeader));
505     header.panId        = panId;
506     header.shortAddress = destinationShortId;
507     if (destinationLongId != NULL)
508     {
509         memmove(header.longAddress, destinationLongId, EUI64_SIZE);
510         header.options |= EMBER_AF_INTERPAN_OPTION_MAC_HAS_LONG_ADDRESS;
511         header.messageType = EMBER_AF_INTER_PAN_UNICAST;
512     }
513     else if (multicastId != 0)
514     {
515         header.groupId     = multicastId;
516         header.messageType = EMBER_AF_INTER_PAN_MULTICAST;
517     }
518     else
519     {
520         header.messageType =
521             (destinationShortId < EMBER_BROADCAST_ADDRESS ? EMBER_AF_INTER_PAN_UNICAST : EMBER_AF_INTER_PAN_BROADCAST);
522     }
523     header.clusterId = clusterId;
524     return emberAfInterpanSendMessageCallback(&header, messageLength, messageBytes);
525 }
526
527 void emberAfPrintMessageData(uint8_t * data, uint16_t length)
528 {
529 #if defined EMBER_AF_PRINT_APP
530     emberAfAppPrint(" payload (len %2x) [", length);
531     emberAfAppPrintBuffer(data, length, true);
532     emberAfAppPrintln("]");
533 #endif
534 }
535
536 void emAfPrintStatus(const char * task, EmberStatus status)
537 {
538     if (status == EMBER_SUCCESS)
539     {
540         emberAfPrint(emberAfPrintActiveArea, "%p: %p", "Success", task);
541     }
542     else
543     {
544         emberAfPrint(emberAfPrintActiveArea, "%p: %p: 0x%x", "Error", task, status);
545     }
546 }
547
548 // ******************************************************************
549 // Functions called by the Serial Command Line Interface (CLI)
550 // ******************************************************************
551
552 static void printMessage(EmberIncomingMessageType type, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * messageContents)
553 {
554     emberAfAppPrint("Cluster: 0x%2X, %d bytes,", apsFrame->clusterId, messageLength);
555     if (messageLength >= 3)
556     {
557         emberAfAppPrint(" ZCL %p Cmd ID: %d", (messageContents[0] & ZCL_CLUSTER_SPECIFIC_COMMAND ? "Cluster" : "Global"),
558                         messageContents[2]);
559     }
560     emberAfAppPrintln("");
561 }
562
563 void emAfMessageSentHandler(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
564                             EmberStatus status, uint16_t messageLength, uint8_t * messageContents, uint8_t messageTag)
565 {
566     EmberAfMessageSentFunction callback;
567     if (status != EMBER_SUCCESS)
568     {
569         emberAfAppPrint("%ptx %x, ", "ERROR: ", status);
570         printMessage(type, apsFrame, messageLength, messageContents);
571     }
572
573     callback = getMessageSentCallback(messageTag);
574     invalidateMessageSentCallbackEntry(messageTag);
575
576     emberAfRemoveFromCurrentAppTasks(EMBER_AF_WAITING_FOR_DATA_ACK);
577
578     if (messageContents != NULL && messageContents[0] & ZCL_CLUSTER_SPECIFIC_COMMAND)
579     {
580         emberAfClusterMessageSentWithMfgCodeCallback(
581             type, indexOrDestination, apsFrame, messageLength, messageContents, status,
582             // If the manufacturer specific flag is set
583             // get read it as next part of message
584             // else use null code.
585             (((messageContents[0] & ZCL_MANUFACTURER_SPECIFIC_MASK) == ZCL_MANUFACTURER_SPECIFIC_MASK)
586                  ? emberAfGetInt16u(messageContents, 1, messageLength)
587                  : EMBER_AF_NULL_MANUFACTURER_CODE));
588     }
589
590     if (callback != NULL)
591     {
592         (*callback)(type, indexOrDestination, apsFrame, messageLength, messageContents, status);
593     }
594
595 #ifdef EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_CALLS
596     EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_CALLS
597 #endif
598
599     emberAfMessageSentCallback(type, indexOrDestination, apsFrame, messageLength, messageContents, status);
600 }
601
602 #ifdef EMBER_AF_PLUGIN_FRAGMENTATION
603 void emAfFragmentationMessageSentHandler(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
604                                          uint8_t * buffer, uint16_t bufLen, EmberStatus status, uint8_t messageTag)
605 {
606     // the fragmented message is no longer in process
607     emberAfDebugPrintln("%pend.", "Fragmentation:");
608     emAfMessageSentHandler(type, indexOrDestination, apsFrame, status, bufLen, buffer, messageTag);
609
610     // EMZIGBEE-4437: setting back the buffers to the original in case someone set
611     // that to something else.
612     emberAfSetExternalBuffer(appResponseData, EMBER_AF_RESPONSE_BUFFER_LEN, &appResponseLength, &emberAfResponseApsFrame);
613 }
614 #endif // EMBER_AF_PLUGIN_FRAGMENTATION
615
616 EmberStatus emAfSend(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame, uint8_t messageLength,
617                      uint8_t * message, uint8_t * messageTag, EmberNodeId alias, uint8_t sequence)
618 {
619     // TODO: There's an impedance mismatch here in a few ways:
620     // 1) The caller expects to get a messageTag out that will identify this
621     // message somewhat uniquely.  Right now we just ignore that and claim an
622     // invalid tag, which means message-sent callbacks don't get called.
623     //
624     // 2) The caller expects us to call emAfMessageSentHandler when we get an
625     // ack or time out, and pass it the original contents of the message, so it
626     // can invoke message-sent callbacks as needed.  But we may not have the
627     // contents of the message if we've done in-place encryption since then.
628     // Need to figure out whether any of this matters.
629     //
630     // https://github.com/project-chip/connectedhomeip/issues/2435 sort of
631     // tracks this.
632     *messageTag        = INVALID_MESSAGE_TAG;
633     EmberStatus status = EMBER_SUCCESS;
634     switch (type)
635     {
636     case EMBER_OUTGOING_VIA_BINDING: {
637         EmberBindingTableEntry binding;
638         // TODO: This cast should go away once
639         // https://github.com/project-chip/connectedhomeip/issues/3584 is fixed.
640         status = emberGetBinding(static_cast<uint8_t>(indexOrDestination), &binding);
641         if (status != EMBER_SUCCESS)
642         {
643             break;
644         }
645         if (binding.type != EMBER_UNICAST_BINDING)
646         {
647             status = EMBER_INVALID_BINDING_INDEX;
648             break;
649         }
650         status = chipSendUnicast(binding.nodeId, apsFrame, messageLength, message);
651         break;
652     }
653     case EMBER_OUTGOING_VIA_ADDRESS_TABLE:
654         // No implementation yet.
655         status = EMBER_ERR_FATAL;
656         break;
657     case EMBER_OUTGOING_DIRECT:
658         status = chipSendUnicast(indexOrDestination, apsFrame, messageLength, message);
659         break;
660     case EMBER_OUTGOING_MULTICAST:
661         // No implementation yet.
662         status = EMBER_ERR_FATAL;
663         break;
664     case EMBER_OUTGOING_MULTICAST_WITH_ALIAS:
665         // No implementation yet.
666         status = EMBER_ERR_FATAL;
667         break;
668     case EMBER_OUTGOING_BROADCAST:
669         // No implementation yet.
670         status = EMBER_ERR_FATAL;
671         break;
672     case EMBER_OUTGOING_BROADCAST_WITH_ALIAS:
673         // No implementation yet.
674         status = EMBER_ERR_FATAL;
675         break;
676     default:
677         status = EMBER_BAD_ARGUMENT;
678         break;
679     }
680     return status;
681 }