3 * Copyright (c) 2020 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * Copyright (c) 2020 Silicon Labs
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
26 * http://www.apache.org/licenses/LICENSE-2.0
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.
34 /***************************************************************************/
37 * @brief Code common to both the Host and SOC (system
38 *on a chip) versions of the Application Framework.
39 *******************************************************************************
40 ******************************************************************************/
42 //#include PLATFORM_HEADER // Micro and compiler specific typedefs and macros
45 #include "stack/include/ember-types.h"
46 #include "stack/include/error.h"
47 #include "stack/include/library.h"
49 // Ember stack and related utilities
50 //#include "stack/include/cbke-crypto-engine.h"
51 //#include "stack/include/ember.h" // Main stack definitions
54 // HAL - hardware abstraction layer
55 //#include "hal/hal.h"
56 //#include "plugin/serial/serial.h" // Serial utility APIs
58 // CLI - command line interface
59 //#include "app/util/serial/command-interpreter2.h"
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"
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"
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"
80 #ifdef EMBER_AF_PLUGIN_FRAGMENTATION
81 #include "app/framework/plugin/fragmentation/fragmentation.h"
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
88 // Service discovery library
89 //#include "service-discovery.h"
91 // determines the number of in-clusters and out-clusters based on defines
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"
101 #include "gen/callback.h"
103 #include "binding-table.h"
104 #include "chip-message-send.h"
107 using namespace chip;
109 // Querying the Ember Stack for what libraries are present.
110 //#include "app/util/common/library.h"
112 // ZDO - ZigBee Device Object
113 //#include "app/util/zigbee-framework/zigbee-device-common.h"
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"
118 //------------------------------------------------------------------------------
120 #define INVALID_MESSAGE_TAG 0xFF
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."
128 #ifdef EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_DECLARATIONS
129 EMBER_AF_GENERATED_PLUGIN_STACK_STATUS_FUNCTION_DECLARATIONS
132 #ifdef EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_DECLARATIONS
133 EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_DECLARATIONS
136 #ifdef EMBER_AF_GENERATED_PLUGIN_ZDO_MESSAGE_RECEIVED_FUNCTION_DECLARATIONS
137 EMBER_AF_GENERATED_PLUGIN_ZDO_MESSAGE_RECEIVED_FUNCTION_DECLARATIONS
140 static CallbackTableEntry messageSentCallbacks[EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE];
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 };
147 //------------------------------------------------------------------------------
148 // Forward declarations
149 static uint8_t getMessageSentCallbackIndex(void);
150 static void invalidateMessageSentCallbackEntry(uint8_t messageTag);
151 static EmberAfMessageSentFunction getMessageSentCallback(uint8_t tag);
153 static uint8_t getMessageSentCallbackIndex(void)
156 for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
158 if (messageSentCallbacks[i].tag == INVALID_MESSAGE_TAG)
164 return INVALID_MESSAGE_TAG;
167 static void invalidateMessageSentCallbackEntry(uint8_t tag)
170 for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
172 if (messageSentCallbacks[i].tag == tag)
174 messageSentCallbacks[i].tag = INVALID_MESSAGE_TAG;
175 messageSentCallbacks[i].callback = NULL;
181 static EmberAfMessageSentFunction getMessageSentCallback(uint8_t tag)
184 for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
186 if (messageSentCallbacks[i].tag == tag)
188 return messageSentCallbacks[i].callback;
195 void emAfInitializeMessageSentCallbackArray(void)
198 for (i = 0; i < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE; i++)
200 messageSentCallbacks[i].tag = INVALID_MESSAGE_TAG;
201 messageSentCallbacks[i].callback = NULL;
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)
211 uint8_t messageSentIndex;
212 uint8_t messageTag = INVALID_MESSAGE_TAG;
214 // The send APIs only deal with ZCL messages, so they must at least contain
216 if (messageLength < EMBER_AF_ZCL_OVERHEAD)
218 return EMBER_ERR_FATAL;
220 else if ((message[0] & ZCL_MANUFACTURER_SPECIFIC_MASK) != 0U)
222 if (messageLength < EMBER_AF_ZCL_MANUFACTURER_SPECIFIC_OVERHEAD)
224 return EMBER_ERR_FATAL;
228 messageSentIndex = getMessageSentCallbackIndex();
229 if (callback != NULL && messageSentIndex == INVALID_MESSAGE_TAG)
231 return EMBER_TABLE_FULL;
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))
240 // status = emberAfPushNetworkIndex(networkIndex);
241 // if (status != EMBER_SUCCESS)
248 index = emberAfIndexFromEndpoint(apsFrame->sourceEndpoint);
251 return EMBER_INVALID_ENDPOINT;
253 // status = emberAfPushEndpointNetworkIndex(apsFrame->sourceEndpoint);
254 // if (status != EMBER_SUCCESS)
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)
265 return EMBER_TRANSMISSION_SUSPENDED;
270 EmberAfMessageStruct messageStruct = {
271 callback, apsFrame, message, indexOrDestination, messageLength, type, broadcast,
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))
282 // SE 1.4 requires an option to disable APS ACK and Default Response
283 emAfApplyDisableDefaultResponse(&message[0]);
284 emAfApplyRetryOverride(&apsFrame->options);
286 if (messageLength <= emberAfMaximumApsPayloadLength(type, indexOrDestination, apsFrame))
288 status = emAfSend(type, indexOrDestination, apsFrame, (uint8_t) messageLength, message, &messageTag, alias, sequence);
289 #ifdef EMBER_AF_PLUGIN_FRAGMENTATION
293 status = emAfFragmentationSendUnicast(type, indexOrDestination, apsFrame, message, messageLength, &messageTag);
294 emberAfDebugPrintln("%pstart:len=%d.", "Fragmentation:", messageLength);
299 status = EMBER_MESSAGE_TOO_LONG;
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))
307 callback(type, indexOrDestination, apsFrame, messageLength, message, status);
309 #endif // EMBER_AF_PLUGIN_CRITICAL_MESSAGE_QUEUE
311 if (callback != NULL && status == EMBER_SUCCESS && messageTag != INVALID_MESSAGE_TAG &&
312 messageSentIndex < EMBER_AF_MESSAGE_SENT_CALLBACK_TABLE_SIZE)
314 messageSentCallbacks[messageSentIndex].tag = messageTag;
315 messageSentCallbacks[messageSentIndex].callback = callback;
318 if (status == EMBER_OPERATION_IN_PROGRESS && apsFrame->options & EMBER_APS_OPTION_DSA_SIGN)
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
325 // emAfSetCryptoOperationInProgress();
328 if (status == EMBER_SUCCESS)
330 emberAfAddToCurrentAppTasks(EMBER_AF_WAITING_FOR_DATA_ACK | EMBER_AF_WAITING_FOR_ZCL_RESPONSE);
333 // emberAfPopNetworkIndex();
337 EmberStatus emberAfSendMulticastWithAliasWithCallback(GroupId multicastId, EmberApsFrame * apsFrame, uint16_t messageLength,
338 uint8_t * message, EmberNodeId alias, uint8_t sequence,
339 EmberAfMessageSentFunction callback)
341 apsFrame->groupId = multicastId;
342 return send(EMBER_OUTGOING_MULTICAST_WITH_ALIAS, multicastId, apsFrame, messageLength, message,
344 alias, sequence, callback);
347 EmberStatus emberAfSendMulticastWithCallback(GroupId multicastId, EmberApsFrame * apsFrame, uint16_t messageLength,
348 uint8_t * message, EmberAfMessageSentFunction callback)
350 apsFrame->groupId = multicastId;
351 return send(EMBER_OUTGOING_MULTICAST, multicastId, apsFrame, messageLength, message,
358 EmberStatus emberAfSendMulticast(GroupId multicastId, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
360 return emberAfSendMulticastWithCallback(multicastId, apsFrame, messageLength, message, NULL);
363 EmberStatus emberAfSendMulticastToBindings(EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
365 EmberStatus status = EMBER_INVALID_BINDING_INDEX;
367 EmberBindingTableEntry binding;
370 if ((NULL == apsFrame) || (0 == messageLength) || (NULL == message))
372 return EMBER_BAD_ARGUMENT;
375 for (i = 0; i < EMBER_BINDING_TABLE_SIZE; i++)
377 status = emberGetBinding(i, &binding);
378 if (status != EMBER_SUCCESS)
383 if (binding.type == EMBER_MULTICAST_BINDING && binding.local == apsFrame->sourceEndpoint &&
384 binding.clusterId == apsFrame->clusterId)
386 groupDest = binding.groupId;
387 apsFrame->groupId = groupDest;
388 apsFrame->destinationEndpoint = binding.remote;
390 status = emberAfSendMulticast(groupDest, // multicast ID
391 apsFrame, messageLength, message);
393 if (status != EMBER_SUCCESS)
403 EmberStatus emberAfSendBroadcastWithCallback(EmberNodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength,
404 uint8_t * message, EmberAfMessageSentFunction callback)
406 return send(EMBER_OUTGOING_BROADCAST, destination, apsFrame, messageLength, message,
412 EmberStatus emberAfSendBroadcastWithAliasWithCallback(EmberNodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength,
413 uint8_t * message, EmberNodeId alias, uint8_t sequence,
414 EmberAfMessageSentFunction callback)
416 return send(EMBER_OUTGOING_BROADCAST_WITH_ALIAS, destination, apsFrame, messageLength, message,
419 sequence, // sequence
423 EmberStatus emberAfSendBroadcast(EmberNodeId destination, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
425 return emberAfSendBroadcastWithCallback(destination, apsFrame, messageLength, message, NULL);
428 EmberStatus emberAfSendUnicastWithCallback(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
429 uint16_t messageLength, uint8_t * message, EmberAfMessageSentFunction callback)
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)
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)
446 apsFrame->sourceEndpoint = binding.local;
447 apsFrame->destinationEndpoint = binding.remote;
449 return send(type, indexOrDestination, apsFrame, messageLength, message,
456 EmberStatus emberAfSendUnicast(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
457 uint16_t messageLength, uint8_t * message)
459 return emberAfSendUnicastWithCallback(type, indexOrDestination, apsFrame, messageLength, message, NULL);
462 EmberStatus emberAfSendUnicastToBindingsWithCallback(EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message,
463 EmberAfMessageSentFunction callback)
465 EmberStatus status = EMBER_INVALID_BINDING_INDEX;
468 for (i = 0; i < EMBER_BINDING_TABLE_SIZE; i++)
470 EmberBindingTableEntry binding;
471 status = emberGetBinding(i, &binding);
472 if (status != EMBER_SUCCESS)
476 if (binding.type == EMBER_UNICAST_BINDING && binding.local == apsFrame->sourceEndpoint &&
477 binding.clusterId == apsFrame->clusterId)
479 apsFrame->destinationEndpoint = binding.remote;
480 status = send(EMBER_OUTGOING_VIA_BINDING, i, apsFrame, messageLength, message,
485 if (status != EMBER_SUCCESS)
495 EmberStatus emberAfSendUnicastToBindings(EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * message)
497 return emberAfSendUnicastToBindingsWithCallback(apsFrame, messageLength, message, NULL);
500 EmberStatus emberAfSendInterPan(EmberPanId panId, const EmberEUI64 destinationLongId, EmberNodeId destinationShortId,
501 GroupId multicastId, ClusterId clusterId, uint16_t messageLength, uint8_t * messageBytes)
503 EmberAfInterpanHeader header;
504 memset(&header, 0, sizeof(EmberAfInterpanHeader));
505 header.panId = panId;
506 header.shortAddress = destinationShortId;
507 if (destinationLongId != NULL)
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;
513 else if (multicastId != 0)
515 header.groupId = multicastId;
516 header.messageType = EMBER_AF_INTER_PAN_MULTICAST;
521 (destinationShortId < EMBER_BROADCAST_ADDRESS ? EMBER_AF_INTER_PAN_UNICAST : EMBER_AF_INTER_PAN_BROADCAST);
523 header.clusterId = clusterId;
524 return emberAfInterpanSendMessageCallback(&header, messageLength, messageBytes);
527 void emberAfPrintMessageData(uint8_t * data, uint16_t length)
529 #if defined EMBER_AF_PRINT_APP
530 emberAfAppPrint(" payload (len %2x) [", length);
531 emberAfAppPrintBuffer(data, length, true);
532 emberAfAppPrintln("]");
536 void emAfPrintStatus(const char * task, EmberStatus status)
538 if (status == EMBER_SUCCESS)
540 emberAfPrint(emberAfPrintActiveArea, "%p: %p", "Success", task);
544 emberAfPrint(emberAfPrintActiveArea, "%p: %p: 0x%x", "Error", task, status);
548 // ******************************************************************
549 // Functions called by the Serial Command Line Interface (CLI)
550 // ******************************************************************
552 static void printMessage(EmberIncomingMessageType type, EmberApsFrame * apsFrame, uint16_t messageLength, uint8_t * messageContents)
554 emberAfAppPrint("Cluster: 0x%2X, %d bytes,", apsFrame->clusterId, messageLength);
555 if (messageLength >= 3)
557 emberAfAppPrint(" ZCL %p Cmd ID: %d", (messageContents[0] & ZCL_CLUSTER_SPECIFIC_COMMAND ? "Cluster" : "Global"),
560 emberAfAppPrintln("");
563 void emAfMessageSentHandler(EmberOutgoingMessageType type, uint64_t indexOrDestination, EmberApsFrame * apsFrame,
564 EmberStatus status, uint16_t messageLength, uint8_t * messageContents, uint8_t messageTag)
566 EmberAfMessageSentFunction callback;
567 if (status != EMBER_SUCCESS)
569 emberAfAppPrint("%ptx %x, ", "ERROR: ", status);
570 printMessage(type, apsFrame, messageLength, messageContents);
573 callback = getMessageSentCallback(messageTag);
574 invalidateMessageSentCallbackEntry(messageTag);
576 emberAfRemoveFromCurrentAppTasks(EMBER_AF_WAITING_FOR_DATA_ACK);
578 if (messageContents != NULL && messageContents[0] & ZCL_CLUSTER_SPECIFIC_COMMAND)
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));
590 if (callback != NULL)
592 (*callback)(type, indexOrDestination, apsFrame, messageLength, messageContents, status);
595 #ifdef EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_CALLS
596 EMBER_AF_GENERATED_PLUGIN_MESSAGE_SENT_FUNCTION_CALLS
599 emberAfMessageSentCallback(type, indexOrDestination, apsFrame, messageLength, messageContents, status);
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)
606 // the fragmented message is no longer in process
607 emberAfDebugPrintln("%pend.", "Fragmentation:");
608 emAfMessageSentHandler(type, indexOrDestination, apsFrame, status, bufLen, buffer, messageTag);
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);
614 #endif // EMBER_AF_PLUGIN_FRAGMENTATION
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)
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.
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.
630 // https://github.com/project-chip/connectedhomeip/issues/2435 sort of
632 *messageTag = INVALID_MESSAGE_TAG;
633 EmberStatus status = EMBER_SUCCESS;
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)
645 if (binding.type != EMBER_UNICAST_BINDING)
647 status = EMBER_INVALID_BINDING_INDEX;
650 status = chipSendUnicast(binding.nodeId, apsFrame, messageLength, message);
653 case EMBER_OUTGOING_VIA_ADDRESS_TABLE:
654 // No implementation yet.
655 status = EMBER_ERR_FATAL;
657 case EMBER_OUTGOING_DIRECT:
658 status = chipSendUnicast(indexOrDestination, apsFrame, messageLength, message);
660 case EMBER_OUTGOING_MULTICAST:
661 // No implementation yet.
662 status = EMBER_ERR_FATAL;
664 case EMBER_OUTGOING_MULTICAST_WITH_ALIAS:
665 // No implementation yet.
666 status = EMBER_ERR_FATAL;
668 case EMBER_OUTGOING_BROADCAST:
669 // No implementation yet.
670 status = EMBER_ERR_FATAL;
672 case EMBER_OUTGOING_BROADCAST_WITH_ALIAS:
673 // No implementation yet.
674 status = EMBER_ERR_FATAL;
677 status = EMBER_BAD_ARGUMENT;