CC2530,zigbee,协议栈,代码集(ZDO)

10 篇文章 1 订阅
8 篇文章 1 订阅

ZDO(ZDO目录)

ZDApp.c

#include "ZComDef.h"
#include "ZMac.h"
#include "OSAL.h"
#include "OSAL_Tasks.h"
#include "OSAL_PwrMgr.h"
#include "OSAL_Nv.h"
#include "AF.h"
#include "APSMEDE.h"
#include "NLMEDE.h"
#include "AddrMgr.h"
#include "ZDProfile.h"
#include "ZDObject.h"
#include "ZDConfig.h"
#include "ZDSecMgr.h"
#include "ZDApp.h"
#include "DebugTrace.h"
#include "nwk_util.h"
#include "OnBoard.h"
#include "ZGlobals.h"
#include "ZDNwkMgr.h"
#include "rtg.h"

#include "ssp.h"

/* HAL */
#include "hal_led.h"
#include "hal_lcd.h"
#include "hal_key.h"

#if defined( MT_MAC_FUNC ) || defined( MT_MAC_CB_FUNC )
  #error "ERROR! MT_MAC functionalities should be disabled on ZDO devices"
#endif


#if !defined( NWK_START_DELAY )
  #define NWK_START_DELAY             100   // in milliseconds
#endif

#if !defined( LEAVE_RESET_DELAY )
  #define LEAVE_RESET_DELAY           5000  // in milliseconds
#endif

// Init ZDO, but hold and wait for application to start the joining or
// forming network
#define ZDO_INIT_HOLD_NWK_START       0xFFFF

#if !defined( EXTENDED_JOINING_RANDOM_MASK )
  #define EXTENDED_JOINING_RANDOM_MASK 0x007F
#endif

#if !defined( BEACON_REQUEST_DELAY )
  #define BEACON_REQUEST_DELAY        100   // in milliseconds
#endif

#if !defined( BEACON_REQ_DELAY_MASK )
  #define BEACON_REQ_DELAY_MASK       0x007F
#endif

#define MAX_RESUME_RETRY            3

#define MAX_DEVICE_UNAUTH_TIMEOUT   5000  // 5 seconds

// Beacon Order Settings (see NLMEDE.h)
#define DEFAULT_BEACON_ORDER        BEACON_ORDER_NO_BEACONS
#define DEFAULT_SUPERFRAME_ORDER    DEFAULT_BEACON_ORDER

#if !defined( MAX_NWK_FRAMECOUNTER_CHANGES )
  // The number of times the frame counter can change before
  // saving to NV
  #define MAX_NWK_FRAMECOUNTER_CHANGES    1000
#endif

// Leave control bits
#define ZDAPP_LEAVE_CTRL_INIT 0
#define ZDAPP_LEAVE_CTRL_SET  1
#define ZDAPP_LEAVE_CTRL_RA   2

// Address Manager Stub Implementation
#define ZDApp_NwkWriteNVRequest AddrMgrWriteNVRequest


#if !defined ZDO_NV_SAVE_RFDs
#define ZDO_NV_SAVE_RFDs  TRUE
#endif

// Delay time before updating NWK NV data to force fewer writes during high activity.
#if ZDO_NV_SAVE_RFDs
#define ZDAPP_UPDATE_NWK_NV_TIME 700
#else
#define ZDAPP_UPDATE_NWK_NV_TIME 65000
#endif

#if defined( LCD_SUPPORTED )
  uint8 MatchRsps = 0;
#endif

uint8 zdoDiscCounter = 1;

zAddrType_t ZDAppNwkAddr;

uint8 zdappMgmtNwkDiscRspTransSeq;
uint8 zdappMgmtNwkDiscReqInProgress = FALSE;
zAddrType_t zdappMgmtNwkDiscRspAddr;
uint8 zdappMgmtNwkDiscStartIndex;
uint8 zdappMgmtSavedNwkState;

uint16 nwkFrameCounterChanges = 0;
uint8 continueJoining = TRUE;

uint8  _tmpRejoinState;

// The extended PanID used in ZDO layer for rejoin.
uint8 ZDO_UseExtendedPANID[Z_EXTADDR_LEN];

pfnZdoCb zdoCBFunc[MAX_ZDO_CB_FUNC];

void ZDApp_NetworkStartEvt( void );
void ZDApp_DeviceAuthEvt( void );
void ZDApp_SaveNetworkStateEvt( void );

uint8 ZDApp_ReadNetworkRestoreState( void );
uint8 ZDApp_RestoreNetworkState( void );
void ZDAppDetermineDeviceType( void );
void ZDApp_InitUserDesc( void );
void ZDAppCheckForHoldKey( void );
void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr );
void ZDApp_ProcessNetworkJoin( void );
void ZDApp_SetCoordAddress( uint8 endPoint, uint8 dstEP );
uint8 ZDApp_RestoreNwkKey( void );
networkDesc_t* ZDApp_NwkDescListProcessing(void);

void ZDApp_SecInit( uint8 state );
UINT16 ZDApp_ProcessSecEvent( uint8 task_id, UINT16 events );
void ZDApp_ProcessSecMsg( osal_event_hdr_t *msgPtr );

void ZDApp_SendMsg( uint8 taskID, uint8 cmd, uint8 len, uint8 *buf );

void ZDApp_ResetTimerStart( uint16 delay );
void ZDApp_ResetTimerCancel( void );
void ZDApp_LeaveCtrlInit( void );
void ZDApp_LeaveCtrlSet( uint8 ra );
uint8 ZDApp_LeaveCtrlBypass( void );
void ZDApp_LeaveCtrlStartup( devStates_t* state, uint16* startDelay );
void ZDApp_LeaveUpdate( uint16 nwkAddr, uint8* extAddr,
                        uint8 removeChildren );
void ZDApp_NodeProfileSync( uint8 stackProfile );
void ZDApp_ProcessMsgCBs( zdoIncomingMsg_t *inMsg );
void ZDApp_RegisterCBs( void );
void ZDApp_InitZdoCBFunc(void);

uint8 ZDAppTaskID;
uint8 nwkStatus;
endPointDesc_t *ZDApp_AutoFindMode_epDesc = (endPointDesc_t *)NULL;
uint8 ZDApp_LeaveCtrl;

#if defined( HOLD_AUTO_START )
  devStates_t devState = DEV_HOLD;
#else
  devStates_t devState = DEV_INIT;
#endif

#if ( ZG_BUILD_RTRONLY_TYPE ) || ( ZG_BUILD_ENDDEVICE_TYPE )
  devStartModes_t devStartMode = MODE_JOIN;     // Assume joining
  //devStartModes_t devStartMode = MODE_RESUME; // if already "directly joined"
                        // to parent. Set to make the device do an Orphan scan.
#else
  // Set the default to coodinator
  devStartModes_t devStartMode = MODE_HARD;
#endif

uint8 retryCnt;

endPointDesc_t ZDApp_epDesc =
{
  ZDO_EP,
  &ZDAppTaskID,
  (SimpleDescriptionFormat_t *)NULL,  // No Simple description for ZDO
  (afNetworkLatencyReq_t)0            // No Network Latency req
};

uint16 ZDApp_SavedPollRate = POLL_RATE;
void ZDApp_Init( uint8 task_id )
{
  // Save the task ID
  ZDAppTaskID = task_id;

  // Initialize the ZDO global device short address storage
  ZDAppNwkAddr.addrMode = Addr16Bit;
  ZDAppNwkAddr.addr.shortAddr = INVALID_NODE_ADDR;
  (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer.

  // Check for manual "Hold Auto Start"
  ZDAppCheckForHoldKey();

  // Initialize ZDO items and setup the device - type of device to create.
  ZDO_Init();

  // Register the endpoint description with the AF
  // This task doesn't have a Simple description, but we still need
  // to register the endpoint.
  afRegister( (endPointDesc_t *)&ZDApp_epDesc );

#if defined( ZDO_USERDESC_RESPONSE )
  ZDApp_InitUserDesc();
#endif // ZDO_USERDESC_RESPONSE

  // Start the device?
  if ( devState != DEV_HOLD )
  {
    ZDOInitDevice( 0 );
  }
  else
  {
    ZDOInitDevice( ZDO_INIT_HOLD_NWK_START );
    // Blink LED to indicate HOLD_START
    HalLedBlink ( HAL_LED_4, 0, 50, 500 );
  }

  // Initialize the ZDO callback function pointers zdoCBFunc[]
  ZDApp_InitZdoCBFunc();

  ZDApp_RegisterCBs();
} /* ZDApp_Init() */
void ZDApp_SecInit( uint8 state )
{
  uint8 zgPreConfigKey[SEC_KEY_LEN];

  if ( ZG_SECURE_ENABLED && ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
  {
    // Set the Trust Center bit
    ZDO_Config_Node_Descriptor.ServerMask |= PRIM_TRUST_CENTER;
  }

  if ( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH )
  {
    ZDO_Config_Node_Descriptor.CapabilityFlags |= CAPINFO_SECURITY_CAPABLE;
  }

  // Initialize ZigBee Device Security Manager
  ZDSecMgrInit(state);

  if ( ZG_SECURE_ENABLED )
  {
    if ( state != ZDO_INITDEV_RESTORED_NETWORK_STATE )
    {
      nwkFrameCounter = 0;

      if( _NIB.nwkKeyLoaded == FALSE )
      {
        if ( ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE          ) ||
             ( ( zgSecurityMode == ZG_SECURITY_RESIDENTIAL ) && zgPreConfigKeys )    )
        {
            ZDSecMgrReadKeyFromNv(ZCD_NV_PRECFGKEY, zgPreConfigKey);
            SSP_UpdateNwkKey( zgPreConfigKey, 0);
            SSP_SwitchNwkKey( 0 );

            // clear local copy of key
            osal_memset(zgPreConfigKey, 0x00, SEC_KEY_LEN);
        }
      }
    }
  }
}

UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events )
{
  uint8 *msg_ptr;

  if ( events & SYS_EVENT_MSG )
  {
    while ( (msg_ptr = osal_msg_receive( ZDAppTaskID )) )
    {
      ZDApp_ProcessOSALMsg( (osal_event_hdr_t *)msg_ptr );

      // Release the memory
      osal_msg_deallocate( msg_ptr );
    }

    // Return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  if ( events & ZDO_NETWORK_INIT )
  {
    // Initialize apps and start the network
    devState = DEV_INIT;
    osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );

    ZDO_StartDevice( (uint8)ZDO_Config_Node_Descriptor.LogicalType, devStartMode,
                     DEFAULT_BEACON_ORDER, DEFAULT_SUPERFRAME_ORDER );

    // Return unprocessed events
    return (events ^ ZDO_NETWORK_INIT);
  }

  if ( ZSTACK_ROUTER_BUILD )
  {
    if ( events & ZDO_NETWORK_START )
    {
      ZDApp_NetworkStartEvt();

      // Return unprocessed events
      return (events ^ ZDO_NETWORK_START);
    }

    if ( events & ZDO_ROUTER_START )
    {
      if ( nwkStatus == ZSuccess )
      {
        if ( devState == DEV_END_DEVICE )
          devState = DEV_ROUTER;

        osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
      }
      else
      {
        // remain as end device!!
      }
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );

      // Return unprocessed events
      return (events ^ ZDO_ROUTER_START);
    }
  }

  if ( events & ZDO_STATE_CHANGE_EVT )
  {
    ZDO_UpdateNwkStatus( devState );

    // At start up, do one MTO route discovery if the device is a concentrator
    if ( zgConcentratorEnable == TRUE )
    {
      // Start next event
      osal_start_timerEx( NWK_TaskID, NWK_MTO_RTG_REQ_EVT, 100 );
    }

    // Return unprocessed events
    return (events ^ ZDO_STATE_CHANGE_EVT);
  }

  if ( events & ZDO_COMMAND_CNF )
  {
    // User defined logic

    // Return unprocessed events
    return (events ^ ZDO_COMMAND_CNF);
  }

  if ( events & ZDO_NWK_UPDATE_NV )
  {
    ZDApp_SaveNetworkStateEvt();

    // Return unprocessed events
    return (events ^ ZDO_NWK_UPDATE_NV);
  }

  if ( events & ZDO_DEVICE_RESET )
  {
#ifdef ZBA_FALLBACK_NWKKEY
    if ( devState == DEV_END_DEVICE_UNAUTH )
    {
      ZDSecMgrFallbackNwkKey();
    }
    else
#endif
    {
      // Set the NV startup option to force a "new" join.
      zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );

      // The device has been in the UNAUTH state, so reset
      // Note: there will be no return from this call
      SystemResetSoft();
    }
  }

  if ( ZG_SECURE_ENABLED )
  {
    return ( ZDApp_ProcessSecEvent( task_id, events ) );
  }
  else
  {
    // Discard or make more handlers
    return 0;
  }
}
UINT16 ZDApp_ProcessSecEvent( uint8 task_id, UINT16 events )
{
  (void)task_id;  // Intentionally unreferenced parameter

  if ( ZSTACK_ROUTER_BUILD )
  {
    if ( events & ZDO_NEW_DEVICE )
    {
      // process the new device event
      if ( ZDSecMgrNewDeviceEvent() == TRUE )
      {
        osal_start_timerEx( ZDAppTaskID, ZDO_NEW_DEVICE, 1000 );
      }

      // Return unprocessed events
      return (events ^ ZDO_NEW_DEVICE);
    }
  }

  if ( (ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH)
      || (ZG_CHECK_SECURITY_MODE == ZG_SECURITY_SE_STANDARD) )
  {
    if ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
    {
      if ( events & ZDO_SECMGR_EVENT )
      {
        ZDSecMgrEvent();

        // Return unprocessed events
        return (events ^ ZDO_SECMGR_EVENT);
      }
    }
  }

  if ( events & ZDO_DEVICE_AUTH )
  {
    ZDApp_DeviceAuthEvt();

    // Return unprocessed events
    return (events ^ ZDO_DEVICE_AUTH);
  }

  if ( events & ZDO_FRAMECOUNTER_CHANGE )
  {
    if ( nwkFrameCounterChanges++ > MAX_NWK_FRAMECOUNTER_CHANGES )
    {
      ZDApp_SaveNwkKey();
    }

    // Return unprocessed events
    return (events ^ ZDO_FRAMECOUNTER_CHANGE);
  }

  if ( events & ZDO_APS_FRAMECOUNTER_CHANGE )
  {
#if defined (NV_RESTORE)
    ZDSecMgrSaveApsLinkKey();
#endif // (NV_RESTORE)

    // Return unprocessed events
    return (events ^ ZDO_APS_FRAMECOUNTER_CHANGE);
  }

  if ( events & ZDO_TCLK_FRAMECOUNTER_CHANGE )
  {
    ZDSecMgrSaveTCLinkKey();

    // Return unprocessed events
    return (events ^ ZDO_TCLK_FRAMECOUNTER_CHANGE);
  }

  // Discard or make more handlers
  return 0;
}

uint8 ZDOInitDevice( uint16 startDelay )
{
  uint8 networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  uint16 extendedDelay = 0;

  if ( devState == DEV_HOLD )
  {
    // Initialize the RAM items table, in case an NV item has been updated.
    zgInitItems( FALSE );
  }

  ZDConfig_InitDescriptors();
  //devtag.071807.todo - fix this temporary solution
  _NIB.CapabilityFlags = ZDO_Config_Node_Descriptor.CapabilityFlags;

#if defined ( NV_RESTORE )
  // Get Keypad directly to see if a reset nv is needed.
  // Hold down the SW_BYPASS_NV key (defined in OnBoard.h)
  // while booting to skip past NV Restore.
  if ( HalKeyRead() == SW_BYPASS_NV )
    networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  else
  {
    // Determine if NV should be restored
    networkStateNV = ZDApp_ReadNetworkRestoreState();
  }

  if ( networkStateNV == ZDO_INITDEV_RESTORED_NETWORK_STATE )
  {
    networkStateNV = ZDApp_RestoreNetworkState();
  }
  else
  {
    // Wipe out the network state in NV
    NLME_InitNV();
    NLME_SetDefaultNV();
    // clear NWK key values
    ZDSecMgrClearNVKeyValues();
  }
#endif

  if ( networkStateNV == ZDO_INITDEV_NEW_NETWORK_STATE )
  {
    ZDAppDetermineDeviceType();

    // Only delay if joining network - not restoring network state
    extendedDelay = (uint16)((NWK_START_DELAY + startDelay)
              + (osal_rand() & EXTENDED_JOINING_RANDOM_MASK));
  }

  // Initialize the security for type of device
  ZDApp_SecInit( networkStateNV );

  if( ZDO_INIT_HOLD_NWK_START != startDelay )
  {
    devState = DEV_INIT;    // Remove the Hold state

    // Initialize leave control logic
    ZDApp_LeaveCtrlInit();

    // Check leave control reset settings
    ZDApp_LeaveCtrlStartup( &devState, &startDelay );

    // Leave may make the hold state come back
    if ( devState == DEV_HOLD )
    {
      // Set the NV startup option to force a "new" join.
      zgWriteStartupOptions( ZG_STARTUP_SET, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );

      // Notify the applications
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );

      return ( ZDO_INITDEV_LEAVE_NOT_STARTED );   // Don't join - (one time).
    }

    // Trigger the network start
    ZDApp_NetworkInit( extendedDelay );
  }

  // set broadcast address mask to support broadcast filtering
  NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags );

  return ( networkStateNV );
}

uint8 ZDApp_ReadNetworkRestoreState( void )
{
  uint8 networkStateNV = ZDO_INITDEV_RESTORED_NETWORK_STATE;

  // Look for the New Network State option.
  if ( zgReadStartupOptions() & ZCD_STARTOPT_DEFAULT_NETWORK_STATE )
  {
    networkStateNV = ZDO_INITDEV_NEW_NETWORK_STATE;
  }

  return ( networkStateNV );
}

void ZDAppDetermineDeviceType( void )
{
  if ( zgDeviceLogicalType == ZG_DEVICETYPE_COORDINATOR )
  {
    devStartMode = MODE_HARD;     // Start as a coordinator
    ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
  }
  else
  {
    if ( zgDeviceLogicalType == ZG_DEVICETYPE_ROUTER  )
      ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_ROUTER;
    else if ( zgDeviceLogicalType == ZG_DEVICETYPE_ENDDEVICE )
      ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_DEVICE;

    // If AIB_apsUseExtendedPANID is set to a non-zero value by commissioning
    // The device shall do rejoin the network. Otherwise, do normal join
    if ( nwk_ExtPANIDValid( AIB_apsUseExtendedPANID ) == false )
    {
      devStartMode = MODE_JOIN;     // Assume joining
    }
    else
    {
      devStartMode = MODE_REJOIN;
    }
  }
}

void ZDApp_NetworkStartEvt( void )
{
  if ( nwkStatus == ZSuccess )
  {
    // Successfully started a ZigBee network
    if ( devState == DEV_COORD_STARTING )
    {
      devState = DEV_ZB_COORD;
    }

    osal_pwrmgr_device( PWRMGR_ALWAYS_ON );
    osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
  }
  else
  {
    // Try again with a higher energy threshold !!
    if ( ( NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT ) < 0xff )
    {
      NLME_SetEnergyThreshold( (uint8)(NLME_GetEnergyThreshold() + ENERGY_SCAN_INCREMENT) );
      osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );
    }
    else
    {
      // Failed to start network. Enter a dormant state (until user intervenes)
      devState = DEV_INIT;
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    }
  }
}

void ZDApp_DeviceAuthEvt( void )
{
  // received authentication from trust center
  if ( devState == DEV_END_DEVICE_UNAUTH )
  {
    // Stop the reset timer so it doesn't reset
    ZDApp_ResetTimerCancel();

    devState = DEV_END_DEVICE;
    osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );

    // Set the Power Manager Device
#if defined ( POWER_SAVING )
    osal_pwrmgr_device( PWRMGR_BATTERY );
#endif

    if ( ZSTACK_ROUTER_BUILD )
    {
      if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
      {
        // NOTE: first two parameters are not used, see NLMEDE.h for details
        NLME_StartRouterRequest( 0, 0, false );
      }
    }

    // Notify to save info into NV
    ZDApp_NVUpdate();

    // Save off the security
    ZDApp_SaveNwkKey();

    ZDApp_AnnounceNewAddress();

    if ( (ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE) == 0 )
    {
      NLME_SetPollRate( ZDApp_SavedPollRate );
    }
  }
  else
  {
    ZDApp_NVUpdate();
  }
}

void ZDApp_SaveNetworkStateEvt( void )
{
#if defined ( NV_RESTORE )
 #if defined ( NV_TURN_OFF_RADIO )
  // Turn off the radio's receiver during an NV update
  uint8 RxOnIdle;
  uint8 x = false;
  ZMacGetReq( ZMacRxOnIdle, &RxOnIdle );
  ZMacSetReq( ZMacRxOnIdle, &x );
 #endif

  // Update the Network State in NV
  NLME_UpdateNV( NWK_NV_NIB_ENABLE        |
                 NWK_NV_DEVICELIST_ENABLE |
                 NWK_NV_BINDING_ENABLE    |
                 NWK_NV_ADDRMGR_ENABLE );

  // Reset the NV startup option to resume from NV by
  // clearing the "New" join option.
  zgWriteStartupOptions( FALSE, ZCD_STARTOPT_DEFAULT_NETWORK_STATE );

 #if defined ( NV_TURN_OFF_RADIO )
  ZMacSetReq( ZMacRxOnIdle, &RxOnIdle );
 #endif
#endif  // NV_RESTORE
}

uint8 ZDApp_RestoreNetworkState( void )
{
  uint8 nvStat;

  // Initialize NWK NV items
  nvStat = NLME_InitNV();

  if ( nvStat != NV_OPER_FAILED )
  {
    if ( NLME_RestoreFromNV() )
    {
      // Are we a coordinator
      ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();
      if ( ZDAppNwkAddr.addr.shortAddr == 0 )
        ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
      devStartMode = MODE_RESUME;
      osal_cpyExtAddr( ZDO_UseExtendedPANID, _NIB.extendedPANID );
    }
    else
      nvStat = NV_ITEM_UNINIT;

    if ( ZG_SECURE_ENABLED )
    {
      nwkFrameCounterChanges = 0;

      if ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
      {
        ZDApp_RestoreNwkKey();
      }
    }

    // The default for RxOnWhenIdle is true for Routers and false for end devices
    // [setup in the NLME_RestoreFromNV()].  Change it here if you want something
    // other than default.
  }

  if ( nvStat == ZSUCCESS )
    return ( ZDO_INITDEV_RESTORED_NETWORK_STATE );
  else
    return ( ZDO_INITDEV_NEW_NETWORK_STATE );
}

void ZDApp_InitUserDesc( void )
{
  UserDescriptorFormat_t ZDO_DefaultUserDescriptor;

  // Initialize the User Descriptor, the descriptor is read from NV
  // when needed.  If you want to initialize the User descriptor to something
  // other than all zero, do it here.
  osal_memset( &ZDO_DefaultUserDescriptor, 0, sizeof( UserDescriptorFormat_t ) );
  if ( ZSUCCESS == osal_nv_item_init( ZCD_NV_USERDESC,
         sizeof(UserDescriptorFormat_t), (void*)&ZDO_DefaultUserDescriptor ) )
  {
    if ( ZSUCCESS == osal_nv_read( ZCD_NV_USERDESC, 0,
         sizeof(UserDescriptorFormat_t), (void*)&ZDO_DefaultUserDescriptor ) )
    {
      if ( ZDO_DefaultUserDescriptor.len != 0 )
      {
        ZDO_Config_Node_Descriptor.UserDescAvail = TRUE;
      }
    }
  }
}

void ZDAppCheckForHoldKey( void )
{
#if (defined HAL_KEY) && (HAL_KEY == TRUE)
  // Get Keypad directly to see if a HOLD_START is needed.
  // Hold down the SW_BYPASS_START key (see OnBoard.h)
  // while booting to avoid starting up the device.
  if ( HalKeyRead () == SW_BYPASS_START)
  {
    // Change the device state to HOLD on start up
    devState = DEV_HOLD;
  }
#endif // HAL_KEY
}

void ZDApp_ProcessOSALMsg( osal_event_hdr_t *msgPtr )
{
  // Data Confirmation message fields
  uint8 sentEP;       // This should always be 0
  uint8 sentStatus;
  afDataConfirm_t *afDataConfirm;
  uint8 tmp;

  switch ( msgPtr->event )
  {
    // Incoming ZDO Message
    case AF_INCOMING_MSG_CMD:
      ZDP_IncomingData( (afIncomingMSGPacket_t *)msgPtr );
      break;

    case ZDO_CB_MSG:
      ZDApp_ProcessMsgCBs( (zdoIncomingMsg_t *)msgPtr );
      break;

    case AF_DATA_CONFIRM_CMD:
      // This message is received as a confirmation of a data packet sent.
      // The status is of ZStatus_t type [defined in NLMEDE.h]
      // The message fields are defined in AF.h
      afDataConfirm = (afDataConfirm_t *)msgPtr;
      sentEP = afDataConfirm->endpoint;
      sentStatus = afDataConfirm->hdr.status;

      // Action taken when confirmation is received.
#if defined ( ZIGBEE_FREQ_AGILITY )
      if ( pZDNwkMgr_ProcessDataConfirm )
        pZDNwkMgr_ProcessDataConfirm( afDataConfirm );
#endif
      (void)sentEP;
      (void)sentStatus;
      break;

    case ZDO_NWK_DISC_CNF:
      if (devState != DEV_NWK_DISC)
        break;

      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        // Process the network discovery scan results and choose a parent
        // device to join/rejoin itself
        networkDesc_t *pChosenNwk;
        if ( ( (pChosenNwk = ZDApp_NwkDescListProcessing()) != NULL ) && (zdoDiscCounter > NUM_DISC_ATTEMPTS) )
        {
          if ( devStartMode == MODE_JOIN )
          {
            devState = DEV_NWK_JOINING;

            ZDApp_NodeProfileSync( pChosenNwk->stackProfile);

            if ( NLME_JoinRequest( pChosenNwk->extendedPANID, pChosenNwk->panId,
                                  pChosenNwk->logicalChannel,
                                  ZDO_Config_Node_Descriptor.CapabilityFlags,
                                  pChosenNwk->chosenRouter, pChosenNwk->chosenRouterDepth ) != ZSuccess )
            {
              ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                                          + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
            }
          } // if ( devStartMode == MODE_JOIN )
          else if ( devStartMode == MODE_REJOIN )
          {
            devState = DEV_NWK_REJOIN;

            // Before trying to do rejoin, check if the device has a valid short address
            // If not, generate a random short address for itself
            if ( _NIB.nwkDevAddress == INVALID_NODE_ADDR )
            {
              _NIB.nwkDevAddress = osal_rand();
              ZMacSetReq( ZMacShortAddress, (byte*)&_NIB.nwkDevAddress );
            }

            if ( ZG_SECURE_ENABLED )
            {
              ZDApp_RestoreNwkKey();
            }

            // Check if the device has a valid PanID, if not, set it to the discovered Pan
            if ( _NIB.nwkPanId == INVALID_PAN_ID )
            {
              _NIB.nwkPanId = pChosenNwk->panId;
              ZMacSetReq( ZMacPanId, (byte*)&(_NIB.nwkPanId) );
            }

            tmp = true;
            ZMacSetReq( ZMacRxOnIdle, &tmp ); // Set receiver always on during rejoin
            if ( NLME_ReJoinRequest( ZDO_UseExtendedPANID, pChosenNwk->logicalChannel) != ZSuccess )
            {
              ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
                                          + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
            }
          } // else if ( devStartMode == MODE_REJOIN )

          if ( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE )
          {
            // The receiver is on, turn network layer polling off.
            NLME_SetPollRate( 0 );
            NLME_SetQueuedPollRate( 0 );
            NLME_SetResponseRate( 0 );
          }
          else
          {
            if ( (ZG_SECURE_ENABLED) && (devStartMode == MODE_JOIN) )
            {
              ZDApp_SavedPollRate = zgPollRate;
              NLME_SetPollRate( zgRejoinPollRate );
            }
          }
        }
        else
        {
          if ( continueJoining )
          {
    #if defined ( MANAGED_SCAN )
            ZDApp_NetworkInit( MANAGEDSCAN_DELAY_BETWEEN_SCANS );
    #else
            zdoDiscCounter++;
            ZDApp_NetworkInit( (uint16)(BEACON_REQUEST_DELAY
                  + ((uint16)(osal_rand()& BEACON_REQ_DELAY_MASK))) );
    #endif
          }
        }
      }
      break;

    case ZDO_NWK_JOIN_IND:
      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        ZDApp_ProcessNetworkJoin();
      }
      break;

    case ZDO_NWK_JOIN_REQ:
      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        retryCnt = 0;
        devStartMode = MODE_RESUME;
        _tmpRejoinState = true;
        osal_cpyExtAddr( ZDO_UseExtendedPANID, _NIB.extendedPANID );
        zgDefaultStartingScanDuration = BEACON_ORDER_60_MSEC;
        ZDApp_NetworkInit( 0 );
      }
      break;

    default:
      if ( ZG_SECURE_ENABLED )
        ZDApp_ProcessSecMsg( msgPtr );
      break;
  }

}

void ZDApp_ProcessMsgCBs( zdoIncomingMsg_t *inMsg )
{
  switch ( inMsg->clusterID )
  {
#if defined ( ZDO_NWKADDR_REQUEST ) || defined ( ZDO_IEEEADDR_REQUEST ) || defined ( REFLECTOR )
    case NWK_addr_rsp:
    case IEEE_addr_rsp:
      {
        ZDO_NwkIEEEAddrResp_t *pAddrRsp;
        pAddrRsp = ZDO_ParseAddrRsp( inMsg );
        if ( pAddrRsp )
        {
          if ( pAddrRsp->status == ZSuccess )
          {
            ZDO_UpdateAddrManager( pAddrRsp->nwkAddr, pAddrRsp->extAddr );
          }
          osal_mem_free( pAddrRsp );
        }
      }
      break;
#endif

#if defined ( REFLECTOR )
    case Bind_req:
    case Unbind_req:
      {
        ZDO_BindUnbindReq_t bindReq;
        ZDO_ParseBindUnbindReq( inMsg, &bindReq );
        ZDO_ProcessBindUnbindReq( inMsg, &bindReq );
      }
      break;
#endif

#if ( ZG_BUILD_COORDINATOR_TYPE )
    case Bind_rsp:
    case Unbind_rsp:
      if (ZG_DEVICE_COORDINATOR_TYPE && matchED)
      {
        ZDMatchSendState(
             (uint8)((inMsg->clusterID == Bind_rsp) ? ZDMATCH_REASON_BIND_RSP : ZDMATCH_REASON_UNBIND_RSP),
             ZDO_ParseBindRsp(inMsg), inMsg->TransSeq );
      }
      break;

    case End_Device_Bind_req:
      if (ZG_DEVICE_COORDINATOR_TYPE)
      {
        ZDEndDeviceBind_t bindReq;
        ZDO_ParseEndDeviceBindReq( inMsg, &bindReq );
        ZDO_MatchEndDeviceBind( &bindReq );

        // Freeing the cluster lists - if allocated.
        if ( bindReq.numInClusters )
          osal_mem_free( bindReq.inClusters );
        if ( bindReq.numOutClusters )
          osal_mem_free( bindReq.outClusters );
      }
      break;
#endif
  }
}

void ZDApp_RegisterCBs( void )
{
#if defined ( ZDO_IEEEADDR_REQUEST ) || defined ( REFLECTOR )
  ZDO_RegisterForZDOMsg( ZDAppTaskID, IEEE_addr_rsp );
#endif
#if defined ( ZDO_NWKADDR_REQUEST ) || defined ( REFLECTOR )
  ZDO_RegisterForZDOMsg( ZDAppTaskID, NWK_addr_rsp );
#endif
#if ZG_BUILD_COORDINATOR_TYPE
  ZDO_RegisterForZDOMsg( ZDAppTaskID, Bind_rsp );
  ZDO_RegisterForZDOMsg( ZDAppTaskID, Unbind_rsp );
  ZDO_RegisterForZDOMsg( ZDAppTaskID, End_Device_Bind_req );
#endif
#if defined ( REFLECTOR )
  ZDO_RegisterForZDOMsg( ZDAppTaskID, Bind_req );
  ZDO_RegisterForZDOMsg( ZDAppTaskID, Unbind_req );
#endif
}

void ZDApp_ProcessSecMsg( osal_event_hdr_t *msgPtr )
{
  switch ( msgPtr->event )
  {
    case ZDO_ESTABLISH_KEY_CFM:
      if ( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH )
      {
        ZDSecMgrEstablishKeyCfm( (ZDO_EstablishKeyCfm_t*)msgPtr );
      }
      break;

    case ZDO_ESTABLISH_KEY_IND:
      if ( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH )
      {
        if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
        {
          ZDSecMgrEstablishKeyInd( (ZDO_EstablishKeyInd_t*)msgPtr );
        }
      }
      break;

    case ZDO_TRANSPORT_KEY_IND:
      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        ZDSecMgrTransportKeyInd( (ZDO_TransportKeyInd_t*)msgPtr );
      }
      break;

    case ZDO_UPDATE_DEVICE_IND:
      if ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
      {
        ZDSecMgrUpdateDeviceInd( (ZDO_UpdateDeviceInd_t*)msgPtr );
      }
      break;

    case ZDO_REMOVE_DEVICE_IND:
      if ( ZG_BUILD_RTRONLY_TYPE && ( zgDeviceLogicalType == ZG_DEVICETYPE_ROUTER ) )
      {
        ZDSecMgrRemoveDeviceInd( (ZDO_RemoveDeviceInd_t*)msgPtr );
      }
      break;

    case ZDO_REQUEST_KEY_IND:
      if (( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH ) ||
          ( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_SE_STANDARD ))
      {
        if ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
        {
          ZDSecMgrRequestKeyInd( (ZDO_RequestKeyInd_t*)msgPtr );
        }
      }
      break;

    case ZDO_SWITCH_KEY_IND:
      if ( ZG_BUILD_JOINING_TYPE && ZG_DEVICE_JOINING_TYPE )
      {
        ZDSecMgrSwitchKeyInd( (ZDO_SwitchKeyInd_t*)msgPtr );
      }
      break;

    case ZDO_AUTHENTICATE_IND:
      if ( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH )
      {
        ZDSecMgrAuthenticateInd( (ZDO_AuthenticateInd_t*)msgPtr );
      }
      break;

    case ZDO_AUTHENTICATE_CFM:
      if ( ZG_CHECK_SECURITY_MODE == ZG_SECURITY_PRO_HIGH )
      {
        ZDSecMgrAuthenticateCfm( (ZDO_AuthenticateCfm_t*)msgPtr );
      }
      break;

    default:
      // Unsupported messages
      break;
  }
}

void ZDApp_ProcessNetworkJoin( void )
{
  if ( (devState == DEV_NWK_JOINING) ||
      ((devState == DEV_NWK_ORPHAN)  &&
       (ZDO_Config_Node_Descriptor.LogicalType == NODETYPE_ROUTER)) )
  {
    // Result of a Join attempt by this device.
    if ( nwkStatus == ZSuccess )
    {
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );

#if defined ( POWER_SAVING )
      osal_pwrmgr_device( PWRMGR_BATTERY );
#endif

      if ( ZG_SECURE_ENABLED && ( ZDApp_RestoreNwkKey() == false ) )
      {
        // wait for auth from trust center!!
        devState = DEV_END_DEVICE_UNAUTH;

        // Start the reset timer for MAX UNAUTH time
        ZDApp_ResetTimerStart( 10000 );//MAX_DEVICE_UNAUTH_TIMEOUT );
      }
      else
      {
        if ( ZSTACK_ROUTER_BUILD )
        {
          if ( devState == DEV_NWK_ORPHAN
            && ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
          {
            // Change NIB state to router for restore
            _NIB.nwkState = NWK_ROUTER;
          }
        }

        if ( devState == DEV_NWK_JOINING )
        {
          ZDApp_AnnounceNewAddress();
        }

        devState = DEV_END_DEVICE;
        if ( ZSTACK_ROUTER_BUILD )
        {
          // NOTE: first two parameters are not used, see NLMEDE.h for details
          if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
          {
            NLME_StartRouterRequest( 0, 0, false );
          }
        }
      }
    }
    else
    {
      if ( (devStartMode == MODE_RESUME) && (++retryCnt >= MAX_RESUME_RETRY) )
      {
        if ( _NIB.nwkPanId == 0xFFFF || _NIB.nwkPanId == INVALID_PAN_ID )
          devStartMode = MODE_JOIN;
        else
        {
          devStartMode = MODE_REJOIN;
          _tmpRejoinState = true;
        }
      }

      if ( (NLME_GetShortAddr() != INVALID_NODE_ADDR) ||
           (_NIB.nwkDevAddress != INVALID_NODE_ADDR) )
      {
        uint16 addr = INVALID_NODE_ADDR;
        // Invalidate nwk addr so end device does not use in its data reqs.
        _NIB.nwkDevAddress = INVALID_NODE_ADDR;
        ZMacSetReq( ZMacShortAddress, (uint8 *)&addr );
      }

      // Clear the neighbor Table and network discovery tables.
      nwkNeighborInitTable();
      NLME_NwkDiscTerm();

      zdoDiscCounter = 1;

//      ZDApp_NetworkInit( (uint16)
//                         ((NWK_START_DELAY * (osal_rand() & 0x0F)) +
//                          (NWK_START_DELAY * 5)) );
      ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
           + ((uint16)(osal_rand()& EXTENDED_JOINING_RANDOM_MASK))) );
    }
  }
  else if ( devState == DEV_NWK_ORPHAN || devState == DEV_NWK_REJOIN )
  {
    // results of an orphaning attempt by this device
    if (nwkStatus == ZSuccess)
    {
      if ( ZG_SECURE_ENABLED )
      {
        ZDApp_RestoreNwkKey();
      }

      devState = DEV_END_DEVICE;
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
      // setup Power Manager Device
#if defined ( POWER_SAVING )
      osal_pwrmgr_device( PWRMGR_BATTERY );
#endif

      if ( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE )
      {
        // The receiver is on, turn network layer polling off.
        NLME_SetPollRate( 0 );
        NLME_SetQueuedPollRate( 0 );
        NLME_SetResponseRate( 0 );
      }

      if ( ZSTACK_ROUTER_BUILD )
      {
        // NOTE: first two parameters are not used, see NLMEDE.h for details
        if ( ZDO_Config_Node_Descriptor.LogicalType != NODETYPE_DEVICE )
        {
          NLME_StartRouterRequest( 0, 0, false );
        }
      }

      ZDApp_AnnounceNewAddress();
    }
    else
    {
      if ( devStartMode == MODE_RESUME )
      {
        if ( ++retryCnt <= MAX_RESUME_RETRY )
        {
          if ( _NIB.nwkPanId == 0xFFFF || _NIB.nwkPanId == INVALID_PAN_ID )
            devStartMode = MODE_JOIN;
          else
          {
            devStartMode = MODE_REJOIN;
            _tmpRejoinState = true;
          }
        }
        // Do a normal join to the network after certain times of rejoin retries
        else if( AIB_apsUseInsecureJoin == true )
        {
          devStartMode = MODE_JOIN;
        }
      }

      // Clear the neighbor Table and network discovery tables.
      nwkNeighborInitTable();
      NLME_NwkDiscTerm();

      // setup a retry for later...
      ZDApp_NetworkInit( (uint16)(NWK_START_DELAY
           + (osal_rand()& EXTENDED_JOINING_RANDOM_MASK)) );
    }
  }
#if defined ( ZIGBEE_STOCHASTIC_ADDRESSING )
  else
  {
    // Assume from address conflict
    if ( _NIB.nwkAddrAlloc == NWK_ADDRESSING_STOCHASTIC )
    {
      // Notify the network
      ZDApp_AnnounceNewAddress();

      // Notify apps
      osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );
    }
  }
#endif
}

void ZDApp_SaveNwkKey( void )
{
  nwkActiveKeyItems keyItems;

  SSP_ReadNwkActiveKey( &keyItems );
  keyItems.frameCounter++;

  osal_nv_write( ZCD_NV_NWKKEY, 0, sizeof( nwkActiveKeyItems ),
                (void *)&keyItems );

  nwkFrameCounterChanges = 0;

  // Clear copy in RAM before return.
  osal_memset( &keyItems, 0x00, sizeof(keyItems) );

}

void ZDApp_ResetNwkKey( void )
{
  nwkActiveKeyItems keyItems;

  osal_memset( &keyItems, 0, sizeof( nwkActiveKeyItems ) );
  osal_nv_write( ZCD_NV_NWKKEY, 0, sizeof( nwkActiveKeyItems ),
                (void *)&keyItems );
}

uint8 ZDApp_RestoreNwkKey( void )
{
  nwkActiveKeyItems keyItems;
  uint8 ret = false;

  if ( osal_nv_read( ZCD_NV_NWKKEY, 0, sizeof(nwkActiveKeyItems), (void*)&keyItems )
      == ZSUCCESS )
  {
    if ( keyItems.frameCounter > 0 )
    {
      // Restore the key information
      keyItems.frameCounter += MAX_NWK_FRAMECOUNTER_CHANGES;
      nwkFrameCounter = keyItems.frameCounter;
      ret = true;
    }

    // Force a save for the first frame counter increment
    nwkFrameCounterChanges = MAX_NWK_FRAMECOUNTER_CHANGES + 1;
  }
  // Clear copy in RAM before return.
  osal_memset( &keyItems, 0x00, sizeof(keyItems) );

  return ( ret );
}

void ZDApp_ResetTimerStart( uint16 delay )
{
  // Start the rest timer
  osal_start_timerEx( ZDAppTaskID, ZDO_DEVICE_RESET, delay );
}

void ZDApp_ResetTimerCancel( void )
{
  // Cancel the reset timer
  osal_stop_timerEx( ZDAppTaskID, ZDO_DEVICE_RESET );
}

void ZDApp_LeaveCtrlInit( void )
{
  uint8 status;


  // Initialize control state
  ZDApp_LeaveCtrl = ZDAPP_LEAVE_CTRL_INIT;

  status = osal_nv_item_init( ZCD_NV_LEAVE_CTRL,
                              sizeof(ZDApp_LeaveCtrl),
                              &ZDApp_LeaveCtrl );

  if ( status == ZSUCCESS )
  {
    // Read saved control
    osal_nv_read( ZCD_NV_LEAVE_CTRL,
                  0,
                  sizeof( uint8 ),
                  &ZDApp_LeaveCtrl);
  }
}

void ZDApp_LeaveCtrlSet( uint8 ra )
{
  ZDApp_LeaveCtrl = ZDAPP_LEAVE_CTRL_SET;

  if ( ra == TRUE )
  {
    ZDApp_LeaveCtrl |= ZDAPP_LEAVE_CTRL_RA;
  }

  // Write the leave control
  osal_nv_write( ZCD_NV_LEAVE_CTRL,
                 0,
                 sizeof( uint8 ),
                 &ZDApp_LeaveCtrl);
}

void ZDApp_LeaveCtrlReset( void )
{
  // Set leave control to initialized state
  ZDApp_LeaveCtrl = ZDAPP_LEAVE_CTRL_INIT;

  // Write initialized control
  osal_nv_write( ZCD_NV_LEAVE_CTRL,
                0,
                sizeof( uint8 ),
                &ZDApp_LeaveCtrl);
}

uint8 ZDApp_LeaveCtrlBypass( void )
{
  uint8 bypass;

  if ( ZDApp_LeaveCtrl & ZDAPP_LEAVE_CTRL_SET )
  {
    bypass = TRUE;
  }
  else
  {
    bypass = FALSE;
  }

  return bypass;
}

void ZDApp_LeaveCtrlStartup( devStates_t* state, uint16* startDelay )
{
  *startDelay = 0;

  if ( ZDApp_LeaveCtrl & ZDAPP_LEAVE_CTRL_SET )
  {
    if ( ZDApp_LeaveCtrl & ZDAPP_LEAVE_CTRL_RA )
    {
      *startDelay = LEAVE_RESET_DELAY;
    }
    else
    {
      *state = DEV_HOLD;
    }

    // Reset leave control logic
    ZDApp_LeaveCtrlReset();
  }
}

void ZDApp_LeaveReset( uint8 ra )
{
  ZDApp_LeaveCtrlSet( ra );

  ZDApp_ResetTimerStart( LEAVE_RESET_DELAY );
}

void ZDApp_LeaveUpdate( uint16 nwkAddr, uint8* extAddr,
                        uint8 removeChildren )
{
  // Remove Apps Key for leaving device
  ZDSecMgrDeviceRemoveByExtAddr(extAddr);

  // Clear SECURITY bit from Address Manager
  ZDSecMgrAddrClear( extAddr );

  if ( pbindRemoveDev )
  {
    zAddrType_t devAddr;

    // Remove bind entry and all related data
    devAddr.addrMode = Addr64Bit;
    osal_memcpy(devAddr.addr.extAddr, extAddr, Z_EXTADDR_LEN);

    pbindRemoveDev(&devAddr);
  }

  // Remove if child
  if ( ZSTACK_ROUTER_BUILD )
  {
    NLME_RemoveChild( extAddr, removeChildren );
  }

  // Remove Routing table related entry
  RTG_RemoveRtgEntry( nwkAddr, 0 );

  // Remove entry from neighborTable
  nwkNeighborRemove( nwkAddr, _NIB.nwkPanId );

  // Schedule to save data to NV
  ZDApp_NwkWriteNVRequest();
}

ZStatus_t ZDApp_NetworkDiscoveryReq( uint32 scanChannels, uint8 scanDuration)
{
  // Setup optional filters - tbd

  // Request NLME network discovery
  return NLME_NetworkDiscoveryRequest(scanChannels, scanDuration);
}

ZStatus_t ZDApp_JoinReq( uint8 channel, uint16 panID, uint8 *extendedPanID,
                         uint16 chosenParent, uint8 parentDepth, uint8 stackProfile )
{
  // Sync up the node with the stack profile (In the case where a pro device
  // joins a non-pro network, or verse versa)
  ZDApp_NodeProfileSync( stackProfile);

  // Request NLME Join Request
  return NLME_JoinRequest(extendedPanID, panID,channel,
                          ZDO_Config_Node_Descriptor.CapabilityFlags,
                          chosenParent, parentDepth);

}

uint8 ZDApp_DeviceConfigured( void )
{
  uint16 nwkAddr = INVALID_NODE_ADDR;

  osal_nv_read( ZCD_NV_NIB, osal_offsetof( nwkIB_t, nwkDevAddress ),
                sizeof( uint16), &nwkAddr );

  // Does the NIB have anything more than default?
  return ( nwkAddr == INVALID_NODE_ADDR ? FALSE : TRUE );
}

void ZDApp_SendEventMsg( uint8 cmd, uint8 len, uint8 *buf )
{
  ZDApp_SendMsg( ZDAppTaskID, cmd, len, buf );
}

void ZDApp_SendMsg( uint8 taskID, uint8 cmd, uint8 len, uint8 *buf )
{
  osal_event_hdr_t *msgPtr;

  // Send the address to the task
  msgPtr = (osal_event_hdr_t *)osal_msg_allocate( len );
  if ( msgPtr )
  {
    if ( (len > 0) && (buf != NULL) )
      osal_memcpy( msgPtr, buf, len );

    msgPtr->event = cmd;
    osal_msg_send( taskID, (uint8 *)msgPtr );
  }
}

ZStatus_t ZDO_NetworkDiscoveryConfirmCB(uint8 status)
{
  osal_event_hdr_t msg;

  // If Scan is initiated by ZDO_MGMT_NWK_DISC_REQ
  // Send ZDO_MGMT_NWK_DISC_RSP back
#if defined ( ZDO_MGMT_NWKDISC_RESPONSE )
  if ( zdappMgmtNwkDiscReqInProgress )
  {
    zdappMgmtNwkDiscReqInProgress = false;
    ZDO_FinishProcessingMgmtNwkDiscReq();
  }
  else
#endif
  {
    // Pass the confirm to another task if it registers the callback
    // Otherwise, pass the confirm to ZDApp.
    if (zdoCBFunc[ZDO_NWK_DISCOVERY_CNF_CBID] != NULL )
    {
      zdoCBFunc[ZDO_NWK_DISCOVERY_CNF_CBID]( (void*)&status );
    }
    else
    {
      // Otherwise, send scan confirm to ZDApp task to proceed
      msg.status = ZDO_SUCCESS;
      ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_DISC_CNF, sizeof(osal_event_hdr_t), (uint8 *)&msg );
    }
  }
  return (ZSuccess);
}  // ZDO_NetworkDiscoveryConfirmCB

#define STACK_PROFILE_MAX 2
networkDesc_t* ZDApp_NwkDescListProcessing(void)
{
  networkDesc_t *pNwkDesc;
  uint8 i, ResultCount = 0;
  uint8 stackProfile;
  uint8 stackProfilePro;
  uint8 selected;

  // Count the number of nwk descriptors in the list
  pNwkDesc = nwk_getNwkDescList();
  while (pNwkDesc)
  {
    ResultCount++;
    pNwkDesc = pNwkDesc->nextDesc;
  }

  // process discovery results
  stackProfilePro = FALSE;
  selected = FALSE;


  for ( stackProfile = 0; stackProfile < STACK_PROFILE_MAX; stackProfile++ )
  {
    pNwkDesc = nwk_getNwkDescList();
    for ( i = 0; i < ResultCount; i++, pNwkDesc = pNwkDesc->nextDesc )
    {
      if ( zgConfigPANID != 0xFFFF )
      {
        // PAN Id is preconfigured. check if it matches
        if ( pNwkDesc->panId != zgConfigPANID )
          continue;
      }

      if ( nwk_ExtPANIDValid( ZDO_UseExtendedPANID) == true )
      {
        // If the extended Pan ID is commissioned to a non zero value
        // Only join the Pan that has match EPID
        if ( osal_ExtAddrEqual( ZDO_UseExtendedPANID, pNwkDesc->extendedPANID) == false )
          continue;

      }

      // check that network is allowing joining
      if ( ZSTACK_ROUTER_BUILD )
      {
        if ( stackProfilePro == FALSE )
        {
          if ( !pNwkDesc->routerCapacity )
          {
            continue;
          }
        }
        else
        {
          if ( !pNwkDesc->deviceCapacity )
          {
            continue;
          }
        }
      }
      else if ( ZSTACK_END_DEVICE_BUILD )
      {
        if ( !pNwkDesc->deviceCapacity )
        {
          continue;
        }
      }

      // check version of zigbee protocol
      if ( pNwkDesc->version != _NIB.nwkProtocolVersion )
        continue;

      // check version of stack profile
      if ( pNwkDesc->stackProfile != zgStackProfile  )
      {
        if ( ((zgStackProfile == HOME_CONTROLS) && (pNwkDesc->stackProfile == ZIGBEEPRO_PROFILE))
            || ((zgStackProfile == ZIGBEEPRO_PROFILE) && (pNwkDesc->stackProfile == HOME_CONTROLS))  )
        {
          stackProfilePro = TRUE;
        }

        if ( stackProfile == 0 )
        {
          continue;
        }
      }

      break;
    }

    if (i < ResultCount)
    {
     selected = TRUE;
      break;
    }

    // break if selected or stack profile pro wasn't found
    if ( (selected == TRUE) || (stackProfilePro == FALSE) )
    {
      break;
    }
  }

  if ( i == ResultCount )
  {
    return (NULL);   // couldn't find appropriate PAN to join !
  }
  else
  {
    return (pNwkDesc);
  }
}// ZDApp_NwkDescListProcessing()

void ZDO_NetworkFormationConfirmCB( ZStatus_t Status )
{
  nwkStatus = (byte)Status;

  if ( Status == ZSUCCESS )
  {
    // LED on shows Coordinator started
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );

    // LED off forgets HOLD_AUTO_START
    HalLedSet (HAL_LED_4, HAL_LED_MODE_OFF);

#if defined ( ZBIT )
    SIM_SetColor(0xd0ffd0);
#endif

    if ( devState == DEV_HOLD )
    {
      // Began with HOLD_AUTO_START
      devState = DEV_COORD_STARTING;
    }
  }
#if defined(BLINK_LEDS)
  else
  {
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH );  // Flash LED to show failure
  }
#endif

  osal_set_event( ZDAppTaskID, ZDO_NETWORK_START );
}

void ZDO_beaconNotifyIndCB( NLME_beaconInd_t *pBeacon )
{
  // Pass the beacon Indication to another task if it registers the callback
  // Otherwise, process the beacon notification here.
  if (zdoCBFunc[ZDO_BEACON_NOTIFY_IND_CBID] != NULL )
  {
    zdoCBFunc[ZDO_BEACON_NOTIFY_IND_CBID]( (void*)pBeacon );
  }
  else
  {
    networkDesc_t *pNwkDesc;
    networkDesc_t *pLastNwkDesc;
    uint8 found = false;

    // Add the network to the Network Descriptor List
    pNwkDesc = NwkDescList;
    pLastNwkDesc = NwkDescList;
    while (pNwkDesc)
    {
      if ((pNwkDesc->panId == pBeacon->panID) &&
          (pNwkDesc->logicalChannel == pBeacon->logicalChannel))
      {
        found = true;
        break;
      }
      pLastNwkDesc = pNwkDesc;
      pNwkDesc = pNwkDesc->nextDesc;
    }

    // If no existing descriptor found, make a new one and add to the list
    if (found == false)
    {
      pNwkDesc = osal_mem_alloc( sizeof(networkDesc_t)  );
      if ( !pNwkDesc )
      {
        // Memory alloc failed, discard this beacon
        return;
      }

      // Clear the network descriptor
      osal_memset( pNwkDesc, 0, sizeof(networkDesc_t)  );

      // Initialize the descriptor
      pNwkDesc->chosenRouter = INVALID_NODE_ADDR;
      pNwkDesc->chosenRouterDepth = 0xFF;

      // Save new entry into the descriptor list
      if ( !NwkDescList )
      {
        NwkDescList = pNwkDesc;
      }
      else
      {
        pLastNwkDesc->nextDesc = pNwkDesc;
      }
    }

    // Update the descriptor with the incoming beacon
    pNwkDesc->stackProfile   = pBeacon->stackProfile;
    pNwkDesc->version        = pBeacon->protocolVersion;
    pNwkDesc->logicalChannel = pBeacon->logicalChannel;
    pNwkDesc->panId          = pBeacon->panID;
    pNwkDesc->updateId       = pBeacon->updateID;

    // Save the extended PAN ID from the beacon payload only if 1.1 version network
    if ( pBeacon->protocolVersion != ZB_PROT_V1_0 )
    {
      osal_cpyExtAddr( pNwkDesc->extendedPANID, pBeacon->extendedPanID );
    }
    else
    {
      osal_memset( pNwkDesc->extendedPANID, 0xFF, Z_EXTADDR_LEN );
    }

    // check if this device is a better choice to join...
    // ...dont bother checking assocPermit flag is doing a rejoin
    if ( ( pBeacon->LQI > gMIN_TREE_LINK_COST ) &&
        ( ( pBeacon->permitJoining == TRUE ) || ( _tmpRejoinState ) ) )
    {
      uint8 selected = FALSE;
      uint8 capacity = FALSE;

      if ( _NIB.nwkAddrAlloc == NWK_ADDRESSING_STOCHASTIC )
      {
        if ( ((pBeacon->LQI   > pNwkDesc->chosenRouterLinkQuality) &&
              (pBeacon->depth < MAX_NODE_DEPTH)) ||
            ((pBeacon->LQI   == pNwkDesc->chosenRouterLinkQuality) &&
             (pBeacon->depth < pNwkDesc->chosenRouterDepth)) )
        {
          selected = TRUE;
        }
      }
      else
      {
        if ( pBeacon->depth < pNwkDesc->chosenRouterDepth )
        {
          selected = TRUE;
        }
      }

      if ( ZSTACK_ROUTER_BUILD )
      {
        capacity = pBeacon->routerCapacity;
      }
      else if ( ZSTACK_END_DEVICE_BUILD )
      {
        capacity = pBeacon->deviceCapacity;
      }

      if ( (capacity) && (selected) )
      {
        // this is the new chosen router for joining...
        pNwkDesc->chosenRouter            = pBeacon->sourceAddr;
        pNwkDesc->chosenRouterLinkQuality = pBeacon->LQI;
        pNwkDesc->chosenRouterDepth       = pBeacon->depth;
      }

      if ( pBeacon->deviceCapacity )
        pNwkDesc->deviceCapacity = 1;

      if ( pBeacon->routerCapacity )
        pNwkDesc->routerCapacity = 1;
    }
  }
}

void ZDO_StartRouterConfirmCB( ZStatus_t Status )
{
  nwkStatus = (byte)Status;

  if ( Status == ZSUCCESS )
  {
    // LED on shows Router started
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );
    // LED off forgets HOLD_AUTO_START
    HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF);
    if ( devState == DEV_HOLD )
    {
      // Began with HOLD_AUTO_START
      devState = DEV_END_DEVICE;
    }
  }
#if defined(BLINK_LEDS)
  else
  {
    HalLedSet( HAL_LED_3, HAL_LED_MODE_FLASH );  // Flash LED to show failure
  }
#endif

  osal_set_event( ZDAppTaskID, ZDO_ROUTER_START );
}

void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status )
{
  (void)PanId;  // remove if this parameter is used.

  nwkStatus = (byte)Status;

  if ( Status == ZSUCCESS )
  {
    // LED on shows device joined
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_ON );
    // LED off forgets HOLD_AUTO_START
    HalLedSet ( HAL_LED_4, HAL_LED_MODE_OFF);
    if ( (devState == DEV_HOLD) )
    {
      // Began with HOLD_AUTO_START
      devState = DEV_NWK_JOINING;
    }

    if ( !ZG_SECURE_ENABLED )
    {
      // Notify to save info into NV
      ZDApp_NVUpdate();
    }
  }
  else
  {
#if defined(BLINK_LEDS)
    HalLedSet ( HAL_LED_3, HAL_LED_MODE_FLASH );  // Flash LED to show failure
#endif
  }

  // Pass the join confirm to higher layer if callback registered
  if (zdoCBFunc[ZDO_JOIN_CNF_CBID] != NULL )
  {
    zdoJoinCnf_t joinCnf;

    joinCnf.status = Status;
    joinCnf.deviceAddr = _NIB.nwkDevAddress;
    joinCnf.parentAddr = _NIB.nwkCoordAddress;

    zdoCBFunc[ZDO_JOIN_CNF_CBID]( (void*)&joinCnf );
  }

  // Notify ZDApp
  ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_IND, sizeof(osal_event_hdr_t), (byte*)NULL );

}

void ZDO_AddrChangeIndicationCB( uint16 newAddr )
{
  ZDO_AddrChangeInd_t *pZDOAddrChangeMsg;
  epList_t *pItem = epList;

  // Notify to save info into NV
  ZDApp_NVUpdate();

  // Notify the applications
  osal_set_event( ZDAppTaskID, ZDO_STATE_CHANGE_EVT );

  while (pItem != NULL)
  {
    if (pItem->epDesc->endPoint != ZDO_EP)
    {
      pZDOAddrChangeMsg = (ZDO_AddrChangeInd_t *)osal_msg_allocate( sizeof( ZDO_AddrChangeInd_t ) );
      if (pZDOAddrChangeMsg != NULL)
      {
        pZDOAddrChangeMsg->hdr.event = ZDO_ADDR_CHANGE_IND;
        pZDOAddrChangeMsg->shortAddr = newAddr;
        osal_msg_send( *(pItem->epDesc->task_id), (uint8 *)pZDOAddrChangeMsg );
      }
    }
    pItem = pItem->nextDesc;
  }

  // Send out a device announce
  ZDApp_AnnounceNewAddress();
}

ZStatus_t ZDO_JoinIndicationCB(uint16 ShortAddress, uint8 *ExtendedAddress,
                                uint8 CapabilityFlags, uint8 type)
{
  (void)ShortAddress;
  (void)ExtendedAddress;
#if ZDO_NV_SAVE_RFDs
  (void)CapabilityFlags;

#else  // if !ZDO_NV_SAVE_RFDs
  if (CapabilityFlags & CAPINFO_DEVICETYPE_FFD)
#endif
  {
    ZDApp_NVUpdate();  // Notify to save info into NV.
  }

  if (ZG_SECURE_ENABLED)  // Send notification to TC of new device.
  {
    if (type == NWK_ASSOC_JOIN || type == NWK_ASSOC_REJOIN_UNSECURE)
    {
      osal_start_timerEx( ZDAppTaskID, ZDO_NEW_DEVICE, 600 );
    }
  }

  return ZSuccess;
}

void ZDO_ConcentratorIndicationCB( uint16 nwkAddr, uint8 *extAddr, uint8 pktCost )
{
  zdoConcentratorInd_t conInd;

  conInd.nwkAddr = nwkAddr;
  conInd.extAddr = extAddr;
  conInd.pktCost = pktCost;

  if( zdoCBFunc[ZDO_CONCENTRATOR_IND_CBID] != NULL )
  {
    zdoCBFunc[ZDO_CONCENTRATOR_IND_CBID]( (void*)&conInd );
  }
}

void ZDO_LeaveCnf( NLME_LeaveCnf_t* cnf )
{
  // Check for this device
  if ( osal_ExtAddrEqual( cnf->extAddr,
                          NLME_GetExtAddr() ) == TRUE )
  {
    // Pass the leave confirm to higher layer if callback registered
    if ( ( zdoCBFunc[ZDO_LEAVE_CNF_CBID] == NULL ) ||
         ( (*zdoCBFunc[ZDO_LEAVE_CNF_CBID])( cnf ) == NULL ) )
    {
      // Prepare to leave with reset
      ZDApp_LeaveReset( cnf->rejoin );
    }
  }
  else if ( ZSTACK_ROUTER_BUILD )
  {
    // Remove device address(optionally descendents) from data
    ZDApp_LeaveUpdate( cnf->dstAddr,
                       cnf->extAddr,
                       cnf->removeChildren );
  }
}

void ZDO_LeaveInd( NLME_LeaveInd_t* ind )
{
  uint8 leave;


  // Parent is requesting the leave - NWK layer filters out illegal
  // requests
  if ( ind->request == TRUE )
  {
    // Notify network of leave
    if ( ZSTACK_ROUTER_BUILD )
    {
      NLME_LeaveRsp_t rsp;
      rsp.rejoin         = ind->rejoin;
      rsp.removeChildren = ind->removeChildren;
      NLME_LeaveRsp( &rsp );
    }

    // Prepare to leave with reset
    ZDApp_LeaveReset( ind->rejoin );
  }
  else
  {
    leave = FALSE;

    // Check if this device needs to leave as a child or descendent
    if ( ind->srcAddr == NLME_GetCoordShortAddr() )
    {
      if ( ( ind->removeChildren == TRUE               ) ||
           ( ZDO_Config_Node_Descriptor.LogicalType ==
             NODETYPE_DEVICE                           )    )
      {
        leave = TRUE;
      }
    }
    else if ( ind->removeChildren == TRUE )
    {
      // Check NWK address allocation algorithm
      //leave = RTG_ANCESTOR(nwkAddr,thisAddr);
    }

    if ( leave == TRUE )
    {
      // Prepare to leave with reset
      ZDApp_LeaveReset( ind->rejoin );
    }
    else
    {
      // Remove device address(optionally descendents) from data
      ZDApp_LeaveUpdate( ind->srcAddr,
                         ind->extAddr,
                         ind->removeChildren );
    }
  }

  // Pass the leave indication to higher layer if callback registered.
  if (zdoCBFunc[ZDO_LEAVE_IND_CBID] != NULL)
  {
    (void)zdoCBFunc[ZDO_LEAVE_IND_CBID](ind);
  }
}

void ZDO_SyncIndicationCB( uint8 type, uint16 shortAddr )
{
  (void)shortAddr;  // Remove this line if this parameter is used.

  if ( ZSTACK_END_DEVICE_BUILD
    || (ZSTACK_ROUTER_BUILD && ((_NIB.CapabilityFlags & ZMAC_ASSOC_CAPINFO_FFD_TYPE) == 0)))
  {
    if ( type == 1 )
    {
      // We lost contact with our parent.  Clear the neighbor Table.
      nwkNeighborInitTable();

      // Start the rejoin process.
      ZDApp_SendMsg( ZDAppTaskID, ZDO_NWK_JOIN_REQ, sizeof(osal_event_hdr_t), NULL );
    }
  }
}

void ZDO_ManytoOneFailureIndicationCB()
{
  // By default, the concentrator automatically redo many-to-one route
  // discovery to update all many-to-one routes in the network
  // If you want anything processing other than the default,
  // please replace the following code.

  RTG_MTORouteReq();
}

void ZDO_PollConfirmCB( uint8 status )
{
  (void)status;  // Remove this line if this parameter is used.
  return;
}

void ZDApp_NwkWriteNVRequest( void )
{
#if defined ( NV_RESTORE )
  if ( !osal_get_timeoutEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV ) )
  {
    // Trigger to save info into NV
    ZDApp_NVUpdate();
  }
#endif
}

ZStatus_t ZDO_UpdateDeviceIndication( uint8 *extAddr, uint8 status )
{
  // can implement a network access policy based on the
  // IEEE address of newly joining devices...
  (void)extAddr;
  (void)status;

  return ZSuccess;
}

void ZDApp_InMsgCB( zdoIncomingMsg_t *inMsg )
{
  if ( inMsg->clusterID & ZDO_RESPONSE_BIT )
  {
    // Handle the response message
  }
  else
  {
    // Handle the request message by sending a generic "not supported".
    // Device Announce doesn't have a response.
    if ( !(inMsg->wasBroadcast) && inMsg->clusterID != Device_annce )
    {
      ZDP_GenericRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_NOT_SUPPORTED, 0,
                      (uint16)(inMsg->clusterID | ZDO_RESPONSE_BIT), inMsg->SecurityUse );
    }
  }
}

void ZDApp_ChangeMatchDescRespPermission( uint8 endpoint, uint8 action )
{
  // Store the action
  afSetMatch( endpoint, action );
}

void ZDApp_NetworkInit( uint16 delay )
{
  if ( delay )
  {
    // Wait awhile before starting the device
    osal_start_timerEx( ZDAppTaskID, ZDO_NETWORK_INIT, delay );
  }
  else
  {
    osal_set_event( ZDAppTaskID, ZDO_NETWORK_INIT );
  }
}

void ZDApp_NwkStateUpdateCB( void )
{
  // Notify to save info into NV
  if ( !osal_get_timeoutEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV ) )
  {
    // Trigger to save info into NV
    ZDApp_NVUpdate();
  }
}

void ZDApp_NodeProfileSync( uint8 stackProfile )
{
  if ( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_DEVICETYPE_FFD  )
  {
    if ( stackProfile != zgStackProfile )
    {
      ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_DEVICE;
      ZDO_Config_Node_Descriptor.CapabilityFlags = CAPINFO_DEVICETYPE_RFD | CAPINFO_POWER_AC | CAPINFO_RCVR_ON_IDLE;
      NLME_SetBroadcastFilter( ZDO_Config_Node_Descriptor.CapabilityFlags );
    }
  }
}

uint8 ZDApp_StartJoiningCycle( void )
{
  if ( devState == DEV_INIT || devState == DEV_NWK_DISC )
  {
    continueJoining = TRUE;
    ZDApp_NetworkInit( 0 );

    return ( TRUE );
  }
  else
    return ( FALSE );
}

uint8 ZDApp_StopJoiningCycle( void )
{
  if ( devState == DEV_INIT || devState == DEV_NWK_DISC )
  {
    continueJoining = FALSE;
    return ( TRUE );
  }
  else
    return ( FALSE );
}

void ZDApp_AnnounceNewAddress( void )
{
#if defined ( ZIGBEE_NWK_UNIQUE_ADDR_CHECK )
  // Turn off data request hold
  APSME_HoldDataRequests( 0 );
#endif

  ZDP_DeviceAnnce( NLME_GetShortAddr(), NLME_GetExtAddr(),
                     ZDO_Config_Node_Descriptor.CapabilityFlags, 0 );

#if defined ( ZIGBEE_NWK_UNIQUE_ADDR_CHECK )
  // Setup the timeout
  APSME_HoldDataRequests( ZDAPP_HOLD_DATA_REQUESTS_TIMEOUT );
#endif
}

void ZDApp_NVUpdate( void )
{
#if defined ( NV_RESTORE )
  osal_start_timerEx( ZDAppTaskID, ZDO_NWK_UPDATE_NV, ZDAPP_UPDATE_NWK_NV_TIME );
#endif
}

uint16 ZDApp_CoordStartPANIDConflictCB( uint16 panid )
{
  return ( panid + 1 );
}

void ZDO_SrcRtgIndCB (uint16 srcAddr, uint8 relayCnt, uint16* pRelayList )
{
  zdoSrcRtg_t srcRtg;

  srcRtg.srcAddr = srcAddr;
  srcRtg.relayCnt = relayCnt;
  srcRtg.pRelayList = pRelayList;

  if( zdoCBFunc[ZDO_SRC_RTG_IND_CBID] != NULL )
  {
    zdoCBFunc[ZDO_SRC_RTG_IND_CBID]( (void*)&srcRtg );
  }
}

void ZDApp_InitZdoCBFunc( void )
{
  uint8 i;

  for ( i=0; i< MAX_ZDO_CB_FUNC; i++ )
  {
    zdoCBFunc[i] = NULL;
  }
}

ZStatus_t ZDO_RegisterForZdoCB( uint8 indID, pfnZdoCb pFn )
{
  // Check the range of the indication ID
  if ( indID < MAX_ZDO_CB_FUNC )
  {
    zdoCBFunc[indID] = pFn;
    return ZSuccess;
  }

  return ZInvalidParameter;
}

ZStatus_t ZDO_DeregisterForZdoCB( uint8 indID )
{
  // Check the range of the indication ID
  if ( indID < MAX_ZDO_CB_FUNC )
  {
    zdoCBFunc[indID] = NULL;
    return ZSuccess;
  }

  return ZInvalidParameter;
}

ZDApp.h

#ifndef ZDAPP_H
#define ZDAPP_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "ZComDef.h"
#include "ZMac.h"
#include "NLMEDE.h"
#include "APS.h"
#include "AF.h"
#include "ZDProfile.h"


// Set this value for use in choosing a PAN to join
// modify ZDApp.c to enable this...
#define ZDO_CONFIG_MAX_BO             15

  // Task Events
#define ZDO_NETWORK_INIT          0x0001
#define ZDO_NETWORK_START         0x0002
#define ZDO_DEVICE_RESET          0x0004
#define ZDO_COMMAND_CNF           0x0008
#define ZDO_STATE_CHANGE_EVT      0x0010
#define ZDO_ROUTER_START          0x0020
#define ZDO_NEW_DEVICE            0x0040
#define ZDO_DEVICE_AUTH           0x0080
#define ZDO_SECMGR_EVENT          0x0100
#define ZDO_NWK_UPDATE_NV         0x0200
#define ZDO_FRAMECOUNTER_CHANGE   0x0400
#define ZDO_TCLK_FRAMECOUNTER_CHANGE  0x0800
#define ZDO_APS_FRAMECOUNTER_CHANGE   0x1000

// Incoming to ZDO
#define ZDO_NWK_DISC_CNF        0x01
#define ZDO_NWK_JOIN_IND        0x02
#define ZDO_NWK_JOIN_REQ        0x03
#define ZDO_ESTABLISH_KEY_CFM   0x04
#define ZDO_ESTABLISH_KEY_IND   0x05
#define ZDO_TRANSPORT_KEY_IND   0x06
#define ZDO_UPDATE_DEVICE_IND   0x07
#define ZDO_REMOVE_DEVICE_IND   0x08
#define ZDO_REQUEST_KEY_IND     0x09
#define ZDO_SWITCH_KEY_IND      0x0A
#define ZDO_AUTHENTICATE_IND    0x0B
#define ZDO_AUTHENTICATE_CFM    0x0C

//  ZDO command message fields
#define ZDO_CMD_ID     0
#define ZDO_CMD_ID_LEN 1

//  ZDO security message fields
#define ZDO_ESTABLISH_KEY_CFM_LEN   \
  ((uint8)                          \
   (sizeof(ZDO_EstablishKeyCfm_t) ) )

#define ZDO_ESTABLISH_KEY_IND_LEN   \
  ((uint8)                          \
   (sizeof(ZDO_EstablishKeyInd_t) ) )

#define ZDO_TRANSPORT_KEY_IND_LEN   \
  ((uint8)                          \
   (sizeof(ZDO_TransportKeyInd_t) ) )

#define ZDO_UPDATE_DEVICE_IND_LEN   \
  ((uint8)                          \
   (sizeof(ZDO_UpdateDeviceInd_t) ) )

#define ZDO_REMOVE_DEVICE_IND_LEN   \
  ((uint8)                          \
   (sizeof(ZDO_RemoveDeviceInd_t) ) )

#define ZDO_REQUEST_KEY_IND_LEN   \
  ((uint8)                        \
   (sizeof(ZDO_RequestKeyInd_t) ) )

#define ZDO_SWITCH_KEY_IND_LEN   \
  ((uint8)                       \
   (sizeof(ZDO_SwitchKeyInd_t) ) )

#define ZDO_AUTHENTICATE_IND_LEN   \
  ((uint8)                       \
   (sizeof(ZDO_AuthenticateInd_t) ) )

#define ZDO_AUTHENTICATE_CFM_LEN   \
  ((uint8)                       \
   (sizeof(ZDO_AuthenticateCfm_t) ) )

#define NWK_RETRY_DELAY             1000   // in milliseconds

#define ZDO_MATCH_DESC_ACCEPT_ACTION    1   // Message field

#if !defined NUM_DISC_ATTEMPTS
#define NUM_DISC_ATTEMPTS           2
#endif

// ZDOInitDevice return values
#define ZDO_INITDEV_RESTORED_NETWORK_STATE      0x00
#define ZDO_INITDEV_NEW_NETWORK_STATE           0x01
#define ZDO_INITDEV_LEAVE_NOT_STARTED           0x02

#if defined ( MANAGED_SCAN )
  // Only use in a battery powered device

  // This is the number of times a channel is scanned at the shortest possible
  // scan time (which is 30 MS (2 x 15).  The idea is to scan one channel at a
  // time (from the channel list), but scan it multiple times.
  #define MANAGEDSCAN_TIMES_PRE_CHANNEL         5
  #define MANAGEDSCAN_DELAY_BETWEEN_SCANS       150   // milliseconds

  extern uint8 zdoDiscCounter;

#endif // MANAGED_SCAN

// Use the following to delay application data request after startup.
#define ZDAPP_HOLD_DATA_REQUESTS_TIMEOUT        0 // in milliseconds

typedef enum
{
  DEV_HOLD,               // Initialized - not started automatically
  DEV_INIT,               // Initialized - not connected to anything
  DEV_NWK_DISC,           // Discovering PAN's to join
  DEV_NWK_JOINING,        // Joining a PAN
  DEV_NWK_REJOIN,         // ReJoining a PAN, only for end devices
  DEV_END_DEVICE_UNAUTH,  // Joined but not yet authenticated by trust center
  DEV_END_DEVICE,         // Started as device after authentication
  DEV_ROUTER,             // Device joined, authenticated and is a router
  DEV_COORD_STARTING,     // Started as Zigbee Coordinator
  DEV_ZB_COORD,           // Started as Zigbee Coordinator
  DEV_NWK_ORPHAN          // Device has lost information about its parent..
} devStates_t;

typedef enum
{
  ZDO_SUCCESS,
  ZDO_FAIL
} zdoStatus_t;


typedef struct
{
  osal_event_hdr_t hdr;
  uint8       dstAddrDstEP;
  zAddrType_t dstAddr;
  uint8       dstAddrClusterIDLSB;
  uint8       dstAddrClusterIDMSB;
  uint8       dstAddrRemove;
  uint8       dstAddrEP;
} ZDO_NewDstAddr_t;

//  ZDO security message types
typedef struct
{
  osal_event_hdr_t hdr;
  uint8            partExtAddr[Z_EXTADDR_LEN];
  uint8            status;
} ZDO_EstablishKeyCfm_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16           srcAddr;
  uint8            initExtAddr[Z_EXTADDR_LEN];
  uint8            method;
  uint8            apsSecure;
  uint8            nwkSecure;
  //devtag.0604.todo - remove obsolete
} ZDO_EstablishKeyInd_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16           srcAddr;
  uint8            keyType;
  uint8            keySeqNum;
  uint8            key[SEC_KEY_LEN];
  uint8            srcExtAddr[Z_EXTADDR_LEN];
  uint8            initiator;
  uint8            secure;
} ZDO_TransportKeyInd_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16           srcAddr;
  uint8            devExtAddr[Z_EXTADDR_LEN];
  uint16           devAddr;
  uint8            status;
} ZDO_UpdateDeviceInd_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16           srcAddr;
  uint8            childExtAddr[Z_EXTADDR_LEN];
} ZDO_RemoveDeviceInd_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16           srcAddr;
  uint8            keyType;
  uint8            partExtAddr[Z_EXTADDR_LEN];
} ZDO_RequestKeyInd_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16           srcAddr;
  uint8            keySeqNum;
} ZDO_SwitchKeyInd_t;

typedef struct
{
  osal_event_hdr_t        hdr;
  APSME_AuthenticateInd_t aps;
} ZDO_AuthenticateInd_t;

typedef struct
{
  osal_event_hdr_t        hdr;
  APSME_AuthenticateCfm_t aps;
} ZDO_AuthenticateCfm_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16 nwkAddr;
  uint8 numInClusters;
  uint16 *pInClusters;
  uint8 numOutClusters;
  uint16 *pOutClusters;
} ZDO_MatchDescRspSent_t;

typedef struct
{
  osal_event_hdr_t hdr;
  uint16 shortAddr;
} ZDO_AddrChangeInd_t;

/* ZDO Indication Callback Registration */
typedef void* (*pfnZdoCb)( void *param );

/* ZDO Indication callback ID */
enum
{
  ZDO_SRC_RTG_IND_CBID,
  ZDO_CONCENTRATOR_IND_CBID,
  ZDO_NWK_DISCOVERY_CNF_CBID,
  ZDO_BEACON_NOTIFY_IND_CBID,
  ZDO_JOIN_CNF_CBID,
  ZDO_LEAVE_CNF_CBID,
  ZDO_LEAVE_IND_CBID,
  MAX_ZDO_CB_FUNC               // Must be at the bottom of the list
};

typedef struct
{
  uint16 srcAddr;
  uint8  relayCnt;
  uint16 *pRelayList;
} zdoSrcRtg_t;

typedef struct
{
  uint16 nwkAddr;
  uint8  *extAddr;
  uint8  pktCost;
} zdoConcentratorInd_t;

/* Keep the same format as NLME_beaconInd_t */
typedef struct
{
  uint16 sourceAddr;  /* Short address of the device sends the beacon */
  uint16 panID;
  uint8  logicalChannel;
  uint8	 permitJoining;
  uint8	 routerCapacity; 	
  uint8	 deviceCapacity;  	
  uint8  protocolVersion;  		
  uint8  stackProfile ;
  uint8	 LQI ;
  uint8  depth ;
  uint8  updateID;
  uint8  extendedPanID[8];
} zdoBeaconInd_t;

typedef struct
{
  uint8  status;
  uint16 deviceAddr;
  uint16 parentAddr;
} zdoJoinCnf_t;

/*********************************************************************
 * GLOBAL VARIABLES
 */
extern uint8 ZDAppTaskID;
extern uint8 nwkStatus;
extern devStates_t devState;

/* Always kept up to date by the network state changed logic, so use this addr
 * in function calls the require my network address as one of the parameters.
 */
extern zAddrType_t ZDAppNwkAddr;
extern uint8 saveExtAddr[];  // Loaded with value by ZDApp_Init().

extern uint8 zdappMgmtNwkDiscRspTransSeq;
extern uint8 zdappMgmtNwkDiscReqInProgress;
extern zAddrType_t zdappMgmtNwkDiscRspAddr;
extern uint8 zdappMgmtNwkDiscStartIndex;
extern uint8 zdappMgmtSavedNwkState;

extern uint8 ZDO_UseExtendedPANID[Z_EXTADDR_LEN];


extern void ZDO_AddrChangeIndicationCB( uint16 newAddr );

  extern void ZDApp_Init( uint8 task_id );

  extern UINT16 ZDApp_event_loop( uint8 task_id, UINT16 events );

extern uint8 ZDOInitDevice( uint16 startDelay );

extern void ZDApp_SendEventMsg( uint8 cmd, uint8 len, uint8 *buf );

extern ZStatus_t ZDApp_EstablishKey( uint8  endPoint,
                                     uint16 nwkAddr,
                                     uint8* extAddr );


extern void ZDApp_NetworkInit( uint16 delay );

extern ZStatus_t ZDApp_NetworkDiscoveryReq( uint32 scanChannels, uint8 scanDuration);

extern ZStatus_t ZDApp_JoinReq( uint8 channel, uint16 panID,
                                uint8 *extendedPanID, uint16 chosenParent,
                                uint8 parentDepth, uint8 stackProfile);


extern ZStatus_t ZDO_NetworkDiscoveryConfirmCB( uint8 status );

extern void ZDO_NetworkFormationConfirmCB( ZStatus_t Status );

extern void ZDO_beaconNotifyIndCB( NLME_beaconInd_t *beacon );

extern void ZDO_JoinConfirmCB( uint16 PanId, ZStatus_t Status );

ZStatus_t ZDO_JoinIndicationCB(uint16 ShortAddress, uint8 *ExtendedAddress,
                                 uint8 CapabilityFlags, uint8 type);

extern void ZDO_ConcentratorIndicationCB( uint16 nwkAddr, uint8 *extAddr, uint8 pktCost );

extern void ZDO_StartRouterConfirmCB( ZStatus_t Status );

extern void ZDO_LeaveCnf( NLME_LeaveCnf_t* cnf );

extern void ZDO_LeaveInd( NLME_LeaveInd_t* ind );

extern void ZDO_SyncIndicationCB( uint8 type, uint16 shortAddr );

extern void ZDO_ManytoOneFailureIndicationCB( void );

extern void ZDO_PollConfirmCB( uint8 status );

extern ZStatus_t ZDO_UpdateDeviceIndication( uint8 *extAddr, uint8 status );

extern void ZDApp_InMsgCB( zdoIncomingMsg_t *inMsg );

extern void ZDO_StartRouterConfirm( ZStatus_t Status );

extern void ZDApp_NwkStateUpdateCB( void );

extern void ZDApp_ChangeMatchDescRespPermission( uint8 endpoint, uint8 action );

extern void ZDApp_SaveNwkKey( void );

extern void ZDApp_ResetNwkKey( void );

extern uint8 ZDApp_StartJoiningCycle( void );

extern uint8 ZDApp_StopJoiningCycle( void );

extern void ZDApp_AnnounceNewAddress( void );

extern void ZDApp_NVUpdate( void );

extern uint16 ZDApp_CoordStartPANIDConflictCB( uint16 panid );

extern void ZDApp_LeaveReset( uint8 ra );

extern void ZDApp_LeaveCtrlReset( void );

extern uint8 ZDApp_DeviceConfigured( void );

extern void ZDO_SrcRtgIndCB (uint16 srcAddr, uint8 relayCnt, uint16* pRelayList );

extern ZStatus_t ZDO_RegisterForZdoCB( uint8 indID, pfnZdoCb pFn );

extern ZStatus_t ZDO_DeregisterForZdoCB( uint8 indID );

#ifdef __cplusplus
}
#endif

#endif /* ZDOBJECT_H */

ZDConfig.c

#include "ZComdef.h"
#include "AF.h"
#include "ZDObject.h"
#include "ZDConfig.h"

NodeDescriptorFormat_t ZDO_Config_Node_Descriptor;
NodePowerDescriptorFormat_t ZDO_Config_Power_Descriptor;

void ZDConfig_InitDescriptors( void )
{
  ZDConfig_UpdateNodeDescriptor();
  ZDConfig_UpdatePowerDescriptor();
}

void ZDConfig_UpdateNodeDescriptor( void )
{
  // Build the Node Descriptor
  if ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
    ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_COORDINATOR;
  else if ( ZSTACK_ROUTER_BUILD )
    ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_ROUTER;
  else if ( ZSTACK_END_DEVICE_BUILD )
    ZDO_Config_Node_Descriptor.LogicalType = NODETYPE_DEVICE;
  
  ZDO_Config_Node_Descriptor.ComplexDescAvail = FALSE;      // set elsewhere
  ZDO_Config_Node_Descriptor.UserDescAvail = FALSE;         // set elsewhere
  ZDO_Config_Node_Descriptor.Reserved = 0;                  // Reserved
  ZDO_Config_Node_Descriptor.APSFlags = 0;                  // NO APS flags
  ZDO_Config_Node_Descriptor.FrequencyBand = NODEFREQ_2400; // Frequency Band
  
  // MAC Capabilities
  if ( ZSTACK_ROUTER_BUILD )
  {
    ZDO_Config_Node_Descriptor.CapabilityFlags 
              = (CAPINFO_DEVICETYPE_FFD | CAPINFO_POWER_AC | CAPINFO_RCVR_ON_IDLE);
    
    if ( ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
      ZDO_Config_Node_Descriptor.CapabilityFlags |= CAPINFO_ALTPANCOORD;
  }
  else if ( ZSTACK_END_DEVICE_BUILD )
  {
    ZDO_Config_Node_Descriptor.CapabilityFlags = (CAPINFO_DEVICETYPE_RFD
  #if ( RFD_RCVC_ALWAYS_ON == TRUE)
            | CAPINFO_RCVR_ON_IDLE
  #endif
        );
  }
  
  // Manufacturer Code - *YOU FILL IN*
  ZDO_Config_Node_Descriptor.ManufacturerCode[0] = 0;
  ZDO_Config_Node_Descriptor.ManufacturerCode[1] = 0;
  
  // Maximum Buffer Size
  ZDO_Config_Node_Descriptor.MaxBufferSize = MAX_BUFFER_SIZE;

  // Maximum Incoming Transfer Size Field
  ZDO_Config_Node_Descriptor.MaxInTransferSize[0] = LO_UINT16( MAX_TRANSFER_SIZE );
  ZDO_Config_Node_Descriptor.MaxInTransferSize[1] = HI_UINT16( MAX_TRANSFER_SIZE );
  
  // Maximum Outgoing Transfer Size Field
  ZDO_Config_Node_Descriptor.MaxOutTransferSize[0] = LO_UINT16( MAX_TRANSFER_SIZE );
  ZDO_Config_Node_Descriptor.MaxOutTransferSize[1] = HI_UINT16( MAX_TRANSFER_SIZE );
  
  // Server Mask
  ZDO_Config_Node_Descriptor.ServerMask = 0;
  
  // Descriptor Capability Field - extended active endpoint list and 
  // extended simple descriptor are not supported.
  ZDO_Config_Node_Descriptor.DescriptorCapability = 0;
}

void ZDConfig_UpdatePowerDescriptor( void )
{
  // Build the Power Descriptor  
  if ( ZSTACK_ROUTER_BUILD )
  {
    ZDO_Config_Power_Descriptor.PowerMode = NODECURPWR_RCVR_ALWAYS_ON;
    ZDO_Config_Power_Descriptor.AvailablePowerSources = NODEAVAILPWR_MAINS;
    ZDO_Config_Power_Descriptor.CurrentPowerSource = NODEAVAILPWR_MAINS;
    ZDO_Config_Power_Descriptor.CurrentPowerSourceLevel = NODEPOWER_LEVEL_100;
  }
  else if ( ZSTACK_END_DEVICE_BUILD )
  {
    if ( zgPollRate )
      ZDO_Config_Power_Descriptor.PowerMode = NODECURPWR_RCVR_AUTO;
    else
      ZDO_Config_Power_Descriptor.PowerMode = NODECURPWR_RCVR_STIM;
      
    ZDO_Config_Power_Descriptor.AvailablePowerSources = NODEAVAILPWR_RECHARGE;
    ZDO_Config_Power_Descriptor.CurrentPowerSource = NODEAVAILPWR_RECHARGE;
    ZDO_Config_Power_Descriptor.CurrentPowerSourceLevel = NODEPOWER_LEVEL_66;
  }
}

ZDConfig.h

#ifndef ZDCONFIG_H
#define ZDCONFIG_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "AF.h"

#if defined ( MT_ZDO_FUNC )
  // All of the ZDO functions are enabled for ZTool use.
  #define ZDO_NWKADDR_REQUEST
  #define ZDO_IEEEADDR_REQUEST
  #define ZDO_MATCH_REQUEST
  #define ZDO_NODEDESC_REQUEST
  #define ZDO_POWERDESC_REQUEST
  #define ZDO_SIMPLEDESC_REQUEST
  #define ZDO_ACTIVEEP_REQUEST

  #define ZDO_COMPLEXDESC_REQUEST
  #define ZDO_USERDESC_REQUEST
  #define ZDO_USERDESCSET_REQUEST
  #define ZDO_ENDDEVICEBIND_REQUEST
  #define ZDO_BIND_UNBIND_REQUEST
  #define ZDO_SERVERDISC_REQUEST
  #define ZDO_NETWORKSTART_REQUEST
  #define ZDO_MANUAL_JOIN

  #define ZDO_COMPLEXDESC_RESPONSE
  #define ZDO_USERDESC_RESPONSE
  #define ZDO_USERDESCSET_RESPONSE
  #define ZDO_SERVERDISC_RESPONSE
  #define ZDO_ENDDEVICE_ANNCE

#if defined ( MT_ZDO_MGMT )
  #define ZDO_MGMT_NWKDISC_REQUEST
  #define ZDO_MGMT_LQI_REQUEST
  #define ZDO_MGMT_RTG_REQUEST
  #define ZDO_MGMT_BIND_REQUEST
  #define ZDO_MGMT_LEAVE_REQUEST
  #define ZDO_MGMT_JOINDIRECT_REQUEST
  #define ZDO_MGMT_PERMIT_JOIN_REQUEST
  #define ZDO_MGMT_NWKUPDATE_REQUEST
  #define ZDO_MGMT_NWKDISC_RESPONSE
  #define ZDO_MGMT_LQI_RESPONSE
  #define ZDO_MGMT_RTG_RESPONSE
  #define ZDO_MGMT_BIND_RESPONSE
  #define ZDO_MGMT_LEAVE_RESPONSE
  #define ZDO_MGMT_JOINDIRECT_RESPONSE
  #define ZDO_MGMT_PERMIT_JOIN_RESPONSE
  #define ZDO_MGMT_NWKUPDATE_NOTIFY
#endif

#else // !MT_ZDO_FUNC

  // Normal operation and sample apps only use End Device Bind
  // and Match Request.

  //#define ZDO_NWKADDR_REQUEST
  //#define ZDO_IEEEADDR_REQUEST
  #define ZDO_MATCH_REQUEST
  //#define ZDO_NODEDESC_REQUEST
  //#define ZDO_POWERDESC_REQUEST
  //#define ZDO_SIMPLEDESC_REQUEST
  //#define ZDO_ACTIVEEP_REQUEST
  //#define ZDO_COMPLEXDESC_REQUEST
  //#define ZDO_USERDESC_REQUEST
  //#define ZDO_USERDESCSET_REQUEST
  #define ZDO_ENDDEVICEBIND_REQUEST
  //#define ZDO_BIND_UNBIND_REQUEST
  //#define ZDO_SERVERDISC_REQUEST
  //#define ZDO_NETWORKSTART_REQUEST
  //#define ZDO_MANUAL_JOIN

  //#define ZDO_BIND_UNBIND_RESPONSE
  //#define ZDO_COMPLEXDESC_RESPONSE
  //#define ZDO_USERDESC_RESPONSE
  //#define ZDO_USERDESCSET_RESPONSE
  //#define ZDO_SERVERDISC_RESPONSE
  #define ZDO_ENDDEVICE_ANNCE

  //#define ZDO_MGMT_NWKDISC_REQUEST
  //#define ZDO_MGMT_LQI_REQUEST
  //#define ZDO_MGMT_RTG_REQUEST
  //#define ZDO_MGMT_BIND_REQUEST
  //#define ZDO_MGMT_LEAVE_REQUEST
  //#define ZDO_MGMT_JOINDIRECT_REQUEST
  //#define ZDO_MGMT_PERMIT_JOIN_REQUEST
  //#define ZDO_MGMT_NWKDISC_RESPONSE
  //#define ZDO_MGMT_LQI_RESPONSE
  //#define ZDO_MGMT_RTG_RESPONSE
  //#define ZDO_MGMT_BIND_RESPONSE
  //#define ZDO_MGMT_LEAVE_RESPONSE
  //#define ZDO_MGMT_JOINDIRECT_RESPONSE
  //#define ZDO_MGMT_PERMIT_JOIN_RESPONSE

#if defined ( REFLECTOR  )
  // Binding needs this request to do a 64 to 16 bit conversion
#if !defined(ZDO_NWKADDR_REQUEST)
  #define ZDO_NWKADDR_REQUEST
#endif
#if !defined(ZDO_IEEEADDR_REQUEST)
  #define ZDO_IEEEADDR_REQUEST
#endif
  #define ZDO_BIND_UNBIND_RESPONSE
#endif

#endif  // !MT_ZDO_FUNC

#define MAX_BUFFER_SIZE		        	80

#if defined ( ZIGBEE_FRAGMENTATION )
  // The application/profile must fill this field out.
  #define MAX_TRANSFER_SIZE	        	160
#else
  #define MAX_TRANSFER_SIZE	        	80
#endif

#define MAX_ENDPOINTS	            	240

// Node Description Bitfields
#define ZDOLOGICALTYPE_MASK		    	0x07
#define ZDOAPSFLAGS_MASK		      	0x07
#define ZDOFREQUENCYBANDS_MASK    	0x1F
#define ZDOAPSFLAGS_BITLEN	    		3

#define SIMPLE_DESC_DATA_SIZE				7
#define NODE_DESC_DATA_SIZE					10

// Simple Description Bitfields
#define ZDOENDPOINT_BITLEN		      5
#define ZDOENDPOINT_MASK		        0x1F
#define ZDOINTERFACE_MASK	      		0x07
#define ZDOAPPFLAGS_MASK	      		0x0F
#define ZDOAPPDEVVER_MASK		      	0x0F
#define ZDOAPPDEVVER_BITLEN		    	4

extern NodeDescriptorFormat_t ZDO_Config_Node_Descriptor;
extern NodePowerDescriptorFormat_t ZDO_Config_Power_Descriptor;

extern void ZDConfig_InitDescriptors( void );
extern void ZDConfig_UpdateNodeDescriptor( void );
extern void ZDConfig_UpdatePowerDescriptor( void );

#ifdef __cplusplus
}
#endif

#endif /* ZDCONFIG_H */

ZDNwkMgr.c

#ifdef __cplusplus
extern "C"
{
#endif

#include "ZComdef.h"
#include "nwk_util.h"
#include "ZDApp.h"
#include "ZDObject.h"
#include "ZGlobals.h"
#include "ZDNwkMgr.h"

#if defined( MT_ZDO_FUNC )
  #include "MT_ZDO.h"
#endif
  
#if defined ( LCD_SUPPORTED )
  #include "OnBoard.h"
#endif

/* HAL */
#include "hal_lcd.h"


#define ONE_MINUTE             60000  // 1(m) * 60(s) * 1000(ms)

#if defined ( LCD_SUPPORTED )
  const char NwkMgrStr_1[]     = "NM-fail not hi";
  const char NwkMgrStr_2[]     = "NM-cur<last fail";
  const char NwkMgrStr_3[]     = "NM-energy too hi";
  const char NwkMgrStr_4[]     = "NM-energy not up";
#endif

  
// Task ID for internal task/event processing. This variable will be
// received when ZDNwkMgr_Init() is called.
uint8 ZDNwkMgr_TaskID = 0;

// Frequency Agility variables
uint8 ZDNwkMgr_MgmtNwkUpdateNotifyTransSeq = 0;
zAddrType_t ZDNwkMgr_MgmtNwkUpdateNotifyAddr;
uint16 ZDNwkMgr_UpdateNotifyTimer = 0;
uint8  ZDNwkMgr_NumUpdateNotifySent = 0;
uint8  ZDNwkMgr_WaitingForNotifyConfirm = FALSE;
uint16 ZDNwkMgr_TotalTransmissions;
uint16 ZDNwkMgr_TxFailures;

ZDO_MgmtNwkUpdateReq_t ZDNwkMgr_MgmtNwkUpdateReq;
  
#if defined ( NWK_MANAGER )
uint16 ZDNwkMgr_UpdateRequestTimer = 0;
uint8  ZDNwkMgr_LastChannelEnergy = 0;
uint16 ZDNwkMgr_LastChannelFailureRate = 0;
#endif // NWK_MANAGER

uint8 ZDNwkMgr_NewChannel;

// PAN ID Conflict variables
#if defined ( NWK_MANAGER )
uint8 ZDNwkMgr_PanIdUpdateInProgress = FALSE;
#endif // NWK_MANAGER

// Freguency Agility functions
void (*pZDNwkMgr_ReportChannelInterference)( NLME_ChanInterference_t *chanInterference ) = NULL;
void (*pZDNwkMgr_ProcessDataConfirm)( afDataConfirm_t *afDataConfirm ) = NULL;
void (*pZDNwkMgr_EDScanConfirmCB)( NLME_EDScanConfirm_t *EDScanConfirm ) = NULL;

// PAN ID Conflict functions
void (*pZDNwkMgr_NetworkReportCB)( ZDNwkMgr_NetworkReport_t *pReport ) = NULL;
void (*pZDNwkMgr_NetworkUpdateCB)( ZDNwkMgr_NetworkUpdate_t *pUpdate ) = NULL;


void ZDNwkMgr_ProcessServerDiscRsp( zdoIncomingMsg_t *inMsg );
void ZDNwkMgr_SetNwkManagerAddr( uint16 nwkManagerAddr );

// Frequency Agility functions
static void ZDNwkMgr_ProcessMsgCBs( zdoIncomingMsg_t *inMsg );

static void ZDNwkMgr_ProcessMgmtNwkUpdateReq( zdoIncomingMsg_t *inMsg );
static void ZDNwkMgr_ProcessChannelInterference( ZDNwkMgr_ChanInterference_t *pChanInterference );
static void ZDNwkMgr_ProcessEDScanConfirm( ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm );
static void ZDNwkMgr_CheckForChannelInterference( ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm );
static void ZDNwkMgr_BuildAndSendUpdateNotify( uint8 TransSeq, zAddrType_t *dstAddr,
                                               uint16 totalTransmissions, uint16 txFailures,
                                               ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm, uint8 txOptions );
void ZDNwkMgr_EDScanConfirmCB( NLME_EDScanConfirm_t *EDScanConfirm );
void ZDNwkMgr_ProcessDataConfirm( afDataConfirm_t *afDataConfirm );
void ZDNwkMgr_ReportChannelInterference( NLME_ChanInterference_t *chanInterference );

#if defined ( NWK_MANAGER )
static void ZDNwkMgr_ProcessMgmtNwkUpdateNotify( zdoIncomingMsg_t *inMsg );
static void ZDNwkMgr_CheckForChannelChange( ZDO_MgmtNwkUpdateNotify_t *pNotify );
#endif // NWK_MANAGER

// PAN ID Conflict functions
#if defined ( NWK_MANAGER )
void ZDNwkMgr_NetworkReportCB( ZDNwkMgr_NetworkReport_t *pReport );
void ZDNwkMgr_NetworkUpdateCB( ZDNwkMgr_NetworkUpdate_t *pUpdate );

void ZDNwkMgr_ProcessNetworkReport( ZDNwkMgr_NetworkReport_t *pNetworkReport );
void ZDNwkMgr_ProcessNetworkUpdate( ZDNwkMgr_NetworkUpdate_t *pNetworkUpdate );
#endif // NWK_MANAGER


void ZDNwkMgr_Init( byte task_id )
{
  // Save the task ID
  ZDNwkMgr_TaskID = task_id;

  ZDO_RegisterForZDOMsg( ZDNwkMgr_TaskID, Server_Discovery_rsp );

  // Frequecy Agility initialization
  ZDO_RegisterForZDOMsg( ZDNwkMgr_TaskID, Mgmt_NWK_Update_req );
#if defined ( NWK_MANAGER )
  ZDO_RegisterForZDOMsg( ZDNwkMgr_TaskID, Mgmt_NWK_Update_notify );
#endif // NWK_MANAGER

  pZDNwkMgr_EDScanConfirmCB = ZDNwkMgr_EDScanConfirmCB;
  pZDNwkMgr_ProcessDataConfirm = ZDNwkMgr_ProcessDataConfirm;
  pZDNwkMgr_ReportChannelInterference = ZDNwkMgr_ReportChannelInterference;
  
  // PAN ID Conflict initialization
#if defined ( NWK_MANAGER )
  pZDNwkMgr_NetworkReportCB = ZDNwkMgr_NetworkReportCB;
  pZDNwkMgr_NetworkUpdateCB = ZDNwkMgr_NetworkUpdateCB;
#endif // NWK_MANAGER
  
  ZDNwkMgr_MgmtNwkUpdateNotifyAddr.addrMode = Addr16Bit;
  ZDNwkMgr_MgmtNwkUpdateNotifyAddr.addr.shortAddr = INVALID_NODE_ADDR;
}

UINT16 ZDNwkMgr_event_loop( byte task_id, UINT16 events )
{
  osal_event_hdr_t *msgPtr;
  (void)task_id;  // Intentionally unreferenced parameter

  if ( events & SYS_EVENT_MSG )
  {
    msgPtr = (osal_event_hdr_t *)osal_msg_receive( ZDNwkMgr_TaskID );
    while ( msgPtr )
    {
      switch ( msgPtr->event )
      {
        case ZDO_CB_MSG:
          // ZDO sends the message that we registered for
          ZDNwkMgr_ProcessMsgCBs( (zdoIncomingMsg_t *)msgPtr );
          break;
         
        case NM_CHANNEL_INTERFERE:
          // NWK layer sends the message when it detectes Channel Interference
          ZDNwkMgr_ProcessChannelInterference( (ZDNwkMgr_ChanInterference_t *)msgPtr );
          break;
   
        case NM_ED_SCAN_CONFIRM:
          // NWK layer sends the message when it receives an ED scan confirmation
          ZDNwkMgr_ProcessEDScanConfirm( (ZDNwkMgr_EDScanConfirm_t *)msgPtr );
          break;
#if defined ( NWK_MANAGER )
        case ZDO_NETWORK_REPORT:
          // NWK layer sends this message when it receives a Network Report message
          ZDNwkMgr_ProcessNetworkReport( (ZDNwkMgr_NetworkReport_t *)msgPtr );
          break;
       
        case ZDO_NETWORK_UPDATE:
          // NKW layer sends this message when it receives a Network Update message
          ZDNwkMgr_ProcessNetworkUpdate( (ZDNwkMgr_NetworkUpdate_t *)msgPtr );
          break;
#endif // NWK_MANAGER         
        default:
          break;
      }

      // Release the memory
      osal_msg_deallocate( (uint8 *)msgPtr );

      // Next
      msgPtr = (osal_event_hdr_t *)osal_msg_receive( ZDNwkMgr_TaskID );
    }
    
    // Return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  if ( events & ZDNWKMGR_CHANNEL_CHANGE_EVT )
  {       
    // Switch channel
    _NIB.nwkLogicalChannel = ZDNwkMgr_NewChannel;
    ZMacSetReq( ZMacChannel, &ZDNwkMgr_NewChannel );
 
    // Our Channel has been changed -- notify to save info into NV
    ZDApp_NwkStateUpdateCB();
    
    // Reset the total transmit count and the transmit failure counters
    _NIB.nwkTotalTransmissions = 0;
    nwkTransmissionFailures( TRUE );
    
    return ( events ^ ZDNWKMGR_CHANNEL_CHANGE_EVT );
  }

  if ( events & ZDNWKMGR_UPDATE_NOTIFY_EVT )
  {
    // Update the Update Notify timer
    if ( ZDNwkMgr_UpdateNotifyTimer > 0 )
    {
      ZDNwkMgr_UpdateNotifyTimer--;
      osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_UPDATE_NOTIFY_EVT, ONE_MINUTE );
    }
    else
    {
      ZDNwkMgr_NumUpdateNotifySent = 0;
    }
    
    return ( events ^ ZDNWKMGR_UPDATE_NOTIFY_EVT );
  }
  
#if defined ( NWK_MANAGER )
  if ( events & ZDNWKMGR_UPDATE_REQUEST_EVT )
  {
    // Update the Update Request timer
    if ( ZDNwkMgr_UpdateRequestTimer > 0 )
    {
      ZDNwkMgr_UpdateRequestTimer--;
      osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_UPDATE_REQUEST_EVT, ONE_MINUTE );
    }
    
    return ( events ^ ZDNWKMGR_UPDATE_REQUEST_EVT );
  }
#endif // NWK_MANAGER
  
  if ( events & ZDNWKMGR_SCAN_REQUEST_EVT )
  {  
    if ( ZDNwkMgr_MgmtNwkUpdateReq.scanCount > 0 )
    {
      if (  NLME_EDScanRequest( ZDNwkMgr_MgmtNwkUpdateReq.channelMask, 
                                ZDNwkMgr_MgmtNwkUpdateReq.scanDuration ) == ZSuccess )
      {
        ZDNwkMgr_MgmtNwkUpdateReq.scanCount--;
      }
    }
      
    return ( events ^ ZDNWKMGR_SCAN_REQUEST_EVT );
  }
  
  // Discard or make more handlers
  return 0;
}

static void ZDNwkMgr_ProcessMsgCBs( zdoIncomingMsg_t *inMsg )
{
  switch ( inMsg->clusterID )
  {   
    case Mgmt_NWK_Update_req:
      ZDNwkMgr_ProcessMgmtNwkUpdateReq( inMsg );
      break;    
#if defined ( NWK_MANAGER )  
    case Mgmt_NWK_Update_notify:
      ZDNwkMgr_ProcessMgmtNwkUpdateNotify( inMsg );
      break;
#endif // NWK_MANAGER
    case Server_Discovery_rsp:
      ZDNwkMgr_ProcessServerDiscRsp( inMsg );
      break;
      
    default:
      // Unknown message
      break;
  }
}

#if defined ( NWK_MANAGER )

static void ZDNwkMgr_ProcessMgmtNwkUpdateNotify( zdoIncomingMsg_t *inMsg )
{
  if ( zgNwkMgrMode == ZDNWKMGR_ENABLE )
  {
    ZDO_MgmtNwkUpdateNotify_t *pNotify = ZDO_ParseMgmtNwkUpdateNotify( inMsg ); 
    if ( pNotify )
    {
      ZDNwkMgr_CheckForChannelChange( pNotify );

      osal_mem_free( pNotify );
    }
  }
}

static void ZDNwkMgr_CheckForChannelChange( ZDO_MgmtNwkUpdateNotify_t *pNotify )
{
  uint8  i;
  uint16 failureRate;
  uint8  lowestEnergyIndex;
  uint8  lowestEnergyValue = 0xFF;
      
  // If any device has more than 50% transmission failures, a channel
  // change should be considered
  failureRate = ( pNotify->transmissionFailures * 100 ) / pNotify->totalTransmissions;
  if ( failureRate < ZDNWKMGR_CC_TX_FAILURE )
  {
#if defined ( LCD_SUPPORTED )
    HalLcdWriteString( (char*)NwkMgrStr_1, HAL_LCD_LINE_1 );
    HalLcdWriteStringValueValue( ": ", failureRate, 10, ZDNWKMGR_CC_TX_FAILURE, 10, HAL_LCD_LINE_2 );
#endif
    return;
  }

  // If the current failure rate is higher than the last failure rate,
  // a channel change should be considered
  if ( failureRate < ZDNwkMgr_LastChannelFailureRate )
  {
#if defined ( LCD_SUPPORTED )
    HalLcdWriteString( (char*)NwkMgrStr_2, HAL_LCD_LINE_1 );
    HalLcdWriteStringValueValue( ": ", failureRate, 10, 
                                 ZDNwkMgr_LastChannelFailureRate, 10, HAL_LCD_LINE_2 );
#endif
    return;
  }
  
  // Select a single channel based on the Mgmt_NWK_Update_notify based on
  // the lowest energy. This is the proposed new channel. 
  for ( i = 0; i < pNotify->listCount; i++ )
  {
    if ( pNotify->energyValues[i] < lowestEnergyValue )
    {
      lowestEnergyIndex = i;
      lowestEnergyValue = pNotify->energyValues[i];
    }
  }
      
  // If this new channel does not have an energy level below an acceptable
  // threshold, a channel change should not be done.
  if ( lowestEnergyValue > ZDNWKMGR_ACCEPTABLE_ENERGY_LEVEL )
  {
#if defined ( LCD_SUPPORTED )
    HalLcdWriteString( (char*)NwkMgrStr_3, HAL_LCD_LINE_1 );
    HalLcdWriteStringValueValue( ": ", lowestEnergyValue, 10, 
                                 ZDNWKMGR_ACCEPTABLE_ENERGY_LEVEL, 10, HAL_LCD_LINE_2 );
#endif
    return;
  }

  // Channel change should be done -- find out the new active channel
  for ( i = 0; i < ED_SCAN_MAXCHANNELS; i++ )
  {
    if ( ( (uint32)1 << i ) & pNotify->scannedChannels )
    {
      if ( lowestEnergyIndex == 0 )
        break;
      lowestEnergyIndex--;
    }
  }
  
  if ( ( _NIB.nwkLogicalChannel != i ) && ( ZDNwkMgr_UpdateRequestTimer == 0 ) )
  {
    uint32 channelMask;
    zAddrType_t dstAddr;
    
    // The new channel
    ZDNwkMgr_NewChannel = i;
        
    // Prior to changing channels, the network manager should store the 
    // energy scan value as the last energy scan value and the failure 
    // rate from the existing channel as the last failure rate.  These 
    // values are useful to allow comparison of the failure rate and energy
    // level on the previous channel to evaluate if the network is causing
    // its own interference.
    ZDNwkMgr_LastChannelEnergy = lowestEnergyValue;
    ZDNwkMgr_LastChannelFailureRate = failureRate;
       
    // The network manager should broadcast a Mgmt_NWK_Update_req notifying
    // devices of the new channel.  The broadcast shall be to all routers 
    // and coordinator.
    dstAddr.addrMode = AddrBroadcast;
    dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVRXON;
    channelMask = (uint32)1 << i;
        
    // Increment the nwkUpdateId parameter and set the updateID in the beacon
    NLME_SetUpdateID(_NIB.nwkUpdateId + 1); 
    
    ZDP_MgmtNwkUpdateReq( &dstAddr, channelMask, 0xfe, 0, _NIB.nwkUpdateId, 0 );
        
    // The network manager shall set a timer based on the value of 
    // apsChannelTimer upon issue of a Mgmt_NWK_Update_req that changes 
    // channels and shall not issue another such command until this 
    // timer expires.  
    ZDNwkMgr_UpdateRequestTimer = ZDNWKMGR_UPDATE_REQUEST_TIMER;
    osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_UPDATE_REQUEST_EVT, ONE_MINUTE );
                  
    // Upon receipt of a Mgmt_NWK_Update_req with a change of channels, 
    // the local network manager shall set a timer equal to the 
    // nwkNetworkBroadcastDeliveryTime and shall switch channels upon 
    // expiration of this timer.  NOTE: since we won't recevied our own
    // broadcasted Update Request, we start the channel change timer here.  
    osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_CHANNEL_CHANGE_EVT, 
                        ZDNWKMGR_BCAST_DELIVERY_TIME );
  }
}
#endif  // NWK_MANAGER

static void ZDNwkMgr_ProcessMgmtNwkUpdateReq( zdoIncomingMsg_t *inMsg )
{
  ZDO_MgmtNwkUpdateReq_t Req;
  
  ZDO_ParseMgmtNwkUpdateReq( inMsg, &Req );
   
  if ( Req.scanDuration <= 0x05 )
  {
    // Request is to scan over channelMask. The result will be reported by Confirm   
    if ( ( !inMsg->wasBroadcast )                     && 
         ( Req.scanCount >  ZDNWKMGR_MIN_SCAN_COUNT ) && 
         ( Req.scanCount <= ZDNWKMGR_MAX_SCAN_COUNT ) )
    {
      if ( NLME_EDScanRequest( Req.channelMask, Req.scanDuration ) == ZSuccess )
      {
        // Save off the information to be used for the notify
        ZDNwkMgr_MgmtNwkUpdateNotifyTransSeq            = inMsg->TransSeq;
        ZDNwkMgr_MgmtNwkUpdateNotifyAddr.addr.shortAddr = inMsg->srcAddr.addr.shortAddr;
        
        Req.scanCount--;
        
        // Save off scan info for the subsequent scans
        ZDNwkMgr_MgmtNwkUpdateReq = Req;
      }
    }
  }
  else if ( Req.scanDuration == 0xFE )
  {
    // Request is to change Channel. The command provide a new active
    // channel as a single channel in the channelMask.
    if ( Req.nwkUpdateId > _NIB.nwkUpdateId )
    {
      uint8 i;
      
      // Set update ID in the Beacon
      NLME_SetUpdateID(Req.nwkUpdateId); 
      
      // Find out the new active channel
      for ( i = 0; i < ED_SCAN_MAXCHANNELS; i++ )
      {
        if ( ( (uint32)1 << i ) & Req.channelMask )
        {
          break;
        }
      }

      if ( _NIB.nwkLogicalChannel != i )
      {
        ZDNwkMgr_NewChannel = i;
          
        // Upon receipt of a Mgmt_NWK_Update_req with a change of channels, 
        // the local network manager shall set a timer equal to the 
        // nwkNetworkBroadcastDeliveryTime and shall switch channels upon 
        // expiration of this timer.  Each node shall also increment the 
        // nwkUpdateId parameter and also reset the total transmit count 
        // and the transmit failure counters.  
        osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_CHANNEL_CHANGE_EVT, 
                            ZDNWKMGR_BCAST_DELIVERY_TIME );
      }
    }
  }
  else if ( Req.scanDuration == 0xFF )
  {
    // Request is to change apsChannelMask and nwkManagerAddr
    if ( Req.nwkUpdateId > _NIB.nwkUpdateId )
    {
      NLME_SetUpdateID(Req.nwkUpdateId); // Set the updateID in the beacon
       
      if ( ( Req.channelMask != 0 ) && ( _NIB.channelList != Req.channelMask ) )
      {
        _NIB.channelList = Req.channelMask;
      
        // Our Channel List has been changed -- notify to save info into NV
        ZDApp_NwkStateUpdateCB();
      }
    
      ZDNwkMgr_SetNwkManagerAddr( Req.nwkManagerAddr );
    }
  }
  else // 0x06-0xFD
  {
    // Request is invalid
    if ( !inMsg->wasBroadcast )
    {
      ZDNwkMgr_MgmtNwkUpdateNotifyAddr.addr.shortAddr = inMsg->srcAddr.addr.shortAddr;
      ZDP_MgmtNwkUpdateNotify( inMsg->TransSeq, &ZDNwkMgr_MgmtNwkUpdateNotifyAddr,
                               ZDP_INVALID_REQTYPE, 0, 0, 0, 0, NULL, AF_TX_OPTIONS_NONE, false );
    }
  }
}

void ZDNwkMgr_ProcessServerDiscRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_ServerDiscRsp_t Rsp;
  
  ZDO_ParseServerDiscRsp( inMsg, &Rsp );
  
  if ( Rsp.status == ZSuccess )
  {
    // Is the Network Manager bit set in the response?
    if ( Rsp.serverMask & NETWORK_MANAGER )
    {
      // Set the Remote Device's NWK Address as the Network Manager Address
      ZDNwkMgr_SetNwkManagerAddr( inMsg->srcAddr.addr.shortAddr );
    }
  }
}

static void ZDNwkMgr_ProcessChannelInterference( ZDNwkMgr_ChanInterference_t *pChanInterference )
{
  // To avoid a device with communication problems from constantly 
  // sending reports to the network manager, the device should not 
  // send a Mgmt_NWK_Update_notify more than 4 times per hour.
  if ( ZDNwkMgr_NumUpdateNotifySent < 4 )
  {
    // Conduct an energy scan on all channels.
    if ( NLME_EDScanRequest( MAX_CHANNELS_24GHZ, _NIB.scanDuration ) == ZSuccess )
    {
      // Save the counters for the Update Notify message to be sent
      ZDNwkMgr_TotalTransmissions = pChanInterference->totalTransmissions;
      ZDNwkMgr_TxFailures = pChanInterference->txFailures;

      // Mark scan as channel inetrference check
      ZDNwkMgr_MgmtNwkUpdateReq.scanCount = 0xFF;
    }
  }
}

static void ZDNwkMgr_ProcessEDScanConfirm( ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm )
{ 
  if ( ZDNwkMgr_MgmtNwkUpdateReq.scanCount == 0xFF )
  {
    // Confirm to scan all channels for channel interference check
    ZDNwkMgr_CheckForChannelInterference( pEDScanConfirm ); 
    
    ZDNwkMgr_MgmtNwkUpdateReq.scanCount = 0;
  }
  else
  {
    // Confirm to the requested scan
    uint16 txFailures = nwkTransmissionFailures( FALSE );
    
    ZDNwkMgr_BuildAndSendUpdateNotify( ZDNwkMgr_MgmtNwkUpdateNotifyTransSeq,
                                       &ZDNwkMgr_MgmtNwkUpdateNotifyAddr, 
                                       _NIB.nwkTotalTransmissions, txFailures, 
                                       pEDScanConfirm, AF_TX_OPTIONS_NONE );
    // More scans needed?
    if ( ZDNwkMgr_MgmtNwkUpdateReq.scanCount > 0 )
    {
      osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_SCAN_REQUEST_EVT, 50 );
    }
  }
}

static void ZDNwkMgr_CheckForChannelInterference( ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm )
{
  uint8 i;
  uint8 channelEnergy = 0;
  uint8 energyIncreased = FALSE;
    
  // Get the current channel energy
  if ( ( (uint32)1 << _NIB.nwkLogicalChannel ) & pEDScanConfirm->scannedChannels )
  {
    channelEnergy = pEDScanConfirm->energyDetectList[_NIB.nwkLogicalChannel];
  }
    
  // If this energy scan does not indicate higher energy on the current 
  // channel then other channels, no action is taken. The device should 
  // continue to operate as normal and the message counters are not reset.
  for ( i = 0; i < ED_SCAN_MAXCHANNELS; i++ )
  {
    if ( ( ( (uint32)1 << i ) & pEDScanConfirm->scannedChannels ) && 
         ( channelEnergy > pEDScanConfirm->energyDetectList[i] ) )
    {
      energyIncreased = TRUE;
      break;
    }
  }
    
  // If the energy scan does indicate increased energy on the channel
  // in use, a Mgmt_NWK_Update_notify should be sent to the Network 
  // Manager to indicate interference is present.
  if ( energyIncreased )
  {
    // Send a Management Network Update notify to the Network Manager
    ZDNwkMgr_MgmtNwkUpdateNotifyAddr.addr.shortAddr = _NIB.nwkManagerAddr;
    ZDNwkMgr_BuildAndSendUpdateNotify( 0, &ZDNwkMgr_MgmtNwkUpdateNotifyAddr, 
                                       ZDNwkMgr_TotalTransmissions, ZDNwkMgr_TxFailures,
                                       pEDScanConfirm, AF_MSG_ACK_REQUEST );
    ZDNwkMgr_WaitingForNotifyConfirm = TRUE; // Confirm will clear the counters
      
    if ( ZDNwkMgr_NumUpdateNotifySent == 0 )
    {
      // First notify message sent within this hour. Start the Update Notify timer.
      ZDNwkMgr_UpdateNotifyTimer = ZDNWKMGR_UPDATE_NOTIFY_TIMER;
      osal_start_timerEx( ZDNwkMgr_TaskID, ZDNWKMGR_UPDATE_NOTIFY_EVT, ONE_MINUTE );
    }
    
    ZDNwkMgr_NumUpdateNotifySent++;
  }
#if defined ( LCD_SUPPORTED )
  else
  {
    HalLcdWriteString( (char*)NwkMgrStr_4, HAL_LCD_LINE_1 );
    HalLcdWriteStringValueValue( ": ", _NIB.nwkLogicalChannel, 10, channelEnergy, 10, HAL_LCD_LINE_2 );
  }
#endif
}

static void ZDNwkMgr_BuildAndSendUpdateNotify( uint8 TransSeq, zAddrType_t *dstAddr,
                                               uint16 totalTransmissions, uint16 txFailures,
                                               ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm,
                                               uint8 txOptions )
{
  uint8 i;
  uint8 listCount = 0;
  uint8 *energyValues = NULL;
  
  // Count number of energy detects
  for ( i = 0; i < ED_SCAN_MAXCHANNELS; i++ )
  {
    if ( ( (uint32)1 << i ) & pEDScanConfirm->scannedChannels )
      listCount++;
  }
  
  if ( listCount > 0 )
  {
    energyValues = (uint8 *)osal_mem_alloc( listCount );
    if ( energyValues )
    {
      uint8 j = 0;

      for ( i = 0; i < ED_SCAN_MAXCHANNELS; i++ )
      {
        if ( ( (uint32)1 << i ) & pEDScanConfirm->scannedChannels )
          energyValues[j++] = pEDScanConfirm->energyDetectList[i];
      }
    }
  }
    
  // Send a Management Network Update notify back
  ZDP_MgmtNwkUpdateNotify( TransSeq, dstAddr, pEDScanConfirm->status, 
                           pEDScanConfirm->scannedChannels,
                           totalTransmissions, txFailures,
                           listCount, energyValues, txOptions, false );
  if ( energyValues )
    osal_mem_free( energyValues );
}

#if defined ( NWK_MANAGER )

void NwkMgr_SetNwkManager( void )
{
  if ( zgNwkMgrMode == ZDNWKMGR_ENABLE )
  {
    // We're the Network Manager. Set our address as the Network Manager Address
    ZDNwkMgr_SetNwkManagerAddr( _NIB.nwkDevAddress );
    
    // Set the Network Manager bit of the Server Mask
    ZDO_Config_Node_Descriptor.ServerMask |= NETWORK_MANAGER;
  }
}
#endif // NWK_MANAGER

void ZDNwkMgr_SetNwkManagerAddr( uint16 nwkManagerAddr )
{
  if ( _NIB.nwkManagerAddr != nwkManagerAddr )
  {
    // Update the Network Manager Address
    _NIB.nwkManagerAddr = nwkManagerAddr;
  
    // Our Network Manger Address has been changed -- notify to save info into NV
    ZDApp_NwkStateUpdateCB();
  }
}


void ZDNwkMgr_ReportChannelInterference(  NLME_ChanInterference_t *chanInterference  )
{
  ZDNwkMgr_ChanInterference_t *pChanInterference;

  // Send Channel Interference message to the Network Manager task
  pChanInterference = (ZDNwkMgr_ChanInterference_t *)osal_msg_allocate( sizeof( ZDNwkMgr_ChanInterference_t ) );
  if ( pChanInterference )
  {
    pChanInterference->hdr.event = NM_CHANNEL_INTERFERE;
      
    // Build the structure
    pChanInterference->totalTransmissions = chanInterference->totalTransmissions;
    pChanInterference->txFailures = chanInterference->txFailures;
              
    osal_msg_send( ZDNwkMgr_TaskID, (uint8 *)pChanInterference );
  }
}

void ZDNwkMgr_EDScanConfirmCB( NLME_EDScanConfirm_t *EDScanConfirm )
{
  ZDNwkMgr_EDScanConfirm_t *pEDScanConfirm;

  // Send ED Confirm to the Network Manager task
  pEDScanConfirm = (ZDNwkMgr_EDScanConfirm_t *)osal_msg_allocate( sizeof( ZDNwkMgr_EDScanConfirm_t ) );
  if ( pEDScanConfirm )
  {
    pEDScanConfirm->hdr.event = NM_ED_SCAN_CONFIRM;
      
    // Build the structure
    pEDScanConfirm->status = EDScanConfirm->status;
    pEDScanConfirm->scannedChannels = EDScanConfirm->scannedChannels;
    osal_memcpy( pEDScanConfirm->energyDetectList, EDScanConfirm->energyDetectList, ED_SCAN_MAXCHANNELS );
      
    osal_msg_send( ZDNwkMgr_TaskID, (uint8 *)pEDScanConfirm );
  }
}

void ZDNwkMgr_ProcessDataConfirm( afDataConfirm_t *afDataConfirm )
{
  if (   ZDNwkMgr_WaitingForNotifyConfirm  && 
       ( afDataConfirm->transID == 0 )     && 
       ( afDataConfirm->hdr.status == ZSuccess ) )
  {
    // The Mgmt NWK Update Notify was sent as an APS Unicast with  
    // acknowledgement and once the acknowledgment is received the 
    // total transmit and transmit failure counters are reset to zero.  
    _NIB.nwkTotalTransmissions = 0;
    nwkTransmissionFailures( TRUE );
    
    ZDNwkMgr_WaitingForNotifyConfirm = FALSE;
  }
}

#if defined ( NWK_MANAGER )

void ZDNwkMgr_NetworkReportCB( ZDNwkMgr_NetworkReport_t *pReport )
{ 
  // Send Network Report message to the Network Manager task
  osal_msg_send( ZDNwkMgr_TaskID, (uint8 *)pReport );
}

void ZDNwkMgr_NetworkUpdateCB( ZDNwkMgr_NetworkUpdate_t *pUpdate )
{
  // Send Network Update message to the Network Manager task
  osal_msg_send( ZDNwkMgr_TaskID, (uint8 *)pUpdate );
}

void ZDNwkMgr_ProcessNetworkReport( ZDNwkMgr_NetworkReport_t *pNetworkReport )
{
  uint8 i;
  uint16 newPID;
  uint8 unique = TRUE;

  if ( pNetworkReport->reportType == NWKREPORT_PANID_CONFLICT )
  {
    if ( ZDNwkMgr_PanIdUpdateInProgress == FALSE )
    {
      do
      {
        // select a new PAN ID
        newPID = (uint16)osal_rand();
      
        // Make sure that the chosen PAN ID is not already in use in the
        // local neighborhood and also not contained within the Report 
        // Information field of the Network Report Command frame
        for ( i = 0; i < pNetworkReport->reportInfoCnt; i++ )
        {
          if ( pNetworkReport->panIDs[i] == newPID )
          {
            unique = FALSE;
            break;
          }
        }
      } while ( !unique );
         
      // Send out a Network Update command.
      NLME_SendNetworkUpdate( NWK_BROADCAST_SHORTADDR, NWKUPDATE_PANID_UPDATE,
                              _NIB.extendedPANID, _NIB.nwkUpdateId+1, newPID );
    
      ZDNwkMgr_PanIdUpdateInProgress = TRUE;
    }
  }
}

void ZDNwkMgr_ProcessNetworkUpdate( ZDNwkMgr_NetworkUpdate_t *pNetworkUpdate )
{
  if ( pNetworkUpdate->updateType == NWKUPDATE_PANID_UPDATE )
  { 
    // Our PAN ID has been changed -- notify to save info into NV
    ZDApp_NwkStateUpdateCB();
    
    ZDNwkMgr_PanIdUpdateInProgress = FALSE;
  }
}
#endif // NWK_MANAGER

ZDNwkMgr.h

#ifndef ZDNWKMGR_H
#define ZDNWKMGR_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "ZComDef.h"
#include "nwk_globals.h"
#include "nwk_util.h"
#include "ZDApp.h"

// Network Manager Role
#define ZDNWKMGR_DISABLE                  0x00
#define ZDNWKMGR_ENABLE                   0x01

// Energy level threshold
#define ZDNWKMGR_ACCEPTABLE_ENERGY_LEVEL  0x1E

// Minimum transmissions attempted for Channel Interference detection
#if !defined ( ZDNWKMGR_MIN_TRANSMISSIONS )
  #define ZDNWKMGR_MIN_TRANSMISSIONS      20
#endif

// Minimum transmit failure rate for Channel Interference detection
#define ZDNWKMGR_CI_TX_FAILURE            25

// Minimum transmit failure rate for Channel Change
#define ZDNWKMGR_CC_TX_FAILURE            50

// Min and Max Scan Counts for Update Request
#define ZDNWKMGR_MIN_SCAN_COUNT           0
#define ZDNWKMGR_MAX_SCAN_COUNT           5

// Update Request and Notify timers
#define ZDNWKMGR_UPDATE_NOTIFY_TIMER      60  // 1(h) * 60(m)
#define ZDNWKMGR_UPDATE_REQUEST_TIMER     60  // 1(h) * 60(m)

// Network Manager Events
#define ZDNWKMGR_CHANNEL_CHANGE_EVT       0x0001
#define ZDNWKMGR_UPDATE_NOTIFY_EVT        0x0002
#define ZDNWKMGR_UPDATE_REQUEST_EVT       0x0004
#define ZDNWKMGR_SCAN_REQUEST_EVT         0x0008

#define ZDNWKMGR_BCAST_DELIVERY_TIME      ( _NIB.BroadcastDeliveryTime * 100 )

// Used for Management Network Update Request message
typedef struct
{
  osal_event_hdr_t hdr;
  uint8 transSeq;
  uint16 srcAddr;
  uint32 channelMask;
  uint8 scanDuration;
  uint8 scanCount;
  int16 nwkManagerAddr;
  uint8 wasBroadcast;
} ZDNwkMgr_MgmtNwkUpdateRequest_t;

// Used for Management Network Update Notify command
typedef struct
{
  osal_event_hdr_t hdr;
  uint16 srcAddr;
  uint8 status;
  uint32 scannedChannels;
  uint16 totalTransmissions;
  uint16 txFailures;
  uint8 listCount;
  uint8 *energyValues;
} ZDNwkMgr_MgmtNwkUpdateNotify_t;

// Used for Channel Interference message
typedef struct
{
  osal_event_hdr_t hdr;
  uint16 totalTransmissions;
  uint16 txFailures;
} ZDNwkMgr_ChanInterference_t;

// Used for ED Scan Confirm message
typedef struct
{
  osal_event_hdr_t hdr;
  uint8 status;
  uint32 scannedChannels;
  uint8 energyDetectList[ED_SCAN_MAXCHANNELS];
} ZDNwkMgr_EDScanConfirm_t;

// Used for Network Report command
typedef struct
{
  osal_event_hdr_t hdr;
  uint16 srcAddr;
  uint8  reportType;
  uint8  EPID[Z_EXTADDR_LEN];
  uint8  reportInfoCnt;
  uint16 panIDs[];
} ZDNwkMgr_NetworkReport_t;

// Used for Network Update command
typedef struct
{
  osal_event_hdr_t hdr;
  uint8  updateType;
  uint8  updateInfoCnt;
  uint16 newPanID;
} ZDNwkMgr_NetworkUpdate_t;

extern byte ZDNwkMgr_TaskID;

extern void ZDNwkMgr_Init( byte task_id );

extern UINT16 ZDNwkMgr_event_loop( byte task_id, UINT16 events );

// Frequency Agility functions
extern void (*pZDNwkMgr_EDScanConfirmCB)( NLME_EDScanConfirm_t *EDScanConfirm );
extern void (*pZDNwkMgr_ProcessDataConfirm)( afDataConfirm_t *afDataConfirm );
extern void (*pZDNwkMgr_ReportChannelInterference)( NLME_ChanInterference_t *chanInterference );

// PAN ID Conflict functions
extern void (*pZDNwkMgr_NetworkReportCB)( ZDNwkMgr_NetworkReport_t *pReport );
extern void (*pZDNwkMgr_NetworkUpdateCB)( ZDNwkMgr_NetworkUpdate_t *pUpdate );

#if defined ( NWK_MANAGER )

extern void NwkMgr_SetNwkManager( void );
#endif

#ifdef __cplusplus
}
#endif

#endif /* ZDNWKMGR_H */

ZDObject.c

#include "ZComdef.h"
#include "OSAL.h"
#include "OSAL_Nv.h"
#include "rtg.h"
#include "NLMEDE.h"
#include "nwk_globals.h"
#include "APS.h"
#include "APSMEDE.h"
#include "AssocList.h"
#include "BindingTable.h"
#include "AddrMgr.h"
#include "AF.h"
#include "ZDObject.h"
#include "ZDProfile.h"
#include "ZDConfig.h"
#include "ZDSecMgr.h"
#include "ZDApp.h"
#include "nwk_util.h"   // NLME_IsAddressBroadcast()
#include "ZGlobals.h"
#if defined MT_ZDO_CB_FUNC
#include "MT.h"
#endif

#if defined( LCD_SUPPORTED )
  #include "OnBoard.h"
#endif

#include "hal_lcd.h"

// NLME Stub Implementations
#define ZDO_ProcessMgmtPermitJoinTimeout NLME_PermitJoiningTimeout

// Status fields used by ZDO_ProcessMgmtRtgReq
#define ZDO_MGMT_RTG_ENTRY_ACTIVE             0x00
#define ZDO_MGMT_RTG_ENTRY_DISCOVERY_UNDERWAY 0x01
#define ZDO_MGMT_RTG_ENTRY_DISCOVERY_FAILED   0x02
#define ZDO_MGMT_RTG_ENTRY_INACTIVE           0x03

#if defined ( REFLECTOR )
typedef struct
{
  byte SrcTransSeq;
  zAddrType_t SrcAddr;
  uint16 LocalCoordinator;
  byte epIntf;
  uint16 ProfileID;
  byte numInClusters;
  uint16 *inClusters;
  byte numOutClusters;
  uint16 *outClusters;
  byte SecurityUse;
  byte status;
} ZDO_EDBind_t;
#endif // defined ( REFLECTOR )

enum
{
  ZDMATCH_INIT,           // Initialized
  ZDMATCH_WAIT_REQ,       // Received first request, waiting for second
  ZDMATCH_SENDING_BINDS   // Received both requests, sending unbind/binds
};

enum
{
  ZDMATCH_SENDING_NOT,
  ZDMATCH_SENDING_UNBIND,
  ZDMATCH_SENDING_BIND
};

static uint16 ZDOBuildBuf[26];  // temp area to build data without allocation

#if defined ( REFLECTOR )
static ZDO_EDBind_t *ZDO_EDBind;     // Null when not used
#endif

#if defined ( MANAGED_SCAN )
  uint32 managedScanNextChannel = 0;
  uint32 managedScanChannelMask = 0;
  uint8  managedScanTimesPerChannel = 0;
#endif

ZDMatchEndDeviceBind_t *matchED = (ZDMatchEndDeviceBind_t *)NULL;

uint32 apsChannelMask = 0;

static void ZDODeviceSetup( void );
#if defined ( MANAGED_SCAN )
  static void ZDOManagedScan_Next( void );
#endif
#if defined ( REFLECTOR )
  static void ZDO_RemoveEndDeviceBind( void );
  static void ZDO_SendEDBindRsp( byte TransSeq, zAddrType_t *dstAddr, byte Status, byte secUse );
#endif
static byte ZDO_CompareClusterLists( byte numList1, uint16 *list1,
                                byte numList2, uint16 *list2, uint16 *pMatches );
static void ZDO_RemoveMatchMemory( void );
static uint8 ZDO_CopyMatchInfo( ZDEndDeviceBind_t *destReq, ZDEndDeviceBind_t *srcReq );
static void ZDO_EndDeviceBindMatchTimeoutCB( void );
uint8 *ZDO_ConvertOTAClusters( uint8 cnt, uint8 *inBuf, uint16 *outList );
static void zdoSendStateChangeMsg(uint8 state, uint8 taskId);

void ZDO_Init( void )
{
  // Initialize ZD items
  #if defined ( REFLECTOR )
  ZDO_EDBind = NULL;
  #endif

  // Initialize default ZDO_UseExtendedPANID to the APS one.
  osal_cpyExtAddr( ZDO_UseExtendedPANID, AIB_apsUseExtendedPANID );

  // Setup the device - type of device to create.
  ZDODeviceSetup();
}

#if defined ( MANAGED_SCAN )

static void ZDOManagedScan_Next( void )
{
  // Is it the first time
  if ( managedScanNextChannel == 0 && managedScanTimesPerChannel == 0 )
  {
    // Setup the defaults
    managedScanNextChannel  = 1;

    while( managedScanNextChannel && (zgDefaultChannelList & managedScanNextChannel) == 0 )
      managedScanNextChannel <<= 1;

    managedScanChannelMask = managedScanNextChannel;
    managedScanTimesPerChannel = MANAGEDSCAN_TIMES_PRE_CHANNEL;
  }
  else
  {
    // Do we need to go to the next channel
    if ( managedScanTimesPerChannel == 0 )
    {
      // Find next active channel
      managedScanChannelMask  = managedScanNextChannel;
      managedScanTimesPerChannel = MANAGEDSCAN_TIMES_PRE_CHANNEL;
    }
    else
    {
      managedScanTimesPerChannel--;

      if ( managedScanTimesPerChannel == 0 )
      {
        managedScanNextChannel  <<= 1;
        while( managedScanNextChannel && (zgDefaultChannelList & managedScanNextChannel) == 0 )
          managedScanNextChannel <<= 1;

        if ( managedScanNextChannel == 0 )
          zdoDiscCounter  = NUM_DISC_ATTEMPTS + 1; // Stop
      }
    }
  }
}
#endif // MANAGED_SCAN

static void ZDODeviceSetup( void )
{
  if ( ZG_BUILD_COORDINATOR_TYPE )
  {
    NLME_CoordinatorInit();
  }

#if defined ( REFLECTOR )
  APS_ReflectorInit( (ZG_DEVICE_COORDINATOR_TYPE) ? APS_REFLECTOR_PUBLIC :  APS_REFLECTOR_PRIVATE );
#endif

  if ( ZG_BUILD_JOINING_TYPE )
  {
    NLME_DeviceJoiningInit();
  }
}

void ZDO_StartDevice( byte logicalType, devStartModes_t startMode, byte beaconOrder, byte superframeOrder )
{
  ZStatus_t ret;
#if defined ( ZIGBEE_FREQ_AGILITY )
  static uint8 discRetries = 0;
#endif
#if defined ( ZIGBEE_COMMISSIONING )
  static uint8 scanCnt = 0;
#endif

  ret = ZUnsupportedMode;

  if ( ZG_BUILD_COORDINATOR_TYPE && logicalType == NODETYPE_COORDINATOR )
  {
    if ( startMode == MODE_HARD )
    {
      devState = DEV_COORD_STARTING;
      ret = NLME_NetworkFormationRequest( zgConfigPANID, zgApsUseExtendedPANID, zgDefaultChannelList,
                                          zgDefaultStartingScanDuration, beaconOrder,
                                          superframeOrder, false );
    }
    else if ( startMode == MODE_RESUME )
    {
      // Just start the coordinator
      devState = DEV_COORD_STARTING;
      ret = NLME_StartRouterRequest( beaconOrder, beaconOrder, false );
    }
    else
    {
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
#endif
    }
  }

  if ( ZG_BUILD_JOINING_TYPE && (logicalType == NODETYPE_ROUTER || logicalType == NODETYPE_DEVICE) )
  {
    if ( (startMode == MODE_JOIN) || (startMode == MODE_REJOIN) )
    {
      devState = DEV_NWK_DISC;

  #if defined( MANAGED_SCAN )
      ZDOManagedScan_Next();
      ret = NLME_NetworkDiscoveryRequest( managedScanChannelMask, BEACON_ORDER_15_MSEC );
  #else
      ret = NLME_NetworkDiscoveryRequest( zgDefaultChannelList, zgDefaultStartingScanDuration );
    #if defined ( ZIGBEE_FREQ_AGILITY )
      if ( !( ZDO_Config_Node_Descriptor.CapabilityFlags & CAPINFO_RCVR_ON_IDLE ) &&
            ( ret == ZSuccess ) && ( ++discRetries == 4 ) )
      {
        // For devices with RxOnWhenIdle equals to FALSE, any network channel
        // change will not be recieved. On these devices or routers that have
        // lost the network, an active scan shall be conducted on the Default
        // Channel list using the extended PANID to find the network. If the
        // extended PANID isn't found using the Default Channel list, an scan
        // should be completed using all channels.
        zgDefaultChannelList = MAX_CHANNELS_24GHZ;
      }
    #endif // ZIGBEE_FREQ_AGILITY
    #if defined ( ZIGBEE_COMMISSIONING )
      if (startMode == MODE_REJOIN && scanCnt++ >= 5 )
      {
        // When ApsUseExtendedPanID is commissioned to a non zero value via
        // application specific means, the device shall conduct an active scan
        // on the Default Channel list and join the PAN with the same
        // ExtendedPanID. If the PAN is not found, an scan should be completed
        // on all channels.
        // When devices rejoin the network and the PAN is not found from
        zgDefaultChannelList = MAX_CHANNELS_24GHZ;
      }
    #endif // ZIGBEE_COMMISSIONING
  #endif
    }
    else if ( startMode == MODE_RESUME )
    {
      if ( logicalType == NODETYPE_ROUTER )
      {
        ZMacScanCnf_t scanCnf;
        devState = DEV_NWK_ORPHAN;

        /* if router and nvram is available, fake successful orphan scan */
        scanCnf.hdr.Status = ZSUCCESS;
        scanCnf.ScanType = ZMAC_ORPHAN_SCAN;
        scanCnf.UnscannedChannels = 0;
        scanCnf.ResultListSize = 0;
        nwk_ScanJoiningOrphan(&scanCnf);

        ret = ZSuccess;
      }
      else
      {
        devState = DEV_NWK_ORPHAN;
        ret = NLME_OrphanJoinRequest( zgDefaultChannelList,
                                      zgDefaultStartingScanDuration );
      }
    }
    else
    {
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( "StartDevice ERR", "MODE unknown" );
#endif
    }
  }

  if ( ret != ZSuccess )
  {
    osal_start_timerEx(ZDAppTaskID, ZDO_NETWORK_INIT, NWK_RETRY_DELAY );
  }
}

static void zdoSendStateChangeMsg(uint8 state, uint8 taskId)
{
  osal_event_hdr_t *pMsg = (osal_event_hdr_t *)osal_msg_find(taskId, ZDO_STATE_CHANGE);

  if (NULL == pMsg)
  {
    if (NULL == (pMsg = (osal_event_hdr_t *)osal_msg_allocate(sizeof(osal_event_hdr_t))))
    {
      // Upon failure to notify any EndPoint of the state change, re-set the ZDO event to
      // try again later when more Heap may be available.
      osal_set_event(ZDAppTaskID, ZDO_STATE_CHANGE_EVT);
    }
    else
    {
      pMsg->event = ZDO_STATE_CHANGE;
      pMsg->status = state;

      (void)osal_msg_send(taskId, (uint8 *)pMsg);
    }
  }
  else
  {
    // Modify in place the status of an existing ZDO_STATE_CHANGE message to the EndPoint.
    pMsg->status = state;
  }
}

void ZDO_UpdateNwkStatus(devStates_t state)
{
  epList_t *pItem = epList;

  while (pItem != NULL)
  {
    if (pItem->epDesc->endPoint != ZDO_EP)
    {
      zdoSendStateChangeMsg(state, *(pItem->epDesc->task_id));
    }

    pItem = pItem->nextDesc;
  }
#if defined MT_ZDO_CB_FUNC
  zdoSendStateChangeMsg(state, MT_TaskID);
#endif

  ZDAppNwkAddr.addr.shortAddr = NLME_GetShortAddr();
  (void)NLME_GetExtAddr();  // Load the saveExtAddr pointer.
}

#if defined ( REFLECTOR )

static void ZDO_RemoveEndDeviceBind( void )
{
  if ( ZDO_EDBind != NULL )
  {
    // Free the RAM
    if ( ZDO_EDBind->inClusters != NULL )
    {
      osal_mem_free( ZDO_EDBind->inClusters );
    }
    if ( ZDO_EDBind->outClusters != NULL )
    {
      osal_mem_free( ZDO_EDBind->outClusters );
    }
    osal_mem_free( ZDO_EDBind );
    ZDO_EDBind = NULL;
  }
}
#endif // REFLECTOR

#if defined ( REFLECTOR )

static void ZDO_SendEDBindRsp( byte TransSeq, zAddrType_t *dstAddr, byte Status, byte secUse )
{
  ZDP_EndDeviceBindRsp( TransSeq, dstAddr, Status, secUse );

#if defined( LCD_SUPPORTED )
  HalLcdWriteString( "End Device Bind", HAL_LCD_LINE_1 );
  if ( Status == ZDP_SUCCESS )
  {
    HalLcdWriteString( "Success Sent", HAL_LCD_LINE_2 );
  }
  else
  {
    HalLcdWriteString( "Timeout", HAL_LCD_LINE_2 );
  }
#endif

}
#endif // REFLECTOR

static byte ZDO_CompareClusterLists( byte numList1, uint16 *list1,
                          byte numList2, uint16 *list2, uint16 *pMatches )
{
  byte x, y;
  uint16 z;
  byte numMatches = 0;

  // Check the first in against the seconds out
  for ( x = 0; x < numList1; x++ )
  {
    for ( y = 0; y < numList2; y++ )
    {
      z = list2[y];
      if ( list1[x] == z )
      {
        pMatches[numMatches++] = z;
      }
    }
  }

  return ( numMatches );
}

byte ZDO_AnyClusterMatches( byte ACnt, uint16 *AList, byte BCnt, uint16 *BList )
{
  byte x, y;

  for ( x = 0; x < ACnt; x++ )
  {
    for ( y = 0; y < BCnt; y++ )
    {
      if ( AList[x] == BList[y] )
      {
        return true;
      }
    }
  }

  return false;
}

void ZDO_ProcessNodeDescReq( zdoIncomingMsg_t *inMsg )
{
  uint16 aoi = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );
  NodeDescriptorFormat_t *desc = NULL;

  if ( aoi == ZDAppNwkAddr.addr.shortAddr )
  {
    desc = &ZDO_Config_Node_Descriptor;
  }

  if ( desc != NULL )
  {
    ZDP_NodeDescMsg( inMsg, aoi, desc );
  }
  else
  {
    ZDP_GenericRsp( inMsg->TransSeq, &(inMsg->srcAddr),
              ZDP_INVALID_REQTYPE, aoi, Node_Desc_rsp, inMsg->SecurityUse );
  }
}


void ZDO_ProcessPowerDescReq( zdoIncomingMsg_t *inMsg )
{
  uint16 aoi = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );
  NodePowerDescriptorFormat_t *desc = NULL;

  if ( aoi == ZDAppNwkAddr.addr.shortAddr )
  {
    desc = &ZDO_Config_Power_Descriptor;
  }

  if ( desc != NULL )
  {
    ZDP_PowerDescMsg( inMsg, aoi, desc );
  }
  else
  {
    ZDP_GenericRsp( inMsg->TransSeq, &(inMsg->srcAddr),
              ZDP_INVALID_REQTYPE, aoi, Power_Desc_rsp, inMsg->SecurityUse );
  }
}

void ZDO_ProcessSimpleDescReq( zdoIncomingMsg_t *inMsg )
{
  SimpleDescriptionFormat_t *sDesc = NULL;
  uint16 aoi = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );
  byte endPoint = inMsg->asdu[2];
  byte free = false;
  byte stat = ZDP_SUCCESS;

  if ( (endPoint == ZDO_EP) || (endPoint > MAX_ENDPOINTS) )
  {
    stat = ZDP_INVALID_EP;
  }
  else if ( aoi == ZDAppNwkAddr.addr.shortAddr )
  {
    free = afFindSimpleDesc( &sDesc, endPoint );
    if ( sDesc == NULL )
    {
      stat = ZDP_NOT_ACTIVE;
    }
  }
  else
  {
    if ( ZSTACK_ROUTER_BUILD )
    {
      stat = ZDP_DEVICE_NOT_FOUND;
    }
    else if ( ZSTACK_END_DEVICE_BUILD )
    {
      stat = ZDP_INVALID_REQTYPE;
    }
  }

  ZDP_SimpleDescMsg( inMsg, stat, sDesc );

  if ( free && sDesc )
  {
    osal_mem_free( sDesc );
  }
}

void ZDO_ProcessActiveEPReq( zdoIncomingMsg_t *inMsg )
{
  byte cnt = 0;
  uint16 aoi;
  byte stat = ZDP_SUCCESS;

  aoi = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );

  if ( aoi == NLME_GetShortAddr() )
  {
    cnt = afNumEndPoints() - 1;  // -1 for ZDO endpoint descriptor
    afEndPoints( (uint8 *)ZDOBuildBuf, true );
  }
  else
  {
    stat = ZDP_INVALID_REQTYPE;
  }

  ZDP_ActiveEPRsp( inMsg->TransSeq, &(inMsg->srcAddr), stat,
                  aoi, cnt, (uint8 *)ZDOBuildBuf, inMsg->SecurityUse );
}

uint8 *ZDO_ConvertOTAClusters( uint8 cnt, uint8 *inBuf, uint16 *outList )
{
  uint8 x;

  for ( x = 0; x < cnt; x++ )
  {
    // convert ota format to internal
    outList[x] = BUILD_UINT16( inBuf[0], inBuf[1] );
    inBuf += sizeof( uint16 );
  }
  return ( inBuf );
}

void ZDO_ProcessMatchDescReq( zdoIncomingMsg_t *inMsg )
{
  uint8 epCnt = 0;
  uint8 numInClusters;
  uint16 *inClusters = NULL;
  uint8 numOutClusters;
  uint16 *outClusters = NULL;
  epList_t *epDesc;
  SimpleDescriptionFormat_t *sDesc = NULL;
  uint8 allocated;
  uint8 *msg;
  uint16 aoi;
  uint16 profileID;

  // Parse the incoming message
  msg = inMsg->asdu;
  aoi = BUILD_UINT16( msg[0], msg[1] );
  profileID = BUILD_UINT16( msg[2], msg[3] );
  msg += 4;

  if ( ADDR_BCAST_NOT_ME == NLME_IsAddressBroadcast(aoi) )
  {
    ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_INVALID_REQTYPE,
                          ZDAppNwkAddr.addr.shortAddr, 0, NULL, inMsg->SecurityUse );
    return;
  }
  else if ( (ADDR_NOT_BCAST == NLME_IsAddressBroadcast(aoi)) && (aoi != ZDAppNwkAddr.addr.shortAddr) )
  {
    ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_INVALID_REQTYPE,
                             ZDAppNwkAddr.addr.shortAddr, 0, NULL, inMsg->SecurityUse );
    return;
  }

  if ((numInClusters = *msg++) &&
      (inClusters = (uint16*)osal_mem_alloc( numInClusters * sizeof( uint16 ) )))
  {
    msg = ZDO_ConvertOTAClusters( numInClusters, msg, inClusters );
  }
  else
  {
    numInClusters = 0;
  }

  if ((numOutClusters = *msg++) &&
      (outClusters = (uint16 *)osal_mem_alloc( numOutClusters * sizeof( uint16 ) )))
  {
    msg = ZDO_ConvertOTAClusters( numOutClusters, msg, outClusters );
  }
  else
  {
    numOutClusters = 0;
  }

  // First count the number of endpoints that match.
  epDesc = epList;
  while ( epDesc )
  {
    // Don't search endpoint 0 and check if response is allowed
    if ( epDesc->epDesc->endPoint != ZDO_EP && (epDesc->flags&eEP_AllowMatch) )
    {
      if ( epDesc->pfnDescCB )
      {
        sDesc = (SimpleDescriptionFormat_t *)epDesc->pfnDescCB( AF_DESCRIPTOR_SIMPLE, epDesc->epDesc->endPoint );
        allocated = TRUE;
      }
      else
      {
        sDesc = epDesc->epDesc->simpleDesc;
        allocated = FALSE;
      }

      if ( sDesc && sDesc->AppProfId == profileID )
      {
        uint8 *uint8Buf = (uint8 *)ZDOBuildBuf;

        // Are there matching input clusters?
        if ((ZDO_AnyClusterMatches( numInClusters, inClusters,
                   sDesc->AppNumInClusters, sDesc->pAppInClusterList )) ||
            // Are there matching output clusters?
            (ZDO_AnyClusterMatches( numOutClusters, outClusters,
                   sDesc->AppNumOutClusters, sDesc->pAppOutClusterList )))
        {
          // Notify the endpoint of the match.
          uint8 bufLen = sizeof( ZDO_MatchDescRspSent_t ) + (numOutClusters + numInClusters) * sizeof(uint16);
          ZDO_MatchDescRspSent_t *pRspSent = (ZDO_MatchDescRspSent_t *) osal_msg_allocate( bufLen );

          if (pRspSent)
          {
            pRspSent->hdr.event = ZDO_MATCH_DESC_RSP_SENT;
            pRspSent->nwkAddr = inMsg->srcAddr.addr.shortAddr;
            pRspSent->numInClusters = numInClusters;
            pRspSent->numOutClusters = numOutClusters;

            if (numInClusters)
            {
              pRspSent->pInClusters = (uint16*) (pRspSent + 1);
              osal_memcpy(pRspSent->pInClusters, inClusters, numInClusters * sizeof(uint16));
            }
            else
            {
              pRspSent->pInClusters = NULL;
            }

            if (numOutClusters)
            {
              pRspSent->pOutClusters = (uint16*)(pRspSent + 1) + numInClusters;
              osal_memcpy(pRspSent->pOutClusters, outClusters, numOutClusters * sizeof(uint16));
            }
            else
            {
              pRspSent->pOutClusters = NULL;
            }

            osal_msg_send( *epDesc->epDesc->task_id, (uint8 *)pRspSent );
          }

          uint8Buf[epCnt++] = sDesc->EndPoint;
        }
      }

      if ( allocated )
      {
        osal_mem_free( sDesc );
      }
    }
    epDesc = epDesc->nextDesc;
  }

  if ( epCnt )
  {
    // Send the message if at least one match found.
    if ( ZSuccess == ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_SUCCESS,
              ZDAppNwkAddr.addr.shortAddr, epCnt, (uint8 *)ZDOBuildBuf, inMsg->SecurityUse ) )
    {
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( "Match Desc Req", "Rsp Sent" );
#endif
    }
  }
  else
  {
    // No match found
    if (ADDR_NOT_BCAST == NLME_IsAddressBroadcast(aoi))
    {
      // send response message with match length = 0
      ZDP_MatchDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZDP_SUCCESS,
                        ZDAppNwkAddr.addr.shortAddr, 0, (uint8 *)ZDOBuildBuf, inMsg->SecurityUse );
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( "Match Desc Req", "Rsp Non Matched" );
#endif
    }
    else
    {
      // no response mesage for broadcast message
#if defined( LCD_SUPPORTED )
      HalLcdWriteScreen( "Match Desc Req", "Non Matched" );
#endif
    }
  }

  if ( inClusters != NULL )
  {
    osal_mem_free( inClusters );
  }

  if ( outClusters != NULL )
  {
    osal_mem_free( outClusters );
  }
}

void ZDO_ProcessBindUnbindReq( zdoIncomingMsg_t *inMsg, ZDO_BindUnbindReq_t *pReq )
{
  zAddrType_t SourceAddr;       // Binding Source addres
  byte bindStat;

  SourceAddr.addrMode = Addr64Bit;
  osal_cpyExtAddr( SourceAddr.addr.extAddr, pReq->srcAddress );

  // If the local device is not the primary binding cache
  // check the src address of the bind request.
  // If it is not the local device's extended address
  // discard the request.
  if ( !osal_ExtAddrEqual( SourceAddr.addr.extAddr, NLME_GetExtAddr()) ||
        (pReq->dstAddress.addrMode != Addr64Bit &&
         pReq->dstAddress.addrMode != AddrGroup) )
  {
    bindStat = ZDP_NOT_SUPPORTED;
  }
  else
  {
    // Check source & destination endpoints
    if ( (pReq->srcEndpoint == 0 || pReq->srcEndpoint > MAX_ENDPOINTS)
        || (( pReq->dstAddress.addrMode == Addr64Bit ) &&
            (pReq->dstEndpoint == 0 || pReq->dstEndpoint > MAX_ENDPOINTS)) )
    {
      bindStat = ZDP_INVALID_EP;
    }
    else
    {
      if ( inMsg->clusterID == Bind_req )
      {
        // Assume the table is full
        bindStat = ZDP_TABLE_FULL;

#if defined( APP_TP ) || defined( APP_TP2 )
        // For ZigBee Conformance Testing
        if ( bindNumOfEntries() < gNWK_MAX_BINDING_ENTRIES )
#endif
        {
          if ( APSME_BindRequest( pReq->srcEndpoint, pReq->clusterID,
                         &(pReq->dstAddress), pReq->dstEndpoint ) == ZSuccess )
          {
            uint16 nwkAddr;

            // valid entry
            bindStat = ZDP_SUCCESS;

            // Notify to save info into NV
            ZDApp_NVUpdate();

            // Check for the destination address
            if ( pReq->dstAddress.addrMode == Addr64Bit )
            {
              if ( APSME_LookupNwkAddr( pReq->dstAddress.addr.extAddr, &nwkAddr ) == FALSE )
              {
                ZDP_NwkAddrReq( pReq->dstAddress.addr.extAddr, ZDP_ADDR_REQTYPE_SINGLE, 0, 0 );
              }
            }
          }
        }
      }
      else // Unbind_req
      {
        if ( APSME_UnBindRequest( pReq->srcEndpoint, pReq->clusterID,
                       &(pReq->dstAddress), pReq->dstEndpoint ) == ZSuccess )
        {
          bindStat = ZDP_SUCCESS;

          // Notify to save info into NV
          ZDApp_NVUpdate();
        }
        else
          bindStat = ZDP_NO_ENTRY;
      }
    }
  }

  // Send back a response message
  ZDP_SendData( &(inMsg->TransSeq), &(inMsg->srcAddr),
               (inMsg->clusterID | ZDO_RESPONSE_BIT), 1, &bindStat,
               inMsg->SecurityUse );
}

void ZDO_UpdateAddrManager( uint16 nwkAddr, uint8 *extAddr )
{
  AddrMgrEntry_t addrEntry;

  // Update the address manager
  addrEntry.user = ADDRMGR_USER_DEFAULT;
  addrEntry.nwkAddr = nwkAddr;
  AddrMgrExtAddrSet( addrEntry.extAddr, extAddr );
  AddrMgrEntryUpdate( &addrEntry );
}

void ZDO_ProcessServerDiscReq( zdoIncomingMsg_t *inMsg )
{
  uint16 serverMask = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );
  uint16 matchMask = serverMask & ZDO_Config_Node_Descriptor.ServerMask;

  if ( matchMask )
  {
    ZDP_ServerDiscRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZSUCCESS,
                ZDAppNwkAddr.addr.shortAddr, matchMask, inMsg->SecurityUse );
  }
}

void ZDO_EndDeviceTimeoutCB( void )
{
#if defined ( REFLECTOR )
  byte stat;
  if ( ZDO_EDBind )
  {
    stat = ZDO_EDBind->status;

    // Send the response message to the first sent
    ZDO_SendEDBindRsp( ZDO_EDBind->SrcTransSeq, &(ZDO_EDBind->SrcAddr),
                        stat, ZDO_EDBind->SecurityUse );

    ZDO_RemoveEndDeviceBind();
  }
#endif  // REFLECTOR
}

void ZDO_ProcessMgmtLqiReq( zdoIncomingMsg_t *inMsg )
{
  byte x;
  byte index;
  byte numItems;
  byte maxItems;
  ZDP_MgmtLqiItem_t* table = NULL;
  ZDP_MgmtLqiItem_t* item;
  neighborEntry_t    entry;
  byte aItems;
  associated_devices_t *aDevice;
  AddrMgrEntry_t  nwkEntry;
  uint8 StartIndex = inMsg->asdu[0];

  // Get the number of neighbor items
  NLME_GetRequest( nwkNumNeighborTableEntries, 0, &maxItems );

  // Get the number of associated items
  aItems = (uint8)AssocCount( PARENT, CHILD_FFD_RX_IDLE );

  // Total number of items
  maxItems += aItems;

  // Start with the supplied index
  if ( maxItems > StartIndex )
  {
    numItems = maxItems - StartIndex;

    // limit the size of the list
    if ( numItems > ZDO_MAX_LQI_ITEMS )
      numItems = ZDO_MAX_LQI_ITEMS;

    // Allocate the memory to build the table
    table = (ZDP_MgmtLqiItem_t*)osal_mem_alloc( (short)
              ( numItems * sizeof( ZDP_MgmtLqiItem_t ) ) );

    if ( table != NULL )
    {
      x = 0;
      item = table;
      index = StartIndex;

      // Loop through associated items and build list
      for ( ; x < numItems; x++ )
      {
        if ( index < aItems )
        {
          // get next associated device
          aDevice = AssocFindDevice( index++ );

          // set basic fields
          item->panID   = _NIB.nwkPanId;
          osal_cpyExtAddr( item->extPanID, _NIB.extendedPANID );
          item->nwkAddr = aDevice->shortAddr;
          item->permit  = ZDP_MGMT_BOOL_UNKNOWN;
          item->depth   = 0xFF;
          item->lqi     = aDevice->linkInfo.rxLqi;

          // set extented address
          nwkEntry.user    = ADDRMGR_USER_DEFAULT;
          nwkEntry.nwkAddr = aDevice->shortAddr;

          if ( AddrMgrEntryLookupNwk( &nwkEntry ) == TRUE )
          {
            osal_cpyExtAddr( item->extAddr, nwkEntry.extAddr );
          }
          else
          {
            osal_memset( item->extAddr, 0xFF, Z_EXTADDR_LEN );
          }

          // use association info to set other fields
          if ( aDevice->nodeRelation == PARENT )
          {
            if (  aDevice->shortAddr == 0 )
            {
              item->devType = ZDP_MGMT_DT_COORD;
              item->depth = 0;
            }
            else
            {
              item->devType = ZDP_MGMT_DT_ROUTER;
              item->depth = _NIB.nodeDepth - 1;
            }

            item->rxOnIdle = ZDP_MGMT_BOOL_UNKNOWN;
            item->relation = ZDP_MGMT_REL_PARENT;
          }
          else
          {
            // If not parent, then it's a child
            item->depth = _NIB.nodeDepth + 1;

            if ( aDevice->nodeRelation < CHILD_FFD )
            {
              item->devType = ZDP_MGMT_DT_ENDDEV;

              if ( aDevice->nodeRelation == CHILD_RFD )
              {
                item->rxOnIdle = FALSE;
              }
              else
              {
                item->rxOnIdle = TRUE;
              }
            }
            else
            {
              item->devType = ZDP_MGMT_DT_ROUTER;

              if ( aDevice->nodeRelation == CHILD_FFD )
              {
                item->rxOnIdle = FALSE;
              }
              else
              {
                item->rxOnIdle = TRUE;
              }
            }

            item->relation = ZDP_MGMT_REL_CHILD;
          }

          item++;
        }
        else
        {
          if ( StartIndex <= aItems )
            // Start with 1st neighbor
            index = 0;
          else
            // Start with >1st neighbor
            index = StartIndex - aItems;
          break;
        }
      }

      // Loop through neighbor items and finish list
      for ( ; x < numItems; x++ )
      {
        // Add next neighbor table item
        NLME_GetRequest( nwkNeighborTable, index++, &entry );

        // set ZDP_MgmtLqiItem_t fields
        item->panID    = entry.panId;
        osal_cpyExtAddr( item->extPanID, _NIB.extendedPANID );
        osal_memset( item->extAddr, 0xFF, Z_EXTADDR_LEN );
        item->nwkAddr  = entry.neighborAddress;
        item->rxOnIdle = ZDP_MGMT_BOOL_UNKNOWN;
        item->relation = ZDP_MGMT_REL_UNKNOWN;
        item->permit   = ZDP_MGMT_BOOL_UNKNOWN;
        item->depth    = 0xFF;
        item->lqi      = entry.linkInfo.rxLqi;

        if ( item->nwkAddr == 0 )
        {
          item->devType = ZDP_MGMT_DT_COORD;
        }
        else
        {
          item->devType = ZDP_MGMT_DT_ROUTER;
        }

        item++;
      }
    }
  }
  else
  {
    numItems = 0;
  }

  // Send response
  ZDP_MgmtLqiRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZSuccess, maxItems,
                  StartIndex, numItems, table, false );

  if ( table )
  {
    osal_mem_free( table );
  }
}

void ZDO_ProcessMgmtNwkDiscReq( zdoIncomingMsg_t *inMsg )
{
  NLME_ScanFields_t scan;
  uint8             index;
  uint8             *msg;

  msg = inMsg->asdu;
  scan.channels = osal_build_uint32( msg, 4 );
  msg += 4;
  scan.duration = *msg++;
  index         = *msg;
  scan.scanType = ZMAC_ACTIVE_SCAN;
  scan.scanApp  = NLME_DISC_SCAN;

  // Save off the information to be used for the response
  zdappMgmtNwkDiscReqInProgress          = true;
  zdappMgmtNwkDiscRspAddr.addrMode       = Addr16Bit;
  zdappMgmtNwkDiscRspAddr.addr.shortAddr = inMsg->srcAddr.addr.shortAddr;
  zdappMgmtNwkDiscStartIndex             = index;
  zdappMgmtNwkDiscRspTransSeq            = inMsg->TransSeq;

  if ( NLME_NwkDiscReq2( &scan ) != ZSuccess )
  {
    NLME_NwkDiscTerm();

    // zdappMgmtNwkDiscReqInProgress will be reset in the confirm callback
  }
}

#if defined ( ZDO_MGMT_NWKDISC_RESPONSE )

void ZDO_FinishProcessingMgmtNwkDiscReq( void )
{
  byte count, i, ResultCount = 0;
  networkDesc_t *newDesc = NULL, *pList, *NetworkList;

  NetworkList = nwk_getNwkDescList();

  // Count the number of nwk descriptors in the list
  pList = nwk_getNwkDescList();
  while (pList)
  {
    ResultCount++;
    pList = pList->nextDesc;
  }

  if ( ZSTACK_ROUTER_BUILD )
  {
    // Look for my PanID.
    pList = nwk_getNwkDescList();
    while ( pList )
    {
      if ( pList->panId == _NIB.nwkPanId )
      {
        break;
      }


      if ( !pList->nextDesc )
      {
        break;
      }
      pList = pList->nextDesc;
    }


    // If my Pan not present (query to a star network ZC or an isolated ZR?),
    // prepend it.
    if ( !pList || (pList->panId != _NIB.nwkPanId) )
    {
      newDesc = (networkDesc_t *)osal_mem_alloc( sizeof( networkDesc_t ) );
      if ( newDesc )
      {
        byte pJoin;

        newDesc->panId = _NIB.nwkPanId;
        newDesc->logicalChannel = _NIB.nwkLogicalChannel;
        newDesc->version = NLME_GetProtocolVersion();
        newDesc->stackProfile = zgStackProfile;

        //Extended PanID
        osal_cpyExtAddr( newDesc->extendedPANID, _NIB.extendedPANID);

        ZMacGetReq( ZMacAssociationPermit, &pJoin );
        newDesc->chosenRouter = ((pJoin) ? ZDAppNwkAddr.addr.shortAddr :
                                           INVALID_NODE_ADDR);

        newDesc->nextDesc = NetworkList;
        NetworkList = newDesc;
        ResultCount++;
      }
    }
  }

  // Calc the count and apply a max count.
  if ( zdappMgmtNwkDiscStartIndex > ResultCount )
  {
    count = 0;
  }
  else
  {
    count = ResultCount - zdappMgmtNwkDiscStartIndex;
    if ( count > ZDO_MAX_NWKDISC_ITEMS )
    {
      count = ZDO_MAX_NWKDISC_ITEMS;
    }

    // Move the list pointer up to the start index.
    for ( i = 0; i < zdappMgmtNwkDiscStartIndex; i++ )
    {
      NetworkList = NetworkList->nextDesc;
    }
  }

  ZDP_MgmtNwkDiscRsp( zdappMgmtNwkDiscRspTransSeq,
                     &zdappMgmtNwkDiscRspAddr, ZSuccess, ResultCount,
                      zdappMgmtNwkDiscStartIndex,
                      count,
                      NetworkList,
                      false );

  if ( ZSTACK_ROUTER_BUILD )
  {
    if ( newDesc != NULL )
    {
      osal_mem_free( newDesc );
    }
  }

  NLME_NwkDiscTerm();
}
#endif

void ZDO_ProcessMgmtRtgReq( zdoIncomingMsg_t *inMsg )
{
  byte x;
  byte maxNumItems;
  byte numItems = 0;
  uint8 *pBuf = NULL;
  rtgItem_t *pList;
  uint8 StartIndex = inMsg->asdu[0];

  // Get the number of table items
  NLME_GetRequest( nwkNumRoutingTableEntries, 0, &maxNumItems );

  if ( maxNumItems > StartIndex )
  {
    numItems = maxNumItems - StartIndex;    // Start at the passed in index

    // limit the size of the list
    if ( numItems > ZDO_MAX_RTG_ITEMS )
    {
      numItems = ZDO_MAX_RTG_ITEMS;
    }

    // Allocate the memory to build the table
    pBuf = osal_mem_alloc( (short)(sizeof( rtgItem_t ) * numItems) );

    if ( pBuf != NULL )
    {
      // Convert buffer to list
      pList = (rtgItem_t *)pBuf;

      // Loop through items and build list
      for ( x = 0; x < numItems; x++ )
      {
        NLME_GetRequest( nwkRoutingTable, (uint16)(x + StartIndex), (void*)pList );

        // Remap the status to the RoutingTableList Record Format defined in the ZigBee spec
        switch( pList->status )
        {
          case RT_ACTIVE:
            pList->status = ZDO_MGMT_RTG_ENTRY_ACTIVE;
            break;

          case RT_DISC:
            pList->status = ZDO_MGMT_RTG_ENTRY_DISCOVERY_UNDERWAY;
            break;

          case RT_LINK_FAIL:
            pList->status = ZDO_MGMT_RTG_ENTRY_DISCOVERY_FAILED;
            break;

          case RT_INIT:
          case RT_REPAIR:
          default:
            pList->status = ZDO_MGMT_RTG_ENTRY_INACTIVE;
            break;
        }

        // Increment pointer to next record
        pList++;
      }
    }
    else
    {
      numItems = 0;
    }
  }

  // Send response
  ZDP_MgmtRtgRsp( inMsg->TransSeq, &(inMsg->srcAddr), ZSuccess, maxNumItems, StartIndex, numItems,
                        (rtgItem_t *)pBuf, false );

  if ( pBuf != NULL )
  {
    osal_mem_free( pBuf );
  }
}

void ZDO_ProcessMgmtBindReq( zdoIncomingMsg_t *inMsg )
{
#if defined ( REFLECTOR )
  byte x;
  uint16 maxNumItems;
  uint16 numItems;
  uint8 *pBuf = NULL;
  apsBindingItem_t *pList;
  uint8 StartIndex = inMsg->asdu[0];
  uint8 status;

  // Get the number of table items
  APSME_GetRequest( apsNumBindingTableEntries, 0, (byte*)(&maxNumItems) );

  if ( maxNumItems > StartIndex )
  {
    numItems = maxNumItems - StartIndex;    // Start at the passed in index
  }
  else
  {
    numItems = 0;
  }

  // limit the size of the list
  if ( numItems > ZDO_MAX_BIND_ITEMS )
  {
    numItems = ZDO_MAX_BIND_ITEMS;
  }

  // Allocate the memory to build the table
  if ( numItems && (pBuf = osal_mem_alloc( sizeof( apsBindingItem_t ) * numItems )) )
  {
    status = ZSuccess;

    // Convert buffer to list
    pList = (apsBindingItem_t *)pBuf;

    // Loop through items and build list
    for ( x = 0; x < numItems; x++ )
    {
      APSME_GetRequest( apsBindingTable, (x + StartIndex), (void*)pList );
      pList++;
    }

  }
  else
  {
    status = ZDP_NOT_PERMITTED;
    numItems = 0;
  }

  // Send response
  ZDP_MgmtBindRsp( inMsg->TransSeq, &(inMsg->srcAddr), status, (byte)maxNumItems, StartIndex,
                   (byte)numItems, (apsBindingItem_t *)pBuf, false );

  if ( pBuf )
  {
    osal_mem_free( pBuf );
  }
#else
  (void)inMsg;
#endif
}

void ZDO_ProcessMgmtDirectJoinReq( zdoIncomingMsg_t *inMsg )
{
  uint8 *deviceAddr;
  uint8 capInfo;
  uint8 stat;

  // Parse the message
  deviceAddr = inMsg->asdu;
  capInfo = inMsg->asdu[Z_EXTADDR_LEN];

  stat = (byte) NLME_DirectJoinRequest( deviceAddr, capInfo );

  ZDP_MgmtDirectJoinRsp( inMsg->TransSeq, &(inMsg->srcAddr), stat, false );
}

void ZDO_ProcessMgmtLeaveReq( zdoIncomingMsg_t *inMsg )
{
  NLME_LeaveReq_t req;
  ZStatus_t       status;
  uint8           option;
  uint8 *msg = inMsg->asdu;

  if ( ( AddrMgrExtAddrValid( msg ) == FALSE                 ) ||
       ( osal_ExtAddrEqual( msg, NLME_GetExtAddr() ) == TRUE )    )
  {
    // Remove this device
    req.extAddr = NULL;
  }
  else
  {
    // Remove child device
    req.extAddr = msg;
  }

  option = msg[Z_EXTADDR_LEN];
  if ( option & ZDP_MGMT_LEAVE_REQ_RC )
  {
    req.removeChildren = TRUE;
  }

  if ( option & ZDP_MGMT_LEAVE_REQ_REJOIN )
  {
     req.rejoin = TRUE;
  }

  req.silent = FALSE;

  status = NLME_LeaveReq( &req );

  ZDP_MgmtLeaveRsp( inMsg->TransSeq, &(inMsg->srcAddr), status, FALSE );
}

void ZDO_ProcessMgmtPermitJoinReq( zdoIncomingMsg_t *inMsg )
{
  uint8 stat;
  uint8 duration;
  uint8 tcsig;

  duration = inMsg->asdu[ZDP_MGMT_PERMIT_JOIN_REQ_DURATION];
  tcsig    = inMsg->asdu[ZDP_MGMT_PERMIT_JOIN_REQ_TC_SIG];

  // Set the network layer permit join duration
  stat = (byte) NLME_PermitJoiningRequest( duration );

  // Handle the Trust Center Significance
  if ( ZG_SECURE_ENABLED && ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
  {
    if ( tcsig == TRUE )
    {
      ZDSecMgrPermitJoining( duration );
    }
  }

  // Send a response if unicast
  if ( !inMsg->wasBroadcast )
  {
    ZDP_MgmtPermitJoinRsp( inMsg->TransSeq, &(inMsg->srcAddr), stat, false );
  }
}

void ZDO_ProcessMgmtPermitJoinTimeout( void )
{
  #if defined( ZDO_MGMT_PERMIT_JOIN_RESPONSE )
  // Currently, only the ZDSecMgr needs to be notified
  if ( ZG_SECURE_ENABLED && ZG_BUILD_COORDINATOR_TYPE && ZG_DEVICE_COORDINATOR_TYPE )
  {
    ZDSecMgrPermitJoiningTimeout();
  }
  #endif
}

void ZDO_ProcessUserDescReq( zdoIncomingMsg_t *inMsg )
{
  uint16 aoi = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );
  UserDescriptorFormat_t userDesc;

  if ( (aoi == ZDAppNwkAddr.addr.shortAddr) && (ZSUCCESS == osal_nv_read(
             ZCD_NV_USERDESC, 0, sizeof(UserDescriptorFormat_t), &userDesc )) )
  {
    ZDP_UserDescRsp( inMsg->TransSeq, &(inMsg->srcAddr), aoi, &userDesc, false );
  }
  else
  {
    ZDP_GenericRsp(inMsg->TransSeq, &(inMsg->srcAddr),
           ZDP_NOT_SUPPORTED, aoi, User_Desc_rsp, inMsg->SecurityUse );
  }
}

void ZDO_ProcessUserDescSet( zdoIncomingMsg_t *inMsg )
{
  uint8 *msg;
  uint16 aoi;
  UserDescriptorFormat_t userDesc;
  uint8 outMsg[3];
  uint8 status;

  msg = inMsg->asdu;
  aoi = BUILD_UINT16( msg[0], msg[1] );

  if ( aoi == ZDAppNwkAddr.addr.shortAddr )
  {
    userDesc.len = (msg[2] < AF_MAX_USER_DESCRIPTOR_LEN) ? msg[2] : AF_MAX_USER_DESCRIPTOR_LEN;
    msg ++;  // increment one for the length field

    osal_memcpy( userDesc.desc, &msg[2], userDesc.len );
    osal_nv_write( ZCD_NV_USERDESC, 0, sizeof(UserDescriptorFormat_t), &userDesc );
    if ( userDesc.len != 0 )
    {
      ZDO_Config_Node_Descriptor.UserDescAvail = TRUE;
    }
    else
    {
      ZDO_Config_Node_Descriptor.UserDescAvail = FALSE;
    }

    status = ZDP_SUCCESS;
  }
  else
  {
    status =  ZDP_NOT_SUPPORTED;
  }

  outMsg[0] = status;
  outMsg[1] = LO_UINT16( aoi );
  outMsg[2] = LO_UINT16( aoi );

  ZDP_SendData( &(inMsg->TransSeq), &(inMsg->srcAddr), User_Desc_conf, 3, outMsg,
               inMsg->SecurityUse );
}

void ZDO_ProcessDeviceAnnce( zdoIncomingMsg_t *inMsg )
{
  ZDO_DeviceAnnce_t Annce;
  AddrMgrEntry_t addrEntry;
  uint8 parentExt[Z_EXTADDR_LEN];

  // Parse incoming message
  ZDO_ParseDeviceAnnce( inMsg, &Annce );

  if ( ZSTACK_END_DEVICE_BUILD )
  {
    // Make sure the message didn't come from myself - end device only
    if ( osal_ExtAddrEqual( NLME_GetExtAddr(), Annce.extAddr ) && Annce.nwkAddr == NLME_GetShortAddr() )
    {
      return;
    }
  }

#if defined ( ZIGBEE_STOCHASTIC_ADDRESSING )
  // Clean up the neighbor table
  nwkNeighborRemoveAllStranded();

  // If address conflict is detected, no need to update the address manager
  if ( NLME_CheckNewAddrSet( Annce.nwkAddr, Annce.extAddr )== ZFailure )
  {
    return;
  }
#endif

#if defined ( ZIGBEE_STOCHASTIC_ADDRESSING )
  // Check for parent's address
  NLME_GetCoordExtAddr( parentExt );
  if ( osal_ExtAddrEqual( parentExt, Annce.extAddr ) )
  {
    if ( Annce.nwkAddr != NLME_GetCoordShortAddr() )
    {
      // Set the Parent's MAC's new short address
      _NIB.nwkCoordAddress = Annce.nwkAddr;
      ZMacSetReq( ZMacCoordShortAddress, (byte*)&(_NIB.nwkCoordAddress) );
    }
  }

  if ( ZSTACK_ROUTER_BUILD )
  {
    // If the device annce comes from a end device child that has moved
    // to another parent, remove it from associated device list

    // If the dev annce is coming from other device's children,
    // (The dev annce from its own children shall be unicast to itself,
    // So check the mac destination address)
    // Remove it from the associated device list. If it is not
    // a child, no action will be taken in AssocRemove() anyway.
    if ( inMsg->macDestAddr != NLME_GetShortAddr() )
    {
      associated_devices_t *dev_ptr;

      // If it's an end device child
      dev_ptr = AssocGetWithExt( Annce.extAddr );
      if ( dev_ptr )
      {
        if ( dev_ptr->nodeRelation == CHILD_RFD ||
             dev_ptr->nodeRelation == CHILD_RFD_RX_IDLE )
        {
          AssocRemove( Annce.extAddr );
        }
      }
    }

    if ( Annce.nwkAddr != NLME_GetShortAddr() )
    {
      // If an associated device is found with matched extended Address,
      // update its short address
      if ( AssocChangeNwkAddr( Annce.nwkAddr, Annce.extAddr ) )
      {
        // Set event to save NV
        ZDApp_NVUpdate();
      }
    }
  }

  // Update the neighbor table
  nwkNeighborUpdateNwkAddr( Annce.nwkAddr, Annce.extAddr );

  // Assume that the device has moved, remove existing routing entries
  RTG_RemoveRtgEntry( Annce.nwkAddr, 0 );

#endif // ZIGBEE_STOCHASTIC_ADDRESSING

  // Fill in the extended address in address manager if we don't have it already.
  addrEntry.user = ADDRMGR_USER_DEFAULT;
  addrEntry.nwkAddr = Annce.nwkAddr;
  if ( AddrMgrEntryLookupNwk( &addrEntry ) )
  {
    osal_memset( parentExt, 0, Z_EXTADDR_LEN );
    if ( osal_ExtAddrEqual( parentExt, addrEntry.extAddr ) )
    {
      AddrMgrExtAddrSet( addrEntry.extAddr, Annce.extAddr );
      AddrMgrEntryUpdate( &addrEntry );
    }
  }

  // Update the short address in address manager if it's been changed
  AddrMgrExtAddrSet( addrEntry.extAddr, Annce.extAddr );
  if ( AddrMgrEntryLookupExt( &addrEntry ) )
  {
    if ( addrEntry.nwkAddr != Annce.nwkAddr )
    {
      addrEntry.nwkAddr = Annce.nwkAddr;
      AddrMgrEntryUpdate( &addrEntry );
    }
  }
}

void ZDO_BuildSimpleDescBuf( uint8 *buf, SimpleDescriptionFormat_t *desc )
{
  byte cnt;
  uint16 *ptr;

  *buf++ = desc->EndPoint;
  *buf++ = HI_UINT16( desc->AppProfId );
  *buf++ = LO_UINT16( desc->AppProfId );
  *buf++ = HI_UINT16( desc->AppDeviceId );
  *buf++ = LO_UINT16( desc->AppDeviceId );

  *buf++ = (byte)(desc->AppDevVer << 4);

  *buf++ = desc->AppNumInClusters;
  ptr = desc->pAppInClusterList;
  for ( cnt = 0; cnt < desc->AppNumInClusters; ptr++, cnt++ )
  {
    *buf++ = HI_UINT16( *ptr );
    *buf++ = LO_UINT16( *ptr );
  }

  *buf++ = desc->AppNumOutClusters;
  ptr = desc->pAppOutClusterList;
  for ( cnt = 0; cnt < desc->AppNumOutClusters; ptr++, cnt++ )
  {
    *buf++ = HI_UINT16( *ptr );
    *buf++ = LO_UINT16( *ptr );
  }
}
void ZDO_MatchEndDeviceBind( ZDEndDeviceBind_t *bindReq )
{
  zAddrType_t dstAddr;
  uint8 sendRsp = FALSE;
  uint8 status;

  // Is this the first request?
  if ( matchED == NULL )
  {
    // Create match info structure
    matchED = (ZDMatchEndDeviceBind_t *)osal_mem_alloc( sizeof ( ZDMatchEndDeviceBind_t ) );
    if ( matchED )
    {
      // Clear the structure
      osal_memset( (uint8 *)matchED, 0, sizeof ( ZDMatchEndDeviceBind_t ) );

      // Copy the first request's information
      if ( !ZDO_CopyMatchInfo( &(matchED->ed1), bindReq ) )
      {
        status = ZDP_NO_ENTRY;
        sendRsp = TRUE;
      }
    }
    else
    {
      status = ZDP_NO_ENTRY;
      sendRsp = TRUE;
    }

    if ( !sendRsp )
    {
      // Set into the correct state
      matchED->state = ZDMATCH_WAIT_REQ;

      // Setup the timeout
      APS_SetEndDeviceBindTimeout( AIB_MaxBindingTime, ZDO_EndDeviceBindMatchTimeoutCB );
    }
  }
  else
  {
      matchED->state = ZDMATCH_SENDING_BINDS;

      // Copy the 2nd request's information
      if ( !ZDO_CopyMatchInfo( &(matchED->ed2), bindReq ) )
      {
        status = ZDP_NO_ENTRY;
        sendRsp = TRUE;
      }

      // Make a source match for ed1
      matchED->ed1numMatched = ZDO_CompareClusterLists(
                  matchED->ed1.numOutClusters, matchED->ed1.outClusters,
                  matchED->ed2.numInClusters, matchED->ed2.inClusters, ZDOBuildBuf );
      if ( matchED->ed1numMatched )
      {
        // Save the match list
        matchED->ed1Matched = osal_mem_alloc( (short)(matchED->ed1numMatched * sizeof ( uint16 )) );
        if ( matchED->ed1Matched )
        {
          osal_memcpy( matchED->ed1Matched, ZDOBuildBuf, (matchED->ed1numMatched * sizeof ( uint16 )) );
        }
        else
        {
          // Allocation error, stop
          status = ZDP_NO_ENTRY;
          sendRsp = TRUE;
        }
      }

      // Make a source match for ed2
      matchED->ed2numMatched = ZDO_CompareClusterLists(
                  matchED->ed2.numOutClusters, matchED->ed2.outClusters,
                  matchED->ed1.numInClusters, matchED->ed1.inClusters, ZDOBuildBuf );
      if ( matchED->ed2numMatched )
      {
        // Save the match list
        matchED->ed2Matched = osal_mem_alloc( (short)(matchED->ed2numMatched * sizeof ( uint16 )) );
        if ( matchED->ed2Matched )
        {
          osal_memcpy( matchED->ed2Matched, ZDOBuildBuf, (matchED->ed2numMatched * sizeof ( uint16 )) );
        }
        else
        {
          // Allocation error, stop
          status = ZDP_NO_ENTRY;
          sendRsp = TRUE;
        }
      }

      if ( (sendRsp == FALSE) && (matchED->ed1numMatched || matchED->ed2numMatched) )
      {
        // Do the first unbind/bind state
        ZDMatchSendState( ZDMATCH_REASON_START, ZDP_SUCCESS, 0 );
      }
      else
      {
        status = ZDP_NO_MATCH;
        sendRsp = TRUE;
      }
  }

  if ( sendRsp )
  {
    // send response to this requester
    dstAddr.addrMode = Addr16Bit;
    dstAddr.addr.shortAddr = bindReq->srcAddr;
    ZDP_EndDeviceBindRsp( bindReq->TransSeq, &dstAddr, status, bindReq->SecurityUse );

    if ( matchED->state == ZDMATCH_SENDING_BINDS )
    {
      // send response to first requester
      dstAddr.addrMode = Addr16Bit;
      dstAddr.addr.shortAddr = matchED->ed1.srcAddr;
      ZDP_EndDeviceBindRsp( matchED->ed1.TransSeq, &dstAddr, status, matchED->ed1.SecurityUse );
    }

    // Process ended - release memory used
    ZDO_RemoveMatchMemory();
  }
}

static void ZDO_RemoveMatchMemory( void )
{
  if ( matchED != NULL )
  {
    if ( matchED->ed2Matched != NULL )
    {
      osal_mem_free( matchED->ed2Matched );
    }
    if ( matchED->ed1Matched != NULL )
    {
      osal_mem_free( matchED->ed1Matched );
    }
    if ( matchED->ed1.inClusters != NULL )
    {
      osal_mem_free( matchED->ed1.inClusters );
    }
    if ( matchED->ed1.outClusters != NULL )
    {
      osal_mem_free( matchED->ed1.outClusters );
    }
    if ( matchED->ed2.inClusters != NULL )
    {
      osal_mem_free( matchED->ed2.inClusters );
    }
    if ( matchED->ed2.outClusters != NULL )
    {
      osal_mem_free( matchED->ed2.outClusters );
    }

    osal_mem_free( matchED );
    matchED = (ZDMatchEndDeviceBind_t *)NULL;
  }
}

static uint8 ZDO_CopyMatchInfo( ZDEndDeviceBind_t *destReq, ZDEndDeviceBind_t *srcReq )
{
  uint8 allOK = TRUE;

  // Copy bind information into the match info structure
  osal_memcpy( (uint8 *)destReq, srcReq, sizeof ( ZDEndDeviceBind_t ) );

  // Initialize the destination cluster pointers
  destReq->inClusters = NULL;
  destReq->outClusters = NULL;

  // Copy input cluster IDs
  if ( srcReq->numInClusters )
  {
    destReq->inClusters = osal_mem_alloc( (short)(srcReq->numInClusters * sizeof ( uint16 )) );
    if ( destReq->inClusters )
    {
      // Copy the clusters
      osal_memcpy( (uint8*)(destReq->inClusters), (uint8 *)(srcReq->inClusters),
                      (srcReq->numInClusters * sizeof ( uint16 )) );
    }
    else
    {
      allOK = FALSE;
    }
  }

  // Copy output cluster IDs
  if ( srcReq->numOutClusters )
  {
    destReq->outClusters = osal_mem_alloc( (short)(srcReq->numOutClusters * sizeof ( uint16 )) );
    if ( destReq->outClusters )
    {
      // Copy the clusters
      osal_memcpy( (uint8 *)(destReq->outClusters), (uint8 *)(srcReq->outClusters),
                      (srcReq->numOutClusters * sizeof ( uint16 )) );
    }
    else
    {
      allOK = FALSE;
    }
  }

  if ( allOK == FALSE )
  {
    if ( destReq->inClusters != NULL )
    {
      osal_mem_free( destReq->inClusters );
    }
    if ( destReq->outClusters != NULL )
    {
      osal_mem_free( destReq->outClusters );
    }
  }

  return ( allOK );
}

uint8 ZDMatchSendState( uint8 reason, uint8 status, uint8 TransSeq )
{
  uint8 *dstIEEEAddr = NULL;
  uint8 dstEP = 0xFF;
  zAddrType_t dstAddr;
  zAddrType_t destinationAddr;
  uint16 msgType;
  uint16 clusterID = 0xFFFF;
  ZDEndDeviceBind_t *ed = NULL;
  uint8 rspStatus = ZDP_SUCCESS;

  if ( matchED == NULL )
  {
    return ( FALSE );
  }

  // Check sequence number
  if ( reason == ZDMATCH_REASON_BIND_RSP || reason == ZDMATCH_REASON_UNBIND_RSP )
  {
    if ( TransSeq != matchED->transSeq )
    {
      return( FALSE ); // ignore the message
    }
  }

  // turn off timer
  APS_SetEndDeviceBindTimeout( 0, ZDO_EndDeviceBindMatchTimeoutCB );

  if ( reason == ZDMATCH_REASON_TIMEOUT )
  {
    rspStatus = ZDP_TIMEOUT;    // The process will stop
  }

  if ( reason == ZDMATCH_REASON_START || reason == ZDMATCH_REASON_BIND_RSP )
  {
    matchED->sending = ZDMATCH_SENDING_UNBIND;

    if ( reason == ZDMATCH_REASON_BIND_RSP && status != ZDP_SUCCESS )
    {
      rspStatus = status;
    }
  }
  else if ( reason == ZDMATCH_REASON_UNBIND_RSP )
  {
    if ( status == ZDP_SUCCESS )
    {
      matchED->sending = ZDMATCH_SENDING_UNBIND;
    }
    else
    {
      matchED->sending = ZDMATCH_SENDING_BIND;
    }
  }

  if ( reason != ZDMATCH_REASON_START && matchED->sending == ZDMATCH_SENDING_UNBIND )
  {
    // Move to the next cluster ID
    if ( matchED->ed1numMatched )
    {
      matchED->ed1numMatched--;
    }
    else if ( matchED->ed2numMatched )
    {
      matchED->ed2numMatched--;
    }
  }

  // What message do we send now
  if ( matchED->ed1numMatched )
  {
    ed = &(matchED->ed1);
    clusterID = matchED->ed1Matched[matchED->ed1numMatched-1];
    dstIEEEAddr = matchED->ed2.ieeeAddr;
    dstEP = matchED->ed2.endpoint;
  }
  else if ( matchED->ed2numMatched )
  {
    ed = &(matchED->ed2);
    clusterID = matchED->ed2Matched[matchED->ed2numMatched-1];
    dstIEEEAddr = matchED->ed1.ieeeAddr;
    dstEP = matchED->ed1.endpoint;
  }

  dstAddr.addrMode = Addr16Bit;

  // Send the next message
  if ( (rspStatus == ZDP_SUCCESS) && ed )
  {
    // Send unbind/bind message to source
    if ( matchED->sending == ZDMATCH_SENDING_UNBIND )
    {
      msgType = Unbind_req;
    }
    else
    {
      msgType = Bind_req;
    }

    dstAddr.addr.shortAddr = ed->srcAddr;

    // Save off the transaction sequence number
    matchED->transSeq = ZDP_TransID;

    destinationAddr.addrMode = Addr64Bit;
    osal_cpyExtAddr( destinationAddr.addr.extAddr, dstIEEEAddr );

    ZDP_BindUnbindReq( msgType, &dstAddr, ed->ieeeAddr, ed->endpoint, clusterID,
        &destinationAddr, dstEP, ed->SecurityUse );

    // Set timeout for response
    APS_SetEndDeviceBindTimeout( AIB_MaxBindingTime, ZDO_EndDeviceBindMatchTimeoutCB );
  }
  else
  {
    // Send the response messages to requesting devices
    // send response to first requester
    dstAddr.addr.shortAddr = matchED->ed1.srcAddr;
    ZDP_EndDeviceBindRsp( matchED->ed1.TransSeq, &dstAddr, rspStatus, matchED->ed1.SecurityUse );

    // send response to second requester
    if ( matchED->state == ZDMATCH_SENDING_BINDS )
    {
      dstAddr.addr.shortAddr = matchED->ed2.srcAddr;
      ZDP_EndDeviceBindRsp( matchED->ed2.TransSeq, &dstAddr, rspStatus, matchED->ed2.SecurityUse );
    }

    // Process ended - release memory used
    ZDO_RemoveMatchMemory();
  }

  return ( TRUE );
}

static void ZDO_EndDeviceBindMatchTimeoutCB( void )
{
  ZDMatchSendState( ZDMATCH_REASON_TIMEOUT, ZDP_TIMEOUT, 0 );
}

void ZDO_ParseEndDeviceBindReq( zdoIncomingMsg_t *inMsg, ZDEndDeviceBind_t *bindReq )
{
  uint8 *msg;

  // Parse the message
  bindReq->TransSeq = inMsg->TransSeq;
  bindReq->srcAddr = inMsg->srcAddr.addr.shortAddr;
  bindReq->SecurityUse = inMsg->SecurityUse;
  msg = inMsg->asdu;

  bindReq->localCoordinator = BUILD_UINT16( msg[0], msg[1] );
  msg += 2;

  osal_cpyExtAddr( bindReq->ieeeAddr, msg );
  msg += Z_EXTADDR_LEN;

  bindReq->endpoint = *msg++;
  bindReq->profileID = BUILD_UINT16( msg[0], msg[1] );
  msg += 2;

  bindReq->inClusters = NULL;
  bindReq->outClusters = NULL;

  if ((bindReq->numInClusters = *msg++) &&
      (bindReq->inClusters = (uint16*)osal_mem_alloc( (bindReq->numInClusters * sizeof( uint16 )))))
  {
    msg = ZDO_ConvertOTAClusters( bindReq->numInClusters, msg, bindReq->inClusters );
  }
  else
  {
    bindReq->numInClusters = 0;
  }

  if ((bindReq->numOutClusters = *msg++) &&
      (bindReq->outClusters = (uint16*)osal_mem_alloc((bindReq->numOutClusters * sizeof(uint16)))))
  {
    msg = ZDO_ConvertOTAClusters( bindReq->numOutClusters, msg, bindReq->outClusters );
  }
  else
  {
    bindReq->numOutClusters = 0;
  }
}

void ZDO_ParseBindUnbindReq( zdoIncomingMsg_t *inMsg, ZDO_BindUnbindReq_t *pReq )
{
  uint8 *msg;

  msg = inMsg->asdu;
  osal_cpyExtAddr( pReq->srcAddress, msg );
  msg += Z_EXTADDR_LEN;
  pReq->srcEndpoint = *msg++;
  pReq->clusterID = BUILD_UINT16( msg[0], msg[1] );
  msg += 2;
  pReq->dstAddress.addrMode = *msg++;
  if ( pReq->dstAddress.addrMode == Addr64Bit )
  {
    osal_cpyExtAddr( pReq->dstAddress.addr.extAddr, msg );
    msg += Z_EXTADDR_LEN;
    pReq->dstEndpoint = *msg;
  }
  else
  {
    // copy group address
    pReq->dstAddress.addr.shortAddr = BUILD_UINT16( msg[0], msg[1] );
  }
}

ZDO_NwkIEEEAddrResp_t *ZDO_ParseAddrRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_NwkIEEEAddrResp_t *rsp;
  uint8 *msg;
  byte cnt = 0;

  // Calculate the number of items in the list
  if ( inMsg->asduLen > (1 + Z_EXTADDR_LEN + 2) )
  {
    cnt = inMsg->asdu[1 + Z_EXTADDR_LEN + 2];
  }
  else
  {
    cnt = 0;
  }

  // Make buffer
  rsp = (ZDO_NwkIEEEAddrResp_t *)osal_mem_alloc( sizeof(ZDO_NwkIEEEAddrResp_t) + (cnt * sizeof ( uint16 )) );

  if ( rsp )
  {
    msg = inMsg->asdu;

    rsp->status = *msg++;
    if ( rsp->status == ZDO_SUCCESS )
    {
      osal_cpyExtAddr( rsp->extAddr, msg );
      msg += Z_EXTADDR_LEN;
      rsp->nwkAddr = BUILD_UINT16( msg[0], msg[1] );

      msg += 2;
      rsp->numAssocDevs = 0;

      // StartIndex field is only present if NumAssocDev field is non-zero.
      if ( cnt > 0 )
      {
        uint16 *pList = &(rsp->devList[0]);
        byte n = cnt;

        rsp->numAssocDevs = *msg++;
        rsp->startIndex = *msg++;

        while ( n != 0 )
        {
          *pList++ = BUILD_UINT16( msg[0], msg[1] );
          msg += sizeof( uint16 );
          n--;
        }
      }
    }
  }

  return ( rsp );
}

void ZDO_ParseNodeDescRsp( zdoIncomingMsg_t *inMsg, ZDO_NodeDescRsp_t *pNDRsp )
{
  uint8 *msg;

  msg = inMsg->asdu;

  pNDRsp->status = *msg++;
  pNDRsp->nwkAddr = BUILD_UINT16( msg[0], msg[1] );

  if ( pNDRsp->status == ZDP_SUCCESS )
  {
    msg += 2;
    pNDRsp->nodeDesc.LogicalType = *msg & 0x07;

    pNDRsp->nodeDesc.ComplexDescAvail = ( *msg & 0x08 ) >> 3;
    pNDRsp->nodeDesc.UserDescAvail = ( *msg & 0x10 ) >> 4;

    msg++;  // Reserved bits.
    pNDRsp->nodeDesc.FrequencyBand = (*msg >> 3) & 0x1f;
    pNDRsp->nodeDesc.APSFlags = *msg++ & 0x07;
    pNDRsp->nodeDesc.CapabilityFlags = *msg++;
    pNDRsp->nodeDesc.ManufacturerCode[0] = *msg++;
    pNDRsp->nodeDesc.ManufacturerCode[1] = *msg++;
    pNDRsp->nodeDesc.MaxBufferSize = *msg++;
    pNDRsp->nodeDesc.MaxInTransferSize[0] = *msg++;
    pNDRsp->nodeDesc.MaxInTransferSize[1] = *msg++;
    pNDRsp->nodeDesc.ServerMask = BUILD_UINT16( msg[0], msg[1] );
    msg += 2;
    pNDRsp->nodeDesc.MaxOutTransferSize[0] = *msg++;
    pNDRsp->nodeDesc.MaxOutTransferSize[1] = *msg++;
    pNDRsp->nodeDesc.DescriptorCapability = *msg;
  }
}

void ZDO_ParsePowerDescRsp( zdoIncomingMsg_t *inMsg, ZDO_PowerRsp_t *pNPRsp )
{
  uint8 *msg;

  msg = inMsg->asdu;
  pNPRsp->status = *msg++;
  pNPRsp->nwkAddr = BUILD_UINT16( msg[0], msg[1] );

  if ( pNPRsp->status == ZDP_SUCCESS )
  {
    msg += 2;
    pNPRsp->pwrDesc.AvailablePowerSources = *msg >> 4;
    pNPRsp->pwrDesc.PowerMode = *msg++ & 0x0F;
    pNPRsp->pwrDesc.CurrentPowerSourceLevel = *msg >> 4;
    pNPRsp->pwrDesc.CurrentPowerSource = *msg++ & 0x0F;
  }
}

void ZDO_ParseSimpleDescRsp( zdoIncomingMsg_t *inMsg, ZDO_SimpleDescRsp_t *pSimpleDescRsp )
{
  uint8 *msg;

  msg = inMsg->asdu;
  pSimpleDescRsp->status = *msg++;
  pSimpleDescRsp->nwkAddr = BUILD_UINT16( msg[0], msg[1] );
  msg += sizeof ( uint16 );
  msg++; // Skip past the length field.

  if ( pSimpleDescRsp->status == ZDP_SUCCESS )
  {
    ZDO_ParseSimpleDescBuf( msg, &(pSimpleDescRsp->simpleDesc) );
  }
}

ZDO_ActiveEndpointRsp_t *ZDO_ParseEPListRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_ActiveEndpointRsp_t *pRsp;
  uint8 *msg;
  uint8 Status;
  uint8 cnt;

  msg = inMsg->asdu;
  Status = *msg++;
  cnt = msg[2];

  pRsp = (ZDO_ActiveEndpointRsp_t *)osal_mem_alloc( sizeof(  ZDO_ActiveEndpointRsp_t ) + cnt );
  if ( pRsp )
  {
    pRsp->status = Status;
    pRsp->nwkAddr = BUILD_UINT16( msg[0], msg[1] );
    msg += sizeof( uint16 );
    pRsp->cnt = cnt;
    msg++; // pass cnt
    osal_memcpy( pRsp->epList, msg, cnt );
  }

  return ( pRsp );
}

void ZDO_ParseServerDiscRsp( zdoIncomingMsg_t *inMsg, ZDO_ServerDiscRsp_t *pRsp )
{
  pRsp->status = inMsg->asdu[0];
  pRsp->serverMask = BUILD_UINT16( inMsg->asdu[1], inMsg->asdu[2] );
}

ZDO_MgmtLqiRsp_t *ZDO_ParseMgmtLqiRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_MgmtLqiRsp_t *pRsp;
  uint8 status;
  uint8 startIndex = 0;
  uint8 neighborLqiCount = 0;
  uint8 neighborLqiEntries = 0;
  uint8 *msg;

  msg = inMsg->asdu;

  status = *msg++;
  if ( status == ZSuccess )
  {
    neighborLqiEntries = *msg++;
    startIndex = *msg++;
    neighborLqiCount = *msg++;
  }

  // Allocate a buffer big enough to handle the list.
  pRsp = (ZDO_MgmtLqiRsp_t *)osal_mem_alloc(
            sizeof( ZDO_MgmtLqiRsp_t ) + (neighborLqiCount * sizeof( neighborLqiItem_t )) );
  if ( pRsp )
  {
    uint8 x;
    neighborLqiItem_t *pList = pRsp->list;
    pRsp->status = status;
    pRsp->neighborLqiEntries = neighborLqiEntries;
    pRsp->startIndex = startIndex;
    pRsp->neighborLqiCount = neighborLqiCount;

    for ( x = 0; x < neighborLqiCount; x++ )
    {
      osal_cpyExtAddr(pList->extPANId, msg);   //Copy extended PAN ID
      msg += Z_EXTADDR_LEN;

      msg += Z_EXTADDR_LEN;  // Throwing away IEEE.
      pList->nwkAddr = BUILD_UINT16( msg[0], msg[1] );
      msg += 2 + 1 + 1 + 1;      // Skip DeviceType, RxOnIdle, Rlationship, PermitJoining and Depth
      pList->rxLqi = *msg++;
      pList->txQuality = 0;  // This is not specified OTA by ZigBee 1.1.
      pList++;
    }
  }

  return ( pRsp );
}

ZDO_MgmNwkDiscRsp_t *ZDO_ParseMgmNwkDiscRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_MgmNwkDiscRsp_t *pRsp;
  uint8 status;
  uint8 networkCount = 0;
  uint8 startIndex = 0;
  uint8 networkListCount = 0;
  uint8 *msg;

  msg = inMsg->asdu;
  status = *msg++;

  if ( status == ZSuccess )
  {
    networkCount = *msg++;
    startIndex = *msg++;
    networkListCount = *msg++;
  }

  // Allocate a buffer big enough to handle the list.
  pRsp = (ZDO_MgmNwkDiscRsp_t *)osal_mem_alloc( sizeof( ZDO_MgmNwkDiscRsp_t )
                                  + (networkListCount * sizeof( mgmtNwkDiscItem_t )) );
  if ( pRsp )
  {
    uint8 x;
    mgmtNwkDiscItem_t *pList;

    pRsp->status = status;
    pRsp->networkCount = networkCount;
    pRsp->startIndex = startIndex;
    pRsp->networkListCount = networkListCount;
    pList = pRsp->list;

    for ( x = 0; x < networkListCount; x++ )
    {
      osal_cpyExtAddr(pList->extendedPANID, msg);   //Copy extended PAN ID
      pList->PANId = BUILD_UINT16( msg[0], msg[1] );
      msg += Z_EXTADDR_LEN;

      pList->logicalChannel = *msg++;
      pList->stackProfile = (*msg) & 0x0F;
      pList->version = (*msg++ >> 4) & 0x0F;
      pList->beaconOrder = (*msg) & 0x0F;
      pList->superFrameOrder = (*msg++ >> 4) & 0x0F;
      pList->permitJoining = *msg++;
      pList++;
    }
  }

  return ( pRsp );
}

ZDO_MgmtRtgRsp_t *ZDO_ParseMgmtRtgRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_MgmtRtgRsp_t *pRsp;
  uint8 status;
  uint8 rtgCount = 0;
  uint8 startIndex = 0;
  uint8 rtgListCount = 0;
  uint8 *msg;

  msg = inMsg->asdu;

  status = *msg++;
  if ( status == ZSuccess )
  {
    rtgCount = *msg++;
    startIndex = *msg++;
    rtgListCount = *msg++;
  }

  // Allocate a buffer big enough to handle the list
  pRsp = (ZDO_MgmtRtgRsp_t *)osal_mem_alloc(
          sizeof( ZDO_MgmtRtgRsp_t ) + (rtgListCount * sizeof( rtgItem_t )) );
  if ( pRsp )
  {
    uint8 x;
    rtgItem_t *pList = pRsp->list;
    pRsp->status = status;
    pRsp->rtgCount = rtgCount;
    pRsp->startIndex = startIndex;
    pRsp->rtgListCount = rtgListCount;

    for ( x = 0; x < rtgListCount; x++ )
    {
      pList->dstAddress = BUILD_UINT16( msg[0], msg[1] );
      msg += 2;
      pList->status = *msg++;
      pList->nextHopAddress = BUILD_UINT16( msg[0], msg[1] );
      msg += 2;
      pList++;
    }
  }

  return ( pRsp );
}

ZDO_MgmtBindRsp_t *ZDO_ParseMgmtBindRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_MgmtBindRsp_t *pRsp;
  uint8 status;
  uint8 bindingCount = 0;
  uint8 startIndex = 0;
  uint8 bindingListCount = 0;
  uint8 *msg;

  msg = inMsg->asdu;

  status = *msg++;
  if ( status == ZSuccess )
  {
    bindingCount = *msg++;
    startIndex = *msg++;
    bindingListCount = *msg++;
  }

  // Allocate a buffer big enough to handle the list
  pRsp = (ZDO_MgmtBindRsp_t *)osal_mem_alloc(
          (sizeof ( ZDO_MgmtBindRsp_t ) + (bindingListCount * sizeof( apsBindingItem_t ))) );
  if ( pRsp )
  {
    uint8 x;
    apsBindingItem_t *pList = pRsp->list;
    pRsp->status = status;
    pRsp->bindingCount = bindingCount;
    pRsp->startIndex = startIndex;
    pRsp->bindingListCount = bindingListCount;

    for ( x = 0; x < bindingListCount; x++ )
    {
      osal_cpyExtAddr( pList->srcAddr, msg );
      msg += Z_EXTADDR_LEN;
      pList->srcEP = *msg++;

      // Get the Cluster ID

      pList->clusterID = BUILD_UINT16( msg[0], msg[1] );
      msg += 2;
      pList->dstAddr.addrMode = *msg++;
      if ( pList->dstAddr.addrMode == Addr64Bit )
      {
        osal_cpyExtAddr( pList->dstAddr.addr.extAddr, msg );
        msg += Z_EXTADDR_LEN;
        pList->dstEP = *msg++;
      }
      else
      {
        pList->dstAddr.addr.shortAddr = BUILD_UINT16( msg[0], msg[1] );
        msg += 2;
      }

      pList++;
    }
  }

  return ( pRsp );
}

ZDO_UserDescRsp_t *ZDO_ParseUserDescRsp( zdoIncomingMsg_t *inMsg )
{
  ZDO_UserDescRsp_t *pRsp;
  uint8 *msg;
  uint8 descLen = 0;

  msg = inMsg->asdu;

  if ( msg[0] == ZSuccess )
  {
    descLen = msg[3];
  }

  pRsp = (ZDO_UserDescRsp_t *)osal_mem_alloc( sizeof ( ZDO_UserDescRsp_t ) + descLen );
  if ( pRsp )
  {
    pRsp->status = msg[0];
    pRsp->nwkAddr = BUILD_UINT16( msg[1], msg[2] );
    pRsp->length = descLen;
    if ( descLen )
    {
      osal_memcpy( pRsp->desc, &msg[4], descLen );
    }
  }

  return ( pRsp );
}

uint8 ZDO_ParseSimpleDescBuf( uint8 *buf, SimpleDescriptionFormat_t *desc )
{
  uint8 num, i;

  desc->EndPoint = *buf++;
  desc->AppProfId = BUILD_UINT16( buf[0], buf[1] );
  buf += 2;
  desc->AppDeviceId = BUILD_UINT16( buf[0], buf[1] );
  buf += 2;
  desc->AppDevVer = *buf >> 4;

  desc->Reserved = 0;
  buf++;

  // move in input cluster list (if any). allocate aligned memory.
  num = desc->AppNumInClusters = *buf++;
  if ( num )
  {
    if (!(desc->pAppInClusterList = (uint16 *)osal_mem_alloc(num*sizeof(uint16))))
    {
      // malloc failed. we're done.
      return 1;
    }
    for (i=0; i<num; ++i)
    {
      desc->pAppInClusterList[i] = BUILD_UINT16( buf[0], buf[1] );
      buf += 2;
    }
  }

  // move in output cluster list (if any). allocate aligned memory.
  num = desc->AppNumOutClusters = *buf++;
  if (num)
  {
    if (!(desc->pAppOutClusterList = (uint16 *)osal_mem_alloc(num*sizeof(uint16))))
    {
      // malloc failed. free input cluster list memory if there is any
      if ( desc->pAppInClusterList != NULL )
      {
        osal_mem_free(desc->pAppInClusterList);

        desc->pAppInClusterList = NULL;
      }
      return 1;
    }
    for (i=0; i<num; ++i)
    {
      desc->pAppOutClusterList[i] = BUILD_UINT16( buf[0], buf[1] );
      buf += 2;
    }
  }
  return 0;
}


void ZDO_ParseDeviceAnnce( zdoIncomingMsg_t *inMsg, ZDO_DeviceAnnce_t *pAnnce )
{
  uint8 *msg;

  // Parse incoming message
  msg = inMsg->asdu;
  pAnnce->nwkAddr = BUILD_UINT16( msg[0], msg[1] );
  msg += 2;
  osal_cpyExtAddr( pAnnce->extAddr, msg );
  msg += Z_EXTADDR_LEN;
  pAnnce->capabilities = *msg;
}

ZDO_MgmtNwkUpdateNotify_t *ZDO_ParseMgmtNwkUpdateNotify( zdoIncomingMsg_t *inMsg )
{
  uint8 status;
  uint32 scannedChannels = 0;
  uint16 totalTransmissions = 0;
  uint16 transmissionFailures = 0;
  uint8 listCount = 0;
  uint8 *msg = inMsg->asdu;
  ZDO_MgmtNwkUpdateNotify_t *pRsp;

  status = *msg++;
  if ( status == ZSuccess )
  {
    scannedChannels = osal_build_uint32( msg, 4 );
    msg += 4;
    totalTransmissions = BUILD_UINT16( msg[0], msg[1] );
    msg += 2;
    transmissionFailures = BUILD_UINT16( msg[0], msg[1] );
    msg += 2;
    listCount = *msg++;
  }

  pRsp = (ZDO_MgmtNwkUpdateNotify_t *)osal_mem_alloc( sizeof ( ZDO_MgmtNwkUpdateNotify_t ) + listCount );

  if ( pRsp )
  {
    pRsp->status = status;
    pRsp->scannedChannels = scannedChannels;
    pRsp->totalTransmissions = totalTransmissions;
    pRsp->transmissionFailures = transmissionFailures;
    pRsp->listCount = listCount;

    // Allocate a buffer big enough to handle the list.
    if ( listCount > 0 )
    {
      osal_memcpy( pRsp->energyValues, msg, listCount );
    }
  }

  return ( pRsp );
}

void ZDO_ParseMgmtNwkUpdateReq( zdoIncomingMsg_t *inMsg, ZDO_MgmtNwkUpdateReq_t *pReq )
{
  uint8 *msg = inMsg->asdu;

  pReq->channelMask = osal_build_uint32( msg, 4 );
  msg += 4;
  pReq->scanDuration = *msg++;

  if ( pReq->scanDuration <= 0x05 )
  {
    // Request is to scan over channelMask
    pReq->scanCount = *msg;
  }
  else if ( ( pReq->scanDuration == 0xFE ) || ( pReq->scanDuration == 0xFF ) )
  {
    // Request is to change Channel (0xFE) or apsChannelMask and NwkManagerAddr (0xFF)
    pReq->nwkUpdateId = *msg++;

    if ( pReq->scanDuration == 0xFF )
    {
      pReq->nwkManagerAddr = BUILD_UINT16( msg[0], msg[1] );
    }
  }
}

ZDObject.h

#ifndef ZDOBJECT_H
#define ZDOBJECT_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "NLMEDE.h"
#include "ZDApp.h"

#if defined( ZIGBEE_FRAGMENTATION ) || ( SECURE == 0 )
  #define ZDO_MAX_LQI_ITEMS     3
#else
  #define ZDO_MAX_LQI_ITEMS     2
#endif

#define ZDO_MAX_NWKDISC_ITEMS   5
#define ZDO_MAX_RTG_ITEMS       10
#define ZDO_MAX_BIND_ITEMS      3

typedef enum
{
  MODE_JOIN,
  MODE_RESUME,
//MODE_SOFT,      // Not supported yet
  MODE_HARD,
  MODE_REJOIN
} devStartModes_t;

typedef struct
{
  uint8  status;
  uint16 nwkAddr;
  uint8  extAddr[Z_EXTADDR_LEN];
  uint8  numAssocDevs;
  uint8  startIndex;
  uint16 devList[];
} ZDO_NwkIEEEAddrResp_t;

typedef struct
{
  uint8 status;
  uint16 nwkAddr;   // Network address of interest
  NodeDescriptorFormat_t nodeDesc;
} ZDO_NodeDescRsp_t;

typedef struct
{
  uint8 status;
  uint16 nwkAddr;   // Network address of interest
  NodePowerDescriptorFormat_t pwrDesc;
} ZDO_PowerRsp_t;

typedef struct
{
  uint8  status;
  uint16 nwkAddr;   // Network address of interest
  SimpleDescriptionFormat_t simpleDesc;
} ZDO_SimpleDescRsp_t;

typedef struct
{
  uint8  status;
  uint16 nwkAddr;   // Network address of interest
  uint8  cnt;
  uint8  epList[];
} ZDO_ActiveEndpointRsp_t;

typedef ZDO_ActiveEndpointRsp_t ZDO_MatchDescRsp_t;

typedef struct
{
  uint8  status;
  uint8  networkCount;
  uint8  startIndex;
  uint8  networkListCount;
  mgmtNwkDiscItem_t list[];
} ZDO_MgmNwkDiscRsp_t;

typedef struct
{
  uint8  status;
  uint8  neighborLqiEntries;
  uint8  startIndex;
  uint8  neighborLqiCount;
  neighborLqiItem_t list[];
} ZDO_MgmtLqiRsp_t;

typedef struct
{
  uint8  status;
  uint8  rtgCount;
  uint8  startIndex;
  uint8  rtgListCount;
  rtgItem_t list[];
} ZDO_MgmtRtgRsp_t;

typedef struct
{
  uint8  status;
  uint8  bindingCount;
  uint8  startIndex;
  uint8  bindingListCount;
  apsBindingItem_t list[];
} ZDO_MgmtBindRsp_t;

typedef struct
{
  uint8  status;
  uint16 nwkAddr;   // Address of interest
  uint8  length;
  uint8  desc[];
} ZDO_UserDescRsp_t;

typedef struct
{
  uint8  status;
  uint16 serverMask;
} ZDO_ServerDiscRsp_t;

typedef struct
{
  uint8       srcAddress[Z_EXTADDR_LEN];
  uint8       srcEndpoint;
  uint16      clusterID;
  zAddrType_t dstAddress;
  uint8       dstEndpoint;
} ZDO_BindUnbindReq_t;

typedef struct
{
  uint16      nwkAddr;
  uint8       extAddr[Z_EXTADDR_LEN];
  uint8       capabilities;
} ZDO_DeviceAnnce_t;

typedef struct
{
  uint32 channelMask;
  uint8 scanDuration;
  uint8 scanCount;
  uint8 nwkUpdateId;
  int16 nwkManagerAddr;
} ZDO_MgmtNwkUpdateReq_t;

typedef struct
{
  uint8 status;
  uint32 scannedChannels;
  uint16 totalTransmissions;
  uint16 transmissionFailures;
  uint8  listCount;
  uint8  energyValues[];
} ZDO_MgmtNwkUpdateNotify_t;

enum
{
  ZDMATCH_REASON_START,
  ZDMATCH_REASON_TIMEOUT,
  ZDMATCH_REASON_UNBIND_RSP,
  ZDMATCH_REASON_BIND_RSP
};

typedef struct
{
  ZDEndDeviceBind_t ed1;
  ZDEndDeviceBind_t ed2;
  uint8  state;            // One of the above states
  uint8  sending;         // 0 - not sent, 1 - unbind, 2 bind - expecting response
  uint8  transSeq;
  uint8  ed1numMatched;
  uint16 *ed1Matched;
  uint8  ed2numMatched;
  uint16 *ed2Matched;
} ZDMatchEndDeviceBind_t;

extern ZDMatchEndDeviceBind_t *matchED;

extern void ZDO_Init( void );

extern void ZDO_StartDevice( byte logicalType, devStartModes_t startMode,
                             byte beaconOrder, byte superframeOrder );


extern void ZDO_UpdateNwkStatus( devStates_t state );

extern void ZDO_MatchEndDeviceBind( ZDEndDeviceBind_t *bindReq );


extern byte ZDO_AnyClusterMatches(
                              byte ACnt, uint16 *AList, byte BCnt, uint16 *BList );

extern void ZDO_ProcessNodeDescReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessPowerDescReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessSimpleDescReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessActiveEPReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMatchDescReq( zdoIncomingMsg_t *inMsg );

void ZDO_ProcessServerDiscRsp( zdoIncomingMsg_t *inMsg );

void ZDO_ProcessServerDiscReq( zdoIncomingMsg_t *inMsg );


extern uint8 ZDMatchSendState( uint8 reason, uint8 status, uint8 TransSeq );


extern void ZDO_EndDeviceTimeoutCB( void );


extern void ZDO_ProcessMgmNwkDiscRsp( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtNwkDiscReq( zdoIncomingMsg_t *inMsg );


extern void ZDO_FinishProcessingMgmtNwkDiscReq( void );

extern void ZDO_ParseMgmtNwkUpdateReq( zdoIncomingMsg_t *inMsg, ZDO_MgmtNwkUpdateReq_t *pReq );

extern void ZDO_ProcessMgmtLqiReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtRtgReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtBindReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtBindRsp( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtDirectJoinReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtLeaveReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessMgmtPermitJoinReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessUserDescReq( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessUserDescSet( zdoIncomingMsg_t *inMsg );

extern void ZDO_ProcessDeviceAnnce( zdoIncomingMsg_t *inMsg );

extern void ZDO_BuildSimpleDescBuf( uint8 *buf, SimpleDescriptionFormat_t *desc );

extern uint8 ZDO_ParseSimpleDescBuf( uint8 *buf, SimpleDescriptionFormat_t *desc );

extern void ZDO_UpdateAddrManager( uint16 nwkAddr, uint8 *extAddr );

extern ZDO_NwkIEEEAddrResp_t *ZDO_ParseAddrRsp( zdoIncomingMsg_t *inMsg );

extern void ZDO_ParseNodeDescRsp( zdoIncomingMsg_t *inMsg, ZDO_NodeDescRsp_t *pNDRsp );

extern void ZDO_ParsePowerDescRsp( zdoIncomingMsg_t *inMsg, ZDO_PowerRsp_t *pNPRsp );

extern void ZDO_ParseSimpleDescRsp( zdoIncomingMsg_t *inMsg, ZDO_SimpleDescRsp_t *pSimpleDescRsp );

extern ZDO_ActiveEndpointRsp_t *ZDO_ParseEPListRsp( zdoIncomingMsg_t *inMsg );

#define ZDO_ParseBindRsp(a) ((uint8)(*(a->asdu)))

extern ZDO_MgmNwkDiscRsp_t *ZDO_ParseMgmNwkDiscRsp( zdoIncomingMsg_t *inMsg );

extern ZDO_MgmtLqiRsp_t *ZDO_ParseMgmtLqiRsp( zdoIncomingMsg_t *inMsg );

extern ZDO_MgmtRtgRsp_t *ZDO_ParseMgmtRtgRsp( zdoIncomingMsg_t *inMsg );

extern ZDO_MgmtBindRsp_t *ZDO_ParseMgmtBindRsp( zdoIncomingMsg_t *inMsg );

#define ZDO_ParseMgmtDirectJoinRsp(a) ((uint8)(*(a->asdu)))

#define ZDO_ParseMgmtLeaveRsp(a) ((uint8)(*(a->asdu)))

#define ZDO_ParseMgmtPermitJoinRsp(a) ((uint8)(*(a->asdu)))

extern ZDO_UserDescRsp_t *ZDO_ParseUserDescRsp( zdoIncomingMsg_t *inMsg );

extern void ZDO_ParseServerDiscRsp( zdoIncomingMsg_t *inMsg, ZDO_ServerDiscRsp_t *pRsp );

extern void ZDO_ParseEndDeviceBindReq( zdoIncomingMsg_t *inMsg, ZDEndDeviceBind_t *bindReq );

extern void ZDO_ParseBindUnbindReq( zdoIncomingMsg_t *inMsg, ZDO_BindUnbindReq_t *pReq );

extern void ZDO_ProcessBindUnbindReq( zdoIncomingMsg_t *inMsg, ZDO_BindUnbindReq_t *pReq );

#define ZDO_ParseUserDescConf(a) ((uint8)(*(a->asdu)))

extern void ZDO_ParseDeviceAnnce( zdoIncomingMsg_t *inMsg, ZDO_DeviceAnnce_t *pAnnce );

extern ZDO_MgmtNwkUpdateNotify_t *ZDO_ParseMgmtNwkUpdateNotify( zdoIncomingMsg_t *inMsg );
#ifdef __cplusplus
}
#endif

#endif /* ZDOBJECT_H */

ZDProfile.c

#include "ZComdef.h"
#include "OSAL.h"
#include "AF.h"
#include "NLMEDE.h"
#include "nwk_util.h"
#include "APS.h"

#include "AddrMgr.h"
#include "ZDConfig.h"
#include "ZDProfile.h"
#include "ZDObject.h"
#include "ZDNwkMgr.h"

#if defined( LCD_SUPPORTED )
  #include "OnBoard.h"
#endif

#include "nwk_util.h"

#if defined( MT_ZDO_FUNC )
  #include "MT_ZDO.h"
#endif

#define ZADDR_TO_AFADDR( pZADDR, AFADDR ) {                            \
  (AFADDR).endPoint = ZDP_AF_ENDPOINT;                                 \
  (AFADDR).addrMode = (afAddrMode_t)(pZADDR)->addrMode;                \
  (AFADDR).addr.shortAddr = (pZADDR)->addr.shortAddr;                  \
}

#define FillAndSendBuffer( TRANSSEQ, ADDR, ID, LEN, BUF ) {     \
  afStatus_t stat;                                    \
  ZDP_TmpBuf = (BUF)+1;                               \
  stat = fillAndSend( (TRANSSEQ), (ADDR), (ID), (LEN) );          \
  osal_mem_free( (BUF) );                             \
  ZDP_TmpBuf = ZDP_Buf+1;                             \
  return stat;                                        \
}

#define FillAndSendTxOptions( TRANSSEQ, ADDR, ID, LEN, TxO ) {  \
  afStatus_t stat;                                    \
  ZDP_TxOptions = (TxO);                              \
  stat = fillAndSend( (TRANSSEQ), (ADDR), (ID), (LEN) );          \
  ZDP_TxOptions = AF_TX_OPTIONS_NONE;                 \
  return stat;                                        \
}

#define FillAndSendBufferTxOptions( TRANSSEQ, ADDR, ID, LEN, BUF, TxO ) { \
  afStatus_t stat;                                    \
  ZDP_TmpBuf = (BUF)+1;                               \
  ZDP_TxOptions = (TxO);                              \
  stat = fillAndSend( (TRANSSEQ), (ADDR), (ID), (LEN) );          \
  osal_mem_free( (BUF) );                             \
  ZDP_TmpBuf = ZDP_Buf+1;                             \
  ZDP_TxOptions = AF_TX_OPTIONS_NONE;                 \
  return stat;                                        \
}

#define ZDP_BUF_SZ          80

CONST byte ZDP_AF_ENDPOINT = 0;

typedef struct
{
  void *next;
  uint8 taskID;
  uint16 clusterID;
} ZDO_MsgCB_t;

byte ZDP_TransID = 0;

extern endPointDesc_t ZDApp_epDesc;


static afStatus_t fillAndSend( uint8 *transSeq, zAddrType_t *addr, cId_t clusterID, byte len );
uint8 ZDO_SendMsgCBs( zdoIncomingMsg_t *inMsg );
void zdpProcessAddrReq( zdoIncomingMsg_t *inMsg );

static uint8  ZDP_Buf[ ZDP_BUF_SZ ];
static uint8 *ZDP_TmpBuf = ZDP_Buf+1;

byte ZDP_TxOptions = AF_TX_OPTIONS_NONE;
ZDO_MsgCB_t *zdoMsgCBs = (ZDO_MsgCB_t *)NULL;

typedef void (*pfnZDPMsgProcessor)( zdoIncomingMsg_t *inMsg );

typedef struct
{
  uint16                clusterID;
  pfnZDPMsgProcessor    pFn;
} zdpMsgProcItem_t;

CONST zdpMsgProcItem_t zdpMsgProcs[] =
{
#if ( RFD_RCVC_ALWAYS_ON==TRUE ) || ( ZG_BUILD_RTR_TYPE )
  // These aren't processed by sleeping end devices.
  { NWK_addr_req,           zdpProcessAddrReq },
  { Device_annce,           ZDO_ProcessDeviceAnnce },
#endif
  { IEEE_addr_req,          zdpProcessAddrReq },
  { Node_Desc_req,          ZDO_ProcessNodeDescReq },
  { Power_Desc_req,         ZDO_ProcessPowerDescReq },
  { Simple_Desc_req,        ZDO_ProcessSimpleDescReq },
  { Active_EP_req,          ZDO_ProcessActiveEPReq },
  { Match_Desc_req,         ZDO_ProcessMatchDescReq },
#if defined ( ZDO_MGMT_NWKDISC_RESPONSE )
  { Mgmt_NWK_Disc_req,      ZDO_ProcessMgmtNwkDiscReq },
#endif
#if defined ( ZDO_MGMT_LQI_RESPONSE ) && ( ZG_BUILD_RTR_TYPE )
  { Mgmt_Lqi_req,           ZDO_ProcessMgmtLqiReq },
#endif
#if defined ( ZDO_MGMT_RTG_RESPONSE ) && ( ZG_BUILD_RTR_TYPE )
  { Mgmt_Rtg_req,           ZDO_ProcessMgmtRtgReq },
#endif
#if defined ( ZDO_MGMT_BIND_RESPONSE ) && defined ( REFLECTOR )
  { Mgmt_Bind_req,          ZDO_ProcessMgmtBindReq },
#endif
#if defined ( ZDO_MGMT_JOINDIRECT_RESPONSE ) && ( ZG_BUILD_RTR_TYPE )
  { Mgmt_Direct_Join_req,   ZDO_ProcessMgmtDirectJoinReq },
#endif
#if defined ( ZDO_MGMT_LEAVE_RESPONSE )
  { Mgmt_Leave_req,         ZDO_ProcessMgmtLeaveReq },
#endif
#if defined ( ZDO_MGMT_PERMIT_JOIN_RESPONSE )  && ( ZG_BUILD_RTR_TYPE )
  { Mgmt_Permit_Join_req,   ZDO_ProcessMgmtPermitJoinReq },
#endif
#if defined ( ZDO_USERDESC_RESPONSE )
  { User_Desc_req,          ZDO_ProcessUserDescReq },
#endif
#if defined ( ZDO_USERDESCSET_RESPONSE )
  { User_Desc_set,          ZDO_ProcessUserDescSet },
#endif
#if defined ( ZDO_SERVERDISC_RESPONSE )
  { Server_Discovery_req,   ZDO_ProcessServerDiscReq },
#endif
  {0xFFFF, NULL} // Last
};

static afStatus_t fillAndSend( uint8 *transSeq, zAddrType_t *addr, cId_t clusterID, byte len )
{
  afAddrType_t afAddr;

  osal_memset( &afAddr, 0, sizeof(afAddrType_t) );
  ZADDR_TO_AFADDR( addr, afAddr );

  *(ZDP_TmpBuf-1) = *transSeq;

  return AF_DataRequest( &afAddr, &ZDApp_epDesc, clusterID,
                           (uint16)(len+1), (uint8*)(ZDP_TmpBuf-1),
                           transSeq, ZDP_TxOptions,  AF_DEFAULT_RADIUS );

}

afStatus_t ZDP_SendData( uint8 *TransSeq, zAddrType_t *dstAddr, uint16 cmd,
                        byte len, uint8 *buf, byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte cnt = len;

  while ( cnt-- )
  {
    *pBuf++ = *buf++;
  }

  FillAndSendTxOptions( TransSeq, dstAddr, cmd, len, ((SecurityEnable) ? AF_EN_SECURITY : 0) );
}

afStatus_t ZDP_NWKAddrOfInterestReq( zAddrType_t *dstAddr, uint16 nwkAddr,
                                     byte cmd, byte SecurityEnable )
{
  (void)SecurityEnable;  // Intentionally unreferenced parameter

  ZDP_TmpBuf[0] = LO_UINT16( nwkAddr );
  ZDP_TmpBuf[1] = HI_UINT16( nwkAddr );

  return fillAndSend( &ZDP_TransID, dstAddr, cmd, 2 );
}

afStatus_t ZDP_NwkAddrReq( uint8 *IEEEAddress, byte ReqType,
                           byte StartIndex, byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = Z_EXTADDR_LEN + 1 + 1;  // IEEEAddress + ReqType + StartIndex.
  zAddrType_t dstAddr;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  if ( osal_ExtAddrEqual( saveExtAddr, IEEEAddress ) == FALSE )
  {
    dstAddr.addrMode = AddrBroadcast;
    dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVRXON;
  }
  else
  {
    dstAddr.addrMode = Addr16Bit;
    dstAddr.addr.shortAddr = ZDAppNwkAddr.addr.shortAddr;
  }

  pBuf = osal_cpyExtAddr( pBuf, IEEEAddress );

  *pBuf++ = ReqType;
  *pBuf++ = StartIndex;

  return fillAndSend( &ZDP_TransID, &dstAddr, NWK_addr_req, len );
}

afStatus_t ZDP_IEEEAddrReq( uint16 shortAddr, byte ReqType,
                            byte StartIndex, byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = 2 + 1 + 1;  // shortAddr + ReqType + StartIndex.
  zAddrType_t dstAddr;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  dstAddr.addrMode = (afAddrMode_t)Addr16Bit;
  dstAddr.addr.shortAddr = shortAddr;

  *pBuf++ = LO_UINT16( shortAddr );
  *pBuf++ = HI_UINT16( shortAddr );

  *pBuf++ = ReqType;
  *pBuf++ = StartIndex;

  return fillAndSend( &ZDP_TransID, &dstAddr, IEEE_addr_req, len );
}

afStatus_t ZDP_MatchDescReq( zAddrType_t *dstAddr, uint16 nwkAddr,
                                uint16 ProfileID,
                                byte NumInClusters, cId_t *InClusterList,
                                byte NumOutClusters, cId_t *OutClusterList,
                                byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  // nwkAddr+ProfileID+NumInClusters+NumOutClusters.
  byte i, len = 2 + 2 + 1 + 1;  // nwkAddr+ProfileID+NumInClusters+NumOutClusters.

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  len += (NumInClusters + NumOutClusters) * sizeof(uint16);

  if ( len >= ZDP_BUF_SZ-1 )
  {
    return afStatus_MEM_FAIL;
  }

  // The spec changed in Zigbee 2007 (2.4.3.1.7.1) to not allow sending
  // this command to 0xFFFF.  So, here we will filter this and replace 
  // with 0xFFFD to only send to devices with RX ON.  This includes the 
  // network address of interest.
  if ( ((dstAddr->addrMode == AddrBroadcast) || (dstAddr->addrMode == Addr16Bit))
      && (dstAddr->addr.shortAddr == NWK_BROADCAST_SHORTADDR_DEVALL) )
  {
    dstAddr->addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVRXON;
  }
  if ( nwkAddr == NWK_BROADCAST_SHORTADDR_DEVALL )
  {
    nwkAddr = NWK_BROADCAST_SHORTADDR_DEVRXON;
  }
  
  *pBuf++ = LO_UINT16( nwkAddr );   // NWKAddrOfInterest
  *pBuf++ = HI_UINT16( nwkAddr );

  *pBuf++ = LO_UINT16( ProfileID );   // Profile ID
  *pBuf++ = HI_UINT16( ProfileID );

  *pBuf++ = NumInClusters; // Input cluster list
  if ( NumInClusters )
  {
    for (i=0; i<NumInClusters; ++i)  {
      *pBuf++ = LO_UINT16( InClusterList[i] );
      *pBuf++ = HI_UINT16( InClusterList[i] );
    }
  }

  *pBuf++ = NumOutClusters; // Output cluster list
  if ( NumOutClusters )
  {
    for (i=0; i<NumOutClusters; ++i)  {
      *pBuf++ = LO_UINT16( OutClusterList[i] );
      *pBuf++ = HI_UINT16( OutClusterList[i] );
    }
  }

  return fillAndSend( &ZDP_TransID, dstAddr, Match_Desc_req, len );
}

afStatus_t ZDP_SimpleDescReq( zAddrType_t *dstAddr, uint16 nwkAddr,
                                    byte endPoint, byte SecurityEnable )

{
  (void)SecurityEnable;  // Intentionally unreferenced parameter

  ZDP_TmpBuf[0] = LO_UINT16( nwkAddr );
  ZDP_TmpBuf[1] = HI_UINT16( nwkAddr );
  ZDP_TmpBuf[2] = endPoint;

  return fillAndSend( &ZDP_TransID, dstAddr, Simple_Desc_req, 3 );
}

afStatus_t ZDP_UserDescSet( zAddrType_t *dstAddr, uint16 nwkAddr,
                          UserDescriptorFormat_t *UserDescriptor,
                          byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = (UserDescriptor->len < AF_MAX_USER_DESCRIPTOR_LEN) ?
              UserDescriptor->len : AF_MAX_USER_DESCRIPTOR_LEN;
  byte addrLen = 2;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  *pBuf++ = LO_UINT16( nwkAddr );
  *pBuf++ = HI_UINT16( nwkAddr );

  *pBuf++ = len;
  addrLen = 3;

  pBuf = osal_memcpy( pBuf, UserDescriptor->desc, len );
  osal_memset( pBuf, AF_USER_DESCRIPTOR_FILL, AF_MAX_USER_DESCRIPTOR_LEN-len );

  return fillAndSend( &ZDP_TransID, dstAddr, User_Desc_set, (AF_MAX_USER_DESCRIPTOR_LEN + addrLen) );
}

afStatus_t ZDP_ServerDiscReq( uint16 serverMask, byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  zAddrType_t dstAddr;

  dstAddr.addrMode = AddrBroadcast;
  dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVRXON;

  *pBuf++ = LO_UINT16( serverMask );
  *pBuf = HI_UINT16( serverMask );

  FillAndSendTxOptions( &ZDP_TransID, &dstAddr, Server_Discovery_req, 2,
             ((SecurityEnable) ? AF_EN_SECURITY : AF_TX_OPTIONS_NONE) );
}

afStatus_t ZDP_DeviceAnnce( uint16 nwkAddr, uint8 *IEEEAddr,
                              byte capabilities, byte SecurityEnable )
{
  zAddrType_t dstAddr;
  uint8 len;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  dstAddr.addrMode = (afAddrMode_t)AddrBroadcast;
  dstAddr.addr.shortAddr = NWK_BROADCAST_SHORTADDR_DEVRXON;

  ZDP_TmpBuf[0] = LO_UINT16( nwkAddr );
  ZDP_TmpBuf[1] = HI_UINT16( nwkAddr );
  osal_cpyExtAddr( &ZDP_TmpBuf[2], IEEEAddr );
  len = 2 + Z_EXTADDR_LEN;

  ZDP_TmpBuf[10] = capabilities;
  len++;

  return fillAndSend( &ZDP_TransID, &dstAddr, Device_annce, len );
}

void zdpProcessAddrReq( zdoIncomingMsg_t *inMsg )
{
  associated_devices_t *pAssoc;
  uint8 reqType;
  uint16 aoi = INVALID_NODE_ADDR;
  uint8 *ieee = NULL;

  reqType = inMsg->asdu[(inMsg->clusterID == NWK_addr_req) ? Z_EXTADDR_LEN : sizeof( uint16 ) ];

  if ( inMsg->clusterID == NWK_addr_req )

  {
    ieee = inMsg->asdu;

    if ( osal_ExtAddrEqual( saveExtAddr, ieee ) )
    {
      aoi = ZDAppNwkAddr.addr.shortAddr;
    }
    // Handle response for sleeping end devices
    else if ( (ZSTACK_ROUTER_BUILD)
      && (((pAssoc = AssocGetWithExt( ieee )) != NULL)
             && (pAssoc->nodeRelation == CHILD_RFD)) )
    {
      aoi = pAssoc->shortAddr;
      if ( reqType != ZDP_ADDR_REQTYPE_SINGLE )
        reqType = 0xFF; // Force Invalid
    }
  }
  else  // if ( inMsg->clusterID == IEEE_addr_req )
  {
    aoi = BUILD_UINT16( inMsg->asdu[0], inMsg->asdu[1] );

    if ( aoi == ZDAppNwkAddr.addr.shortAddr )
    {
      ieee = saveExtAddr;
    }
    else if ( (ZSTACK_ROUTER_BUILD)
      && (((pAssoc = AssocGetWithShort( aoi )) != NULL)
             && (pAssoc->nodeRelation == CHILD_RFD)) )
    {
      AddrMgrEntry_t addrEntry;
      addrEntry.user = ADDRMGR_USER_DEFAULT;
      addrEntry.index = pAssoc->addrIdx;
      if ( AddrMgrEntryGet( &addrEntry ) )
      {
        ieee = addrEntry.extAddr;
      }

      if ( reqType != ZDP_ADDR_REQTYPE_SINGLE )
        reqType = 0xFF; // Force Invalid
    }
  }

  if ( ((aoi != INVALID_NODE_ADDR) && (ieee != NULL)) || (inMsg->wasBroadcast == FALSE) )
  {
    uint8 stat;
    uint8 *pBuf = ZDP_TmpBuf;
    // Status + IEEE-Addr + Nwk-Addr.
    uint8 len = 1 + Z_EXTADDR_LEN + 2;
    
    // If aoi and iee are both setup, we found results
    if ( (aoi != INVALID_NODE_ADDR) && (ieee != NULL) )
    {
      stat = ((reqType == ZDP_ADDR_REQTYPE_SINGLE) || (reqType == ZDP_ADDR_REQTYPE_EXTENDED)) 
                    ? ZDP_SUCCESS : ZDP_INVALID_REQTYPE;
    }
    else 
    {
      // not found and the req was unicast to this device
      stat = ZDP_DEVICE_NOT_FOUND;
      
      // Fill in the missing field with this device's address
      if ( inMsg->clusterID == NWK_addr_req )
      {
        aoi = ZDAppNwkAddr.addr.shortAddr;
      }
      else
      {
        ieee = saveExtAddr;
      }
    }

    *pBuf++ = stat;

    pBuf = osal_cpyExtAddr( pBuf, ieee );

    *pBuf++ = LO_UINT16( aoi );
    *pBuf++ = HI_UINT16( aoi );

    if ( ZSTACK_ROUTER_BUILD )
    {
      if ( (reqType == ZDP_ADDR_REQTYPE_EXTENDED) && (aoi == ZDAppNwkAddr.addr.shortAddr)
           && (stat == ZDP_SUCCESS) )
      {
        uint8  cnt = 0;
        uint16 *list = AssocMakeList( &cnt );

        if ( list != NULL )
        {
          byte idx = inMsg->asdu[(((inMsg->clusterID == NWK_addr_req) ? Z_EXTADDR_LEN : sizeof( uint16 )) + 1)];
          uint16 *pList = list + idx;

          // NumAssocDev field is only present on success.
          if ( cnt > idx )
          {
            cnt -= idx;
            len += (cnt * sizeof( uint16 ));
          }
          else
          {
            cnt = 0;
          }
          *pBuf++ = cnt;
          len++;

          // StartIndex field is only present if NumAssocDev field is non-zero.
          *pBuf++ = idx;
          len++;

          while ( cnt != 0 )
          {
            *pBuf++ = LO_UINT16( *pList );
            *pBuf++ = HI_UINT16( *pList );
            pList++;
            cnt--;
          }

          osal_mem_free( (uint8 *)list );
        }
        else
        {
          // NumAssocDev field is only present on success.
          *pBuf++ = 0;
          len++;
        }
      }
    }

    ZDP_TxOptions = AF_MSG_ACK_REQUEST;
    fillAndSend( &(inMsg->TransSeq), &(inMsg->srcAddr), (cId_t)(inMsg->clusterID | ZDO_RESPONSE_BIT), len );
    ZDP_TxOptions = AF_TX_OPTIONS_NONE;
  }
}

afStatus_t ZDP_NodeDescMsg( zdoIncomingMsg_t *inMsg,
                           uint16 nwkAddr, NodeDescriptorFormat_t *pNodeDesc )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len;

  len = 1 + 2 + 13;  // Status + nwkAddr + Node descriptor

  *pBuf++ = ZDP_SUCCESS;

  *pBuf++ = LO_UINT16( nwkAddr );
  *pBuf++ = HI_UINT16( nwkAddr );

  *pBuf++ = (byte)((pNodeDesc->ComplexDescAvail << 3) |
                     (pNodeDesc->UserDescAvail << 4) |
                     (pNodeDesc->LogicalType & 0x07));

  *pBuf++ = (byte)((pNodeDesc->FrequencyBand << 3) | (pNodeDesc->APSFlags & 0x07));
  *pBuf++ = pNodeDesc->CapabilityFlags;
  *pBuf++ = pNodeDesc->ManufacturerCode[0];
  *pBuf++ = pNodeDesc->ManufacturerCode[1];
  *pBuf++ = pNodeDesc->MaxBufferSize;
  *pBuf++ = pNodeDesc->MaxInTransferSize[0];
  *pBuf++ = pNodeDesc->MaxInTransferSize[1];

  *pBuf++ = LO_UINT16( pNodeDesc->ServerMask );
  *pBuf++ = HI_UINT16( pNodeDesc->ServerMask );
  *pBuf++ = pNodeDesc->MaxOutTransferSize[0];
  *pBuf++ = pNodeDesc->MaxOutTransferSize[1];
  *pBuf = pNodeDesc->DescriptorCapability;

  return fillAndSend( &(inMsg->TransSeq), &(inMsg->srcAddr), Node_Desc_rsp, len );
}

afStatus_t ZDP_PowerDescMsg( zdoIncomingMsg_t *inMsg,
                     uint16 nwkAddr, NodePowerDescriptorFormat_t *pPowerDesc )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = 1 + 2 + 2;  // Status + nwkAddr + Node Power descriptor.

  *pBuf++ = ZDP_SUCCESS;

  *pBuf++ = LO_UINT16( nwkAddr );
  *pBuf++ = HI_UINT16( nwkAddr );

  *pBuf++ = (byte)((pPowerDesc->AvailablePowerSources << 4)
                    | (pPowerDesc->PowerMode & 0x0F));
  *pBuf++ = (byte)((pPowerDesc->CurrentPowerSourceLevel << 4)
                    | (pPowerDesc->CurrentPowerSource & 0x0F));

  return fillAndSend( &(inMsg->TransSeq), &(inMsg->srcAddr), Power_Desc_rsp, len );
}

afStatus_t ZDP_SimpleDescMsg( zdoIncomingMsg_t *inMsg, byte Status,
                              SimpleDescriptionFormat_t *pSimpleDesc )
{
  uint8 *pBuf = ZDP_TmpBuf;
  uint8 i, len;

  if ( Status == ZDP_SUCCESS && pSimpleDesc )
  {
    // Status + NWKAddrOfInterest + desc length + empty simple descriptor.
    len = 1 + 2 + 1 + 8;
    len += (pSimpleDesc->AppNumInClusters + pSimpleDesc->AppNumOutClusters) * sizeof ( uint16 );
  }
  else
  {
    len = 1 + 2 + 1; // Status + desc length
  }
  if ( len >= ZDP_BUF_SZ-1 )
  {
    return afStatus_MEM_FAIL;
  }

  *pBuf++ = Status;

  *pBuf++ = LO_UINT16( ZDAppNwkAddr.addr.shortAddr );
  *pBuf++ = HI_UINT16( ZDAppNwkAddr.addr.shortAddr );

  if ( len > 4 )
  {
    *pBuf++ = len - 4;   // Simple descriptor length

    *pBuf++ = pSimpleDesc->EndPoint;
    *pBuf++ = LO_UINT16( pSimpleDesc->AppProfId );
    *pBuf++ = HI_UINT16( pSimpleDesc->AppProfId );
    *pBuf++ = LO_UINT16( pSimpleDesc->AppDeviceId );
    *pBuf++ = HI_UINT16( pSimpleDesc->AppDeviceId );

    *pBuf++ = (byte)(pSimpleDesc->AppDevVer << 4);

    *pBuf++ = pSimpleDesc->AppNumInClusters;
    if ( pSimpleDesc->AppNumInClusters )
    {
      for (i=0; i<pSimpleDesc->AppNumInClusters; ++i)
      {
        *pBuf++ = LO_UINT16( pSimpleDesc->pAppInClusterList[i] );
        *pBuf++ = HI_UINT16( pSimpleDesc->pAppInClusterList[i] );
      }
    }

    *pBuf++ = pSimpleDesc->AppNumOutClusters;
    if ( pSimpleDesc->AppNumOutClusters )
    {
      for (i=0; i<pSimpleDesc->AppNumOutClusters; ++i)
      {
        *pBuf++ = LO_UINT16( pSimpleDesc->pAppOutClusterList[i] );
        *pBuf++ = HI_UINT16( pSimpleDesc->pAppOutClusterList[i] );
      }
    }
  }

  else
  {
    *pBuf = 0; // Description Length = 0;
  }

  return fillAndSend( &(inMsg->TransSeq), &(inMsg->srcAddr), Simple_Desc_rsp, len );
}

afStatus_t ZDP_EPRsp( uint16 MsgType, byte TransSeq, zAddrType_t *dstAddr,
                        byte Status, uint16 nwkAddr, byte Count,
                        uint8 *pEPList,
                        byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = 1 + 2 + 1;  // Status + nwkAddr + endpoint/interface count.
  byte txOptions;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  if ( MsgType == Match_Desc_rsp )
    txOptions = AF_MSG_ACK_REQUEST;
  else
    txOptions = 0;

    *pBuf++ = Status;
  *pBuf++ = LO_UINT16( nwkAddr );
  *pBuf++ = HI_UINT16( nwkAddr );

  *pBuf++ = Count;   // Endpoint/Interface count

  if ( Count )
  {
    len += Count;
    osal_memcpy( pBuf, pEPList, Count );
  }

  FillAndSendTxOptions( &TransSeq, dstAddr, MsgType, len, txOptions );
}

ZStatus_t ZDP_UserDescRsp( byte TransSeq, zAddrType_t *dstAddr,
                uint16 nwkAddrOfInterest, UserDescriptorFormat_t *userDesc,
                byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = 1 + 2 + 1;  // Status + nwkAddr + descriptor length.

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  len += userDesc->len;

  *pBuf++ = ZSUCCESS;

  *pBuf++ = LO_UINT16( nwkAddrOfInterest );
  *pBuf++ = HI_UINT16( nwkAddrOfInterest );

  *pBuf++ = userDesc->len;
  osal_memcpy( pBuf, userDesc->desc, userDesc->len );

  return (ZStatus_t)fillAndSend( &TransSeq, dstAddr, User_Desc_rsp, len );
}

ZStatus_t ZDP_ServerDiscRsp( byte transID, zAddrType_t *dstAddr, byte status,
                           uint16 aoi, uint16 serverMask, byte SecurityEnable )
{
  const byte len = 1  + 2;  // status + aoi + mask.
  uint8 *pBuf = ZDP_TmpBuf;
  ZStatus_t stat;

  // Intentionally unreferenced parameters
  (void)aoi;
  (void)SecurityEnable;

  *pBuf++ = status;

  *pBuf++ = LO_UINT16( serverMask );
  *pBuf++ = HI_UINT16( serverMask );

  ZDP_TxOptions = AF_MSG_ACK_REQUEST;
  stat = fillAndSend( &transID, dstAddr, Server_Discovery_rsp, len );
  ZDP_TxOptions = AF_TX_OPTIONS_NONE;

  return ( stat );
}

afStatus_t ZDP_GenericRsp( byte TransSeq, zAddrType_t *dstAddr,
                     byte status, uint16 aoi, uint16 rspID, byte SecurityEnable )
{
  uint8 len;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  ZDP_TmpBuf[0] = status;
  ZDP_TmpBuf[1] = LO_UINT16( aoi );
  ZDP_TmpBuf[2] = HI_UINT16( aoi );

  // Length byte
  ZDP_TmpBuf[3] = 0;
  len = 4;

  return fillAndSend( &TransSeq, dstAddr, rspID, len );
}

afStatus_t ZDP_EndDeviceBindReq( zAddrType_t *dstAddr,
                                 uint16 LocalCoordinator,
                                 byte endPoint,
                                 uint16 ProfileID,
                                 byte NumInClusters, cId_t *InClusterList,
                                 byte NumOutClusters, cId_t *OutClusterList,
                                 byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  uint8 i, len;
  uint8 *ieeeAddr;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  // LocalCoordinator + SrcExtAddr + ep + ProfileID +  NumInClusters + NumOutClusters.
  len = 2 + Z_EXTADDR_LEN + 1 + 2 + 1 + 1;
  len += (NumInClusters + NumOutClusters) * sizeof ( uint16 );

  if ( len >= ZDP_BUF_SZ-1 )
  {
    return afStatus_MEM_FAIL;
  }

  if ( LocalCoordinator != NLME_GetShortAddr() )
  {
    return afStatus_INVALID_PARAMETER;
  }

  *pBuf++ = LO_UINT16( LocalCoordinator );
  *pBuf++ = HI_UINT16( LocalCoordinator );

  ieeeAddr = NLME_GetExtAddr();
  pBuf = osal_cpyExtAddr( pBuf, ieeeAddr );

  *pBuf++ = endPoint;

  *pBuf++ = LO_UINT16( ProfileID );   // Profile ID
  *pBuf++ = HI_UINT16( ProfileID );

  *pBuf++ = NumInClusters; // Input cluster list
  for ( i = 0; i < NumInClusters; ++i )
  {
    *pBuf++ = LO_UINT16(InClusterList[i]);
    *pBuf++ = HI_UINT16(InClusterList[i]);
  }

  *pBuf++ = NumOutClusters; // Output cluster list
  for ( i = 0; i < NumOutClusters; ++i )
  {
    *pBuf++ = LO_UINT16(OutClusterList[i]);
    *pBuf++ = HI_UINT16(OutClusterList[i]);
  }

  return fillAndSend( &ZDP_TransID, dstAddr, End_Device_Bind_req, len );
}

afStatus_t ZDP_BindUnbindReq( uint16 BindOrUnbind, zAddrType_t *dstAddr,
                              uint8 *SourceAddr, byte SrcEndPoint,
                              cId_t ClusterID,
                              zAddrType_t *destinationAddr, byte DstEndPoint,
                              byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  // SourceAddr + SrcEPIntf + ClusterID +  addrMode.
  len = Z_EXTADDR_LEN + 1 + sizeof( cId_t ) + sizeof( uint8 );
  if ( destinationAddr->addrMode == Addr64Bit )
    len += Z_EXTADDR_LEN + 1;     // +1 for DstEPIntf
  else if ( destinationAddr->addrMode == AddrGroup )
    len += sizeof ( uint16 );

  pBuf = osal_cpyExtAddr( pBuf, SourceAddr );
  *pBuf++ = SrcEndPoint;

  *pBuf++ = LO_UINT16( ClusterID );

  *pBuf++ = HI_UINT16( ClusterID );
  *pBuf++ = destinationAddr->addrMode;
  if ( destinationAddr->addrMode == Addr64Bit )
  {
    pBuf = osal_cpyExtAddr( pBuf, destinationAddr->addr.extAddr );
    *pBuf = DstEndPoint;
  }
  else if ( destinationAddr->addrMode == AddrGroup )
  {
    *pBuf++ = LO_UINT16( destinationAddr->addr.shortAddr );
    *pBuf++ = HI_UINT16( destinationAddr->addr.shortAddr );
  }

  FillAndSendTxOptions( &ZDP_TransID, dstAddr, BindOrUnbind, len, AF_MSG_ACK_REQUEST );
}

afStatus_t ZDP_MgmtNwkDiscReq( zAddrType_t *dstAddr,
                               uint32 ScanChannels,
                               byte ScanDuration,
                               byte StartIndex,
                               byte SecurityEnable )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = sizeof( uint32 )+1+1;  // ScanChannels + ScanDuration + StartIndex.

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  pBuf = osal_buffer_uint32( pBuf, ScanChannels );

  *pBuf++ = ScanDuration;
  *pBuf = StartIndex;

  return fillAndSend( &ZDP_TransID, dstAddr, Mgmt_NWK_Disc_req, len );
}

afStatus_t ZDP_MgmtDirectJoinReq( zAddrType_t *dstAddr,
                               uint8 *deviceAddr,
                               byte capInfo,
                               byte SecurityEnable )
{
  (void)SecurityEnable;  // Intentionally unreferenced parameter

  osal_cpyExtAddr( ZDP_TmpBuf, deviceAddr );
  ZDP_TmpBuf[Z_EXTADDR_LEN] = capInfo;

  return fillAndSend( &ZDP_TransID, dstAddr, Mgmt_Direct_Join_req, (Z_EXTADDR_LEN + 1) );
}

afStatus_t ZDP_MgmtPermitJoinReq( zAddrType_t *dstAddr, byte duration,
                                  byte TcSignificance, byte SecurityEnable )
{
  (void)SecurityEnable;  // Intentionally unreferenced parameter

  // Build buffer
  ZDP_TmpBuf[ZDP_MGMT_PERMIT_JOIN_REQ_DURATION] = duration;
  ZDP_TmpBuf[ZDP_MGMT_PERMIT_JOIN_REQ_TC_SIG]   = TcSignificance;

  // Send the message
  return fillAndSend( &ZDP_TransID, dstAddr, Mgmt_Permit_Join_req,
                      ZDP_MGMT_PERMIT_JOIN_REQ_SIZE );
}

afStatus_t ZDP_MgmtLeaveReq( zAddrType_t *dstAddr, uint8 *IEEEAddr, uint8 RemoveChildren,
                 uint8 Rejoin, uint8 SecurityEnable )

{
  (void)SecurityEnable;  // Intentionally unreferenced parameter

  osal_cpyExtAddr( ZDP_TmpBuf, IEEEAddr );
  ZDP_TmpBuf[Z_EXTADDR_LEN] = 0;

  if ( RemoveChildren == TRUE )
  {
    ZDP_TmpBuf[Z_EXTADDR_LEN] |= ZDP_MGMT_LEAVE_REQ_RC;
  }
  if ( Rejoin == TRUE )
  {
    ZDP_TmpBuf[Z_EXTADDR_LEN] |= ZDP_MGMT_LEAVE_REQ_REJOIN;
  }

  return fillAndSend( &ZDP_TransID, dstAddr, Mgmt_Leave_req, (Z_EXTADDR_LEN + 1) );
}

afStatus_t ZDP_MgmtNwkUpdateReq( zAddrType_t *dstAddr,
                                 uint32 ChannelMask,
                                 uint8 ScanDuration,
                                 uint8 ScanCount,
                                 uint8 NwkUpdateId,
                                 uint16 NwkManagerAddr )
{
  uint8 *pBuf = ZDP_TmpBuf;
  byte len = sizeof( uint32 ) + 1;  // ChannelMask + ScanDuration

  pBuf = osal_buffer_uint32( pBuf, ChannelMask );

  *pBuf++ = ScanDuration;

  if ( ScanDuration <= 0x05 )
  {
    // Request is to scan over channelMask
    len += sizeof( uint8 );

    *pBuf++ = ScanCount;
  }
  else if ( ( ScanDuration == 0xFE ) || ( ScanDuration == 0xFF ) )
  {
    // Request is to change Channel (0xFE) or apsChannelMask and NwkManagerAddr (0xFF)
    len += sizeof( uint8 );

    *pBuf++ = NwkUpdateId;

    if ( ScanDuration == 0xFF )
    {
      len += sizeof( uint16 );

      *pBuf++  = LO_UINT16( NwkManagerAddr );
      *pBuf++  = HI_UINT16( NwkManagerAddr );
    }
  }

  return fillAndSend( &ZDP_TransID, dstAddr, Mgmt_NWK_Update_req, len );
}

afStatus_t ZDP_MgmtNwkDiscRsp( byte TransSeq, zAddrType_t *dstAddr,
                            byte Status,
                            byte NetworkCount,
                            byte StartIndex,
                            byte NetworkListCount,
                            networkDesc_t *NetworkList,
                            byte SecurityEnable )
{
  uint8 *buf;
  uint8 *pBuf;
  byte len = 1+1+1+1;  // Status + NetworkCount + StartIndex + NetworkCountList.
  byte idx;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  len += (NetworkListCount * ( ZDP_NETWORK_EXTENDED_DISCRIPTOR_SIZE - 2 ));

  buf = osal_mem_alloc( len+1 );
  if ( buf == NULL )
  {
    return afStatus_MEM_FAIL;
  }

  pBuf = buf+1;

  *pBuf++ = Status;
  *pBuf++ = NetworkCount;
  *pBuf++ = StartIndex;
  *pBuf++ = NetworkListCount;

  for ( idx = 0; idx < NetworkListCount; idx++ )
  {
    osal_cpyExtAddr( pBuf, NetworkList->extendedPANID);
    pBuf += Z_EXTADDR_LEN;

    *pBuf++  = NetworkList->logicalChannel;                // LogicalChannel
    *pBuf    = NetworkList->stackProfile;                  // Stack profile
    *pBuf++ |= (byte)(NetworkList->version << 4);          // ZigBee Version
    *pBuf    = BEACON_ORDER_NO_BEACONS;                    // Beacon Order
    *pBuf++ |= (uint8)(BEACON_ORDER_NO_BEACONS << 4);      // Superframe Order

    if ( NetworkList->chosenRouter != INVALID_NODE_ADDR )
    {
      *pBuf++ = TRUE;                         // Permit Joining
    }
    else
    {
      *pBuf++ = FALSE;
    }

    NetworkList = NetworkList->nextDesc;    // Move to next list entry
  }

  FillAndSendBuffer( &TransSeq, dstAddr, Mgmt_NWK_Disc_rsp, len, buf );
}

ZStatus_t ZDP_MgmtLqiRsp( byte TransSeq, zAddrType_t *dstAddr,
                          byte Status,
                          byte NeighborLqiEntries,
                          byte StartIndex,
                          byte NeighborLqiCount,
                          ZDP_MgmtLqiItem_t* NeighborList,
                          byte SecurityEnable )
{
  ZDP_MgmtLqiItem_t* list = NeighborList;
  uint8 *buf, *pBuf;
  byte len, x;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  if ( ZSuccess != Status )
  {
    ZDP_TmpBuf[0] = Status;
    return fillAndSend( &TransSeq, dstAddr, Mgmt_Lqi_rsp, 1 );
  }

  // (Status + NeighborLqiEntries + StartIndex + NeighborLqiCount) +
  //  neighbor LQI data.
  len = (1 + 1 + 1 + 1) + (NeighborLqiCount * ZDP_MGMTLQI_EXTENDED_SIZE);

  buf = osal_mem_alloc( len+1 );
  if ( buf == NULL )
  {
    return afStatus_MEM_FAIL;
  }

  pBuf = buf+1;

  *pBuf++ = Status;
  *pBuf++ = NeighborLqiEntries;
  *pBuf++ = StartIndex;
  *pBuf++ = NeighborLqiCount;

  for ( x = 0; x < NeighborLqiCount; x++ )
  {
    osal_cpyExtAddr( pBuf, list->extPanID);         // Extended PanID
    pBuf += Z_EXTADDR_LEN;

    // EXTADDR
    pBuf = osal_cpyExtAddr( pBuf, list->extAddr );

    // NWKADDR
    *pBuf++ = LO_UINT16( list->nwkAddr );
    *pBuf++ = HI_UINT16( list->nwkAddr );

    // DEVICETYPE
    *pBuf = list->devType;

    // RXONIDLE
    *pBuf |= (uint8)(list->rxOnIdle << 2);

    // RELATIONSHIP
    *pBuf++ |= (uint8)(list->relation << 4);

    // PERMITJOINING
    *pBuf++ = (uint8)(list->permit);

    // DEPTH
    *pBuf++ = list->depth;

    // LQI
    *pBuf++ = list->lqi;

    list++; // next list entry
  }

  FillAndSendBuffer( &TransSeq, dstAddr, Mgmt_Lqi_rsp, len, buf );
}

ZStatus_t ZDP_MgmtRtgRsp( byte TransSeq, zAddrType_t *dstAddr,
                            byte Status,
                            byte RoutingTableEntries,
                            byte StartIndex,
                            byte RoutingListCount,
                            rtgItem_t *RoutingTableList,
                            byte SecurityEnable )
{
  uint8 *buf;
  uint8 *pBuf;
  // Status + RoutingTableEntries + StartIndex + RoutingListCount.
  byte len = 1 + 1 + 1 + 1;
  byte x;

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  // Add an array for Routing List data
  len += (RoutingListCount * ZDP_ROUTINGENTRY_SIZE);

  buf = osal_mem_alloc( (short)(len+1) );
  if ( buf == NULL )
  {
    return afStatus_MEM_FAIL;
  }

  pBuf = buf+1;

  *pBuf++ = Status;
  *pBuf++ = RoutingTableEntries;
  *pBuf++ = StartIndex;
  *pBuf++ = RoutingListCount;

  for ( x = 0; x < RoutingListCount; x++ )
  {
    *pBuf++ = LO_UINT16( RoutingTableList->dstAddress );  // Destination Address
    *pBuf++ = HI_UINT16( RoutingTableList->dstAddress );
    *pBuf++ = RoutingTableList->status;
    *pBuf++ = LO_UINT16( RoutingTableList->nextHopAddress );  // Next hop
    *pBuf++ = HI_UINT16( RoutingTableList->nextHopAddress );
    RoutingTableList++;    // Move to next list entry
  }

  FillAndSendBuffer( &TransSeq, dstAddr, Mgmt_Rtg_rsp, len, buf );
}

ZStatus_t ZDP_MgmtBindRsp( byte TransSeq, zAddrType_t *dstAddr,
                            byte Status,
                            byte BindingTableEntries,
                            byte StartIndex,
                            byte BindingTableListCount,
                            apsBindingItem_t *BindingTableList,
                            byte SecurityEnable )
{
  uint8 *buf;
  uint8 *pBuf;
  uint8 maxLen; // maxLen is the maximum packet length to allocate enough memory space
  uint8 len;    // Actual length varies due to different addrMode
  uint8 x;
  byte extZdpBindEntrySize = ZDP_BINDINGENTRY_SIZE + 1 + 1; // One more byte for cluserID and DstAddrMode
  byte shortZdpBindEntrySize = ZDP_BINDINGENTRY_SIZE + 1 + 1 + 2 - 8 - 1; // clusterID + DstAddrMode + shortAddr - ExtAddr - DstEndpoint

  (void)SecurityEnable;  // Intentionally unreferenced parameter

  // Status + BindingTableEntries + StartIndex + BindingTableListCount.
  maxLen = 1 + 1 + 1 + 1;
  maxLen += (BindingTableListCount * extZdpBindEntrySize );  //max length
  buf = osal_mem_alloc( maxLen + 1 );  // +1 for transaction ID

  if ( buf == NULL )
  {
    return afStatus_MEM_FAIL;
  }

  pBuf = buf+1;

  *pBuf++ = Status;
  *pBuf++ = BindingTableEntries;
  *pBuf++ = StartIndex;
  *pBuf++ = BindingTableListCount;

  // Initial length = Status + BindingTableEntries + StartIndex + BindingTableListCount.
  // length += ZDP_BINDINGENTRY_SIZE   -- Version 1.0
  //           extZdpBindEntrySize     -- Version 1.1 extended address mode
  //           shortZdpBindEntrySize   -- Version 1.1 group address mode

  len = 1 + 1 + 1 + 1;
  for ( x = 0; x < BindingTableListCount; x++ )
  {
    pBuf = osal_cpyExtAddr( pBuf, BindingTableList->srcAddr );
    *pBuf++ = BindingTableList->srcEP;

    // Cluster ID
    *pBuf++ = LO_UINT16( BindingTableList->clusterID );
    *pBuf++ = HI_UINT16( BindingTableList->clusterID );

    *pBuf++ = BindingTableList->dstAddr.addrMode;
    if ( BindingTableList->dstAddr.addrMode == Addr64Bit )
    {
      len += extZdpBindEntrySize;
      pBuf = osal_cpyExtAddr( pBuf, BindingTableList->dstAddr.addr.extAddr );
      *pBuf++ = BindingTableList->dstEP;
    }
    else
    {
      len += shortZdpBindEntrySize;
      *pBuf++ = LO_UINT16( BindingTableList->dstAddr.addr.shortAddr );
      *pBuf++ = HI_UINT16( BindingTableList->dstAddr.addr.shortAddr );
    }
    BindingTableList++;    // Move to next list entry
  }

  FillAndSendBuffer( &TransSeq, dstAddr, Mgmt_Bind_rsp, len, buf );
}

afStatus_t ZDP_MgmtNwkUpdateNotify( uint8 TransSeq, zAddrType_t *dstAddr,
                                    uint8 status, uint32 scannedChannels,
                                    uint16 totalTransmissions, uint16 transmissionFailures,
                                    uint8 listCount, uint8 *energyValues, uint8 txOptions,
                                    uint8 securityEnable )
{
  uint8 *buf;
  uint8 *pBuf;
  uint8 len;

  (void)securityEnable;  // Intentionally unreferenced parameter

  // Status + ScannedChannels + totalTransmissions + transmissionFailures + ListCount + energyValues
  len = 1 + 4 + 2 + 2 + 1 + listCount;

  buf = osal_mem_alloc( len+1 ); // +1 for transaction ID
  if ( buf == NULL )
  {
    return afStatus_MEM_FAIL;
  }

  pBuf = buf+1;

  *pBuf++ = status;

  pBuf = osal_buffer_uint32( pBuf, scannedChannels );

  *pBuf++ = LO_UINT16( totalTransmissions );
  *pBuf++ = HI_UINT16( totalTransmissions );

  *pBuf++ = LO_UINT16( transmissionFailures );
  *pBuf++ = HI_UINT16( transmissionFailures );

  *pBuf++ = listCount;

  if ( listCount > 0 )
    osal_memcpy( pBuf, energyValues, listCount );

  FillAndSendBufferTxOptions( &TransSeq, dstAddr, Mgmt_NWK_Update_notify, len, buf, txOptions );
}

ZStatus_t ZDO_RegisterForZDOMsg( uint8 taskID, uint16 clusterID )
{
  ZDO_MsgCB_t *pList;
  ZDO_MsgCB_t *pLast;
  ZDO_MsgCB_t *pNew;

  // Look for duplicate
  pList = pLast = zdoMsgCBs;
  while ( pList )
  {
    if ( pList->taskID == taskID && pList->clusterID == clusterID )
      return ( ZSuccess );
    pLast = pList;
    pList = (ZDO_MsgCB_t *)pList->next;
  }

  // Add to the list
  pNew = (ZDO_MsgCB_t *)osal_mem_alloc( sizeof ( ZDO_MsgCB_t ) );
  if ( pNew )
  {
    pNew->taskID = taskID;
    pNew->clusterID = clusterID;
    pNew->next = NULL;
    if ( zdoMsgCBs )
    {
      pLast->next = pNew;
    }
    else
      zdoMsgCBs = pNew;
    return ( ZSuccess );
  }
  else
    return ( ZMemError );
}

ZStatus_t ZDO_RemoveRegisteredCB( uint8 taskID, uint16 clusterID )
{
  ZDO_MsgCB_t *pList;
  ZDO_MsgCB_t *pLast = NULL;

  pList = zdoMsgCBs;
  while ( pList )
  {
    if ( pList->taskID == taskID && pList->clusterID == clusterID )
    {
      if ( pLast )
      {
        // remove this one from the linked list
        pLast->next = pList->next;
      }
      else if ( pList->next )
      {
        // remove the first one from the linked list
        zdoMsgCBs = pList->next;
      }
      else
      {
        // remove the only item from the list
        zdoMsgCBs = (ZDO_MsgCB_t *)NULL;
      }
      osal_mem_free( pList );
      return ( ZSuccess );
    }
    pLast = pList;
    pList = pList->next;
  }

  return ( ZFailure );
}

uint8 ZDO_SendMsgCBs( zdoIncomingMsg_t *inMsg )
{
  uint8 ret = FALSE;
  ZDO_MsgCB_t *pList = zdoMsgCBs;
  while ( pList )
  {
    if ( pList->clusterID == inMsg->clusterID )
    {
      zdoIncomingMsg_t *msgPtr;

      // Send the address to the task
      msgPtr = (zdoIncomingMsg_t *)osal_msg_allocate( sizeof( zdoIncomingMsg_t ) + inMsg->asduLen );
      if ( msgPtr )
      {
        // copy struct
        osal_memcpy( msgPtr, inMsg, sizeof( zdoIncomingMsg_t ));

        if ( inMsg->asduLen )
        {
          msgPtr->asdu = (byte*)(((byte*)msgPtr) + sizeof( zdoIncomingMsg_t ));
          osal_memcpy( msgPtr->asdu, inMsg->asdu, inMsg->asduLen );
        }

        msgPtr->hdr.event = ZDO_CB_MSG;
        osal_msg_send( pList->taskID, (uint8 *)msgPtr );
        ret = TRUE;
      }
    }
    pList = (ZDO_MsgCB_t *)pList->next;
  }
  return ( ret );
}

void ZDP_IncomingData( afIncomingMSGPacket_t *pData )
{
  uint8 x = 0;
  uint8 handled;
  zdoIncomingMsg_t inMsg;

  inMsg.srcAddr.addrMode = Addr16Bit;
  inMsg.srcAddr.addr.shortAddr = pData->srcAddr.addr.shortAddr;
  inMsg.wasBroadcast = pData->wasBroadcast;
  inMsg.clusterID = pData->clusterId;
  inMsg.SecurityUse = pData->SecurityUse;

  inMsg.asduLen = pData->cmd.DataLength-1;
  inMsg.asdu = pData->cmd.Data+1;
  inMsg.TransSeq = pData->cmd.Data[0];
  inMsg.macDestAddr = pData->macDestAddr;

  handled = ZDO_SendMsgCBs( &inMsg );

#if (defined MT_ZDO_CB_FUNC)
#if !defined MT_TASK
  if (zgZdoDirectCB)
#endif
  {
    MT_ZdoDirectCB( pData, &inMsg );
  }
#endif

  while ( zdpMsgProcs[x].clusterID != 0xFFFF )
  {
    if ( zdpMsgProcs[x].clusterID == inMsg.clusterID )
    {
      zdpMsgProcs[x].pFn( &inMsg );
      return;
    }
    x++;
  }

  // Handle unhandled messages
  if ( !handled )
    ZDApp_InMsgCB( &inMsg );
}

ZDProfile.h

#ifndef ZDPROFILE_H
#define ZDPROFILE_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "ZComDef.h"
#include "NLMEDE.h"
#include "AF.h"
#include "ZDConfig.h"


#define ZDO_EP 0   // Endpoint of ZDO
#define ZDO_PROFILE_ID 0

// IEEE_addr_req request types
#define ZDP_ADDR_REQTYPE_SINGLE     0
#define ZDP_ADDR_REQTYPE_EXTENDED   1

// ZDO Status Values
#define ZDP_SUCCESS            0x00  // Operation completed successfully
#define ZDP_INVALID_REQTYPE    0x80  // The supplied request type was invalid
#define ZDP_DEVICE_NOT_FOUND   0x81  // Reserved
#define ZDP_INVALID_EP         0x82  // Invalid endpoint value
#define ZDP_NOT_ACTIVE         0x83  // Endpoint not described by a simple desc.
#define ZDP_NOT_SUPPORTED      0x84  // Optional feature not supported
#define ZDP_TIMEOUT            0x85  // Operation has timed out
#define ZDP_NO_MATCH           0x86  // No match for end device bind
#define ZDP_NO_ENTRY           0x88  // Unbind request failed, no entry
#define ZDP_NO_DESCRIPTOR      0x89  // Child descriptor not available
#define ZDP_INSUFFICIENT_SPACE 0x8a  // Insufficient space to support operation
#define ZDP_NOT_PERMITTED      0x8b  // Not in proper state to support operation
#define ZDP_TABLE_FULL         0x8c  // No table space to support operation
#define ZDP_NOT_AUTHORIZED     0x8d  // Permissions indicate request not authorized
#define ZDP_BINDING_TABLE_FULL 0x8e  // No binding table space to support operation

#define ZDP_NETWORK_DISCRIPTOR_SIZE  8
#define ZDP_NETWORK_EXTENDED_DISCRIPTOR_SIZE  14
#define ZDP_RTG_DISCRIPTOR_SIZE      5
#define ZDP_BIND_DISCRIPTOR_SIZE  19

// Mgmt_Permit_Join_req fields
#define ZDP_MGMT_PERMIT_JOIN_REQ_DURATION 0
#define ZDP_MGMT_PERMIT_JOIN_REQ_TC_SIG   1
#define ZDP_MGMT_PERMIT_JOIN_REQ_SIZE     2

// Mgmt_Leave_req fields
#define ZDP_MGMT_LEAVE_REQ_REJOIN      1 << 7
#define ZDP_MGMT_LEAVE_REQ_RC          1 << 6   // Remove Children

// Mgmt_Lqi_rsp - (neighbor table) device type
#define ZDP_MGMT_DT_COORD  0x0
#define ZDP_MGMT_DT_ROUTER 0x1
#define ZDP_MGMT_DT_ENDDEV 0x2

// Mgmt_Lqi_rsp - (neighbor table) relationship
#define ZDP_MGMT_REL_PARENT  0x0
#define ZDP_MGMT_REL_CHILD   0x1
#define ZDP_MGMT_REL_SIBLING 0x2
#define ZDP_MGMT_REL_UNKNOWN 0x3

// Mgmt_Lqi_rsp - (neighbor table) unknown boolean value
#define ZDP_MGMT_BOOL_UNKNOWN 0x02

// ZDO Cluster IDs
#define ZDO_RESPONSE_BIT_V1_0   ((uint8)0x80)
#define ZDO_RESPONSE_BIT        ((uint16)0x8000)

#define NWK_addr_req            ((uint16)0x0000)
#define IEEE_addr_req           ((uint16)0x0001)
#define Node_Desc_req           ((uint16)0x0002)
#define Power_Desc_req          ((uint16)0x0003)
#define Simple_Desc_req         ((uint16)0x0004)
#define Active_EP_req           ((uint16)0x0005)
#define Match_Desc_req          ((uint16)0x0006)
#define NWK_addr_rsp            (NWK_addr_req | ZDO_RESPONSE_BIT)
#define IEEE_addr_rsp           (IEEE_addr_req | ZDO_RESPONSE_BIT)
#define Node_Desc_rsp           (Node_Desc_req | ZDO_RESPONSE_BIT)
#define Power_Desc_rsp          (Power_Desc_req | ZDO_RESPONSE_BIT)
#define Simple_Desc_rsp         (Simple_Desc_req | ZDO_RESPONSE_BIT)
#define Active_EP_rsp           (Active_EP_req | ZDO_RESPONSE_BIT)
#define Match_Desc_rsp          (Match_Desc_req | ZDO_RESPONSE_BIT)

#define Complex_Desc_req        ((uint16)0x0010)
#define User_Desc_req           ((uint16)0x0011)
#define Discovery_Cache_req     ((uint16)0x0012)
#define Device_annce            ((uint16)0x0013)
#define User_Desc_set           ((uint16)0x0014)
#define Server_Discovery_req    ((uint16)0x0015)
#define Complex_Desc_rsp        (Complex_Desc_req | ZDO_RESPONSE_BIT)
#define User_Desc_rsp           (User_Desc_req | ZDO_RESPONSE_BIT)
#define Discovery_Cache_rsp     (Discovery_Cache_req | ZDO_RESPONSE_BIT)
#define User_Desc_conf          (User_Desc_set | ZDO_RESPONSE_BIT)
#define Server_Discovery_rsp    (Server_Discovery_req | ZDO_RESPONSE_BIT)

#define End_Device_Bind_req     ((uint16)0x0020)
#define Bind_req                ((uint16)0x0021)
#define Unbind_req              ((uint16)0x0022)
#define Bind_rsp                (Bind_req | ZDO_RESPONSE_BIT)
#define End_Device_Bind_rsp     (End_Device_Bind_req | ZDO_RESPONSE_BIT)
#define Unbind_rsp              (Unbind_req | ZDO_RESPONSE_BIT)

#define Mgmt_NWK_Disc_req       ((uint16)0x0030)
#define Mgmt_Lqi_req            ((uint16)0x0031)
#define Mgmt_Rtg_req            ((uint16)0x0032)
#define Mgmt_Bind_req           ((uint16)0x0033)
#define Mgmt_Leave_req          ((uint16)0x0034)
#define Mgmt_Direct_Join_req    ((uint16)0x0035)
#define Mgmt_Permit_Join_req    ((uint16)0x0036)
#define Mgmt_NWK_Update_req     ((uint16)0x0038)
#define Mgmt_NWK_Disc_rsp       (Mgmt_NWK_Disc_req | ZDO_RESPONSE_BIT)
#define Mgmt_Lqi_rsp            (Mgmt_Lqi_req | ZDO_RESPONSE_BIT)
#define Mgmt_Rtg_rsp            (Mgmt_Rtg_req | ZDO_RESPONSE_BIT)
#define Mgmt_Bind_rsp           (Mgmt_Bind_req | ZDO_RESPONSE_BIT)
#define Mgmt_Leave_rsp          (Mgmt_Leave_req | ZDO_RESPONSE_BIT)
#define Mgmt_Direct_Join_rsp    (Mgmt_Direct_Join_req | ZDO_RESPONSE_BIT)
#define Mgmt_Permit_Join_rsp    (Mgmt_Permit_Join_req | ZDO_RESPONSE_BIT)
#define Mgmt_NWK_Update_notify  (Mgmt_NWK_Update_req | ZDO_RESPONSE_BIT)

#define ZDP_BINDINGENTRY_SIZE   19

typedef struct
{
  osal_event_hdr_t hdr;
  zAddrType_t      srcAddr;
  uint8            wasBroadcast;
  cId_t            clusterID;
  uint8            SecurityUse;
  uint8            TransSeq;
  uint8            asduLen;
  uint16           macDestAddr;
  uint8            *asdu;
} zdoIncomingMsg_t;

// This structure is used to build the Mgmt Network Discover Response
typedef struct
{
  uint8 extendedPANID[Z_EXTADDR_LEN];   // The extended PAN ID
  uint16 PANId;            // The network PAN ID
  uint8   logicalChannel;  // Network's channel
  uint8   stackProfile;    // Network's profile
  uint8   version;         // Network's Zigbee version
  uint8   beaconOrder;     // Beacon Order
  uint8   superFrameOrder;
  uint8   permitJoining;   // PermitJoining. 1 or 0
} mgmtNwkDiscItem_t;

// This structure is used to build the Mgmt LQI Response
typedef struct
{
  uint16 nwkAddr;         // device's short address
  uint16 PANId;           // The neighbor device's PAN ID
  uint8  extPANId[Z_EXTADDR_LEN]; // The neighbor device's Extended PanID
  uint8   txQuality;       // Transmit quality
  uint8   rxLqi;           // Receive LQI
} neighborLqiItem_t;
#define ZDP_NEIGHBORLQI_SIZE    12

// This structure is used to build the Mgmt_Lqi_rsp
typedef struct
{
  uint16 panID;                  // PAN Id
  uint8  extPanID[Z_EXTADDR_LEN];// Extended Pan ID
  uint8  extAddr[Z_EXTADDR_LEN]; // Extended address
  uint16 nwkAddr;                // Network address
  uint8  devType;                // Device type
  uint8  rxOnIdle;               // RxOnWhenIdle
  uint8  relation;               // Relationship
  uint8  permit;                 // Permit joining
  uint8  depth;                  // Depth
  uint8  lqi;                    // LQI
} ZDP_MgmtLqiItem_t;
// devType, rxOnIdle, relation, are all packed into 1 byte: 18-2=16.
#define ZDP_MGMTLQI_SIZE           15
#define ZDP_MGMTLQI_EXTENDED_SIZE  22   // One extra byte for permitJointing, also with extended PanID instead of PanID 15+8-2+1 = 22

// This structure is used to build the Mgmt Routing Response
// NOTICE: this structure must match "rtgEntry_t" in rtg.h
typedef struct
{
  uint16 dstAddress;     // Destination short address
  uint16 nextHopAddress; // next hop short address
  uint8  expiryTime;     // expiration time - not used for response
  uint8  status;         // route entry status
  uint8  options;
} rtgItem_t;
// expiryTime is not packed & sent OTA.
#define ZDP_ROUTINGENTRY_SIZE   5

typedef struct
{
  uint8  TransSeq;
  byte SecurityUse;
  uint16 srcAddr;
  uint16 localCoordinator;
  uint8  ieeeAddr[Z_EXTADDR_LEN];
  uint8  endpoint;
  uint16 profileID;
  uint8  numInClusters;
  uint16 *inClusters;
  uint8  numOutClusters;
  uint16 *outClusters;
} ZDEndDeviceBind_t;

extern byte ZDP_TransID;
extern byte ZDP_TxOptions;

extern afStatus_t ZDP_SendData( uint8 *transSeq, zAddrType_t *dstAddr, uint16 cmd, byte len,
                                              uint8 *buf, byte SecurityEnable );


#define ZDP_NodeDescReq( dstAddr, NWKAddrOfInterest, SecurityEnable ) \
                          ZDP_NWKAddrOfInterestReq(  dstAddr, \
                              NWKAddrOfInterest, Node_Desc_req, SecurityEnable )


#define ZDP_PowerDescReq( dstAddr, NWKAddrOfInterest, SecurityEnable ) \
                          ZDP_NWKAddrOfInterestReq(  dstAddr, \
                              NWKAddrOfInterest, Power_Desc_req, SecurityEnable )

#define ZDP_ActiveEPReq( dstAddr, NWKAddrOfInterest, SecurityEnable ) \
                          ZDP_NWKAddrOfInterestReq(  dstAddr, \
                            NWKAddrOfInterest, Active_EP_req, SecurityEnable )

#define ZDP_ComplexDescReq( dstAddr, NWKAddrOfInterest, SecurityEnable ) \
                          ZDP_NWKAddrOfInterestReq(  dstAddr, \
                            NWKAddrOfInterest, Complex_Desc_req, SecurityEnable )

#define ZDP_UserDescReq( dstAddr, NWKAddrOfInterest, SecurityEnable ) \
                          ZDP_NWKAddrOfInterestReq(  dstAddr, \
                            NWKAddrOfInterest, User_Desc_req, SecurityEnable )

#define ZDP_BindReq( dstAddr, SourceAddr, SrcEP, \
              ClusterID, DestinationAddr, DstEP, SecurityEnable ) \
                       ZDP_BindUnbindReq( Bind_req, dstAddr, \
                            SourceAddr, SrcEP, ClusterID, \
                            DestinationAddr, DstEP, SecurityEnable )

#define ZDP_UnbindReq( dstAddr, SourceAddr, SrcEP, \
              ClusterID, DestinationAddr, DstEP, SecurityEnable ) \
                       ZDP_BindUnbindReq( Unbind_req, dstAddr, \
                            SourceAddr, SrcEP, ClusterID, \
                            DestinationAddr, DstEP, SecurityEnable )

#define ZDP_MgmtLqiReq( dstAddr, StartIndex, SecurityEnable ) \
          ZDP_SendData( &ZDP_TransID, dstAddr, Mgmt_Lqi_req, 1, &StartIndex, SecurityEnable )

#define ZDP_MgmtRtgReq( dstAddr, StartIndex, SecurityEnable ) \
          ZDP_SendData( &ZDP_TransID, dstAddr, Mgmt_Rtg_req, 1, &StartIndex, SecurityEnable )

#define ZDP_MgmtBindReq( dstAddr, StartIndex, SecurityEnable ) \
         ZDP_SendData( &ZDP_TransID, dstAddr, Mgmt_Bind_req, 1, &StartIndex, SecurityEnable )

#ifdef REMOVE_BY_LAF

#define ZDP_NWKAddrRsp( TransSeq, dstAddr, Status, IEEEAddrRemoteDev, ReqType, nwkAddr, NumAssocDev, \
                 StartIndex, NWKAddrAssocDevList, SecurityEnable ) \
                          ZDP_AddrRsp( NWK_addr_rsp, TransSeq, dstAddr, Status, \
                                IEEEAddrRemoteDev, ReqType, nwkAddr, NumAssocDev, StartIndex, \
                                NWKAddrAssocDevList, SecurityEnable )

#define ZDP_IEEEAddrRsp( TransSeq, dstAddr, Status, IEEEAddrRemoteDev, ReqType, nwkAddr, NumAssocDev, \
                 StartIndex, NWKAddrAssocDevList, SecurityEnable ) \
                          ZDP_AddrRsp( IEEE_addr_rsp, TransSeq, dstAddr, Status, \
                                IEEEAddrRemoteDev, ReqType, nwkAddr, NumAssocDev, StartIndex, \
                                NWKAddrAssocDevList, SecurityEnable )
#endif

#define ZDP_ActiveEPRsp( TransSeq, dstAddr, Status, nwkAddr, Count, \
                  pEPList, SecurityEnable ) \
                      ZDP_EPRsp( Active_EP_rsp, TransSeq, dstAddr, Status, \
                           nwkAddr, Count, pEPList, SecurityEnable )

#define ZDP_MatchDescRsp( TransSeq, dstAddr, Status, nwkAddr, Count, \
                  pEPList, SecurityEnable ) \
                      ZDP_EPRsp( Match_Desc_rsp, TransSeq, dstAddr, Status, \
                           nwkAddr, Count, pEPList, SecurityEnable )

#define ZDP_ComplexDescRsp( dstAddr, SecurityEnable ) \
        ZDP_GenericRsp( dstAddr, Complex_Desc_rsp, SecurityEnable )


#define ZDP_UserDescConf( TransSeq, dstAddr, Status, SecurityEnable ) \
            ZDP_SendData( &TransSeq, dstAddr, User_Desc_conf, 1, &Status, SecurityEnable )

#define ZDP_EndDeviceBindRsp( TransSeq, dstAddr, Status, SecurityEnable ) \
       ZDP_SendData( &TransSeq, dstAddr, End_Device_Bind_rsp, 1, &Status, SecurityEnable )

#define ZDP_BindRsp( TransSeq, dstAddr, Status, SecurityEnable ) \
                  ZDP_SendData( &TransSeq, dstAddr, Bind_rsp, 1, &Status, SecurityEnable )


#define ZDP_UnbindRsp( TransSeq, dstAddr, Status, SecurityEnable ) \
                ZDP_SendData( &TransSeq, dstAddr, Unbind_rsp, 1, &Status, SecurityEnable )


#define ZDP_MgmtLeaveRsp( TransSeq, dstAddr, Status, SecurityEnable ) \
            ZDP_SendData( &TransSeq, dstAddr, Mgmt_Leave_rsp, 1, &Status, SecurityEnable )

#define ZDP_MgmtPermitJoinRsp( TransSeq, dstAddr, Status, SecurityEnable ) \
      ZDP_SendData( &TransSeq, dstAddr, Mgmt_Permit_Join_rsp, 1, &Status, SecurityEnable )

#define ZDP_MgmtDirectJoinRsp( TransSeq, dstAddr, Status, SecurityEnable ) \
      ZDP_SendData( &TransSeq, dstAddr, Mgmt_Direct_Join_rsp, 1, &Status, SecurityEnable )

extern afStatus_t ZDP_NWKAddrOfInterestReq( zAddrType_t *dstAddr,
                              uint16 nwkAddr, byte cmd, byte SecurityEnable );

extern afStatus_t ZDP_NwkAddrReq( uint8 *IEEEAddress, byte ReqType,
                               byte StartIndex, byte SecurityEnable );

extern afStatus_t ZDP_IEEEAddrReq( uint16 shortAddr, byte ReqType,
                                byte StartIndex, byte SecurityEnable );

extern afStatus_t ZDP_MatchDescReq( zAddrType_t *dstAddr, uint16 nwkAddr,
                                uint16 ProfileID,
                                byte NumInClusters, uint16 *InClusterList,
                                byte NumOutClusters, uint16 *OutClusterList,
                                byte SecurityEnable );

extern afStatus_t ZDP_SimpleDescReq( zAddrType_t *dstAddr, uint16 nwkAddr,
                                    byte ep, byte SecurityEnable );


extern afStatus_t ZDP_UserDescSet( zAddrType_t *dstAddr, uint16 nwkAddr,
                          UserDescriptorFormat_t *UserDescriptor,
                          byte SecurityEnable );

afStatus_t ZDP_ServerDiscReq( uint16 serverMask, byte SecurityEnable );

extern afStatus_t ZDP_DeviceAnnce( uint16 nwkAddr, uint8 *IEEEAddr,
                         byte capabilities, byte SecurityEnable );

extern afStatus_t ZDP_EndDeviceBindReq( zAddrType_t *dstAddr,
                              uint16 LocalCoordinator,
                              byte ep,
                              uint16 ProfileID,
                              byte NumInClusters, uint16 *InClusterList,
                              byte NumOutClusters, uint16 *OutClusterList,
                              byte SecurityEnable );


extern afStatus_t ZDP_BindUnbindReq( uint16 BindOrUnbind, zAddrType_t *dstAddr,
                            uint8 *SourceAddr, byte SrcEP,
                            cId_t  ClusterID,
                            zAddrType_t *DestinationAddr, byte DstEP,
                            byte SecurityEnable );

extern afStatus_t ZDP_MgmtNwkDiscReq( zAddrType_t *dstAddr,
                            uint32 ScanChannels,
                            byte ScanDuration,
                            byte StartIndex,
                            byte SecurityEnable );


extern afStatus_t ZDP_MgmtDirectJoinReq( zAddrType_t *dstAddr,
                               uint8 *deviceAddr,
                               byte capInfo,
                               byte SecurityEnable );


extern afStatus_t ZDP_MgmtLeaveReq( zAddrType_t *dstAddr,
                                   uint8 *IEEEAddr,
                                   uint8 RemoveChildren,
                                   uint8 Rejoin,
                                   uint8 SecurityEnable );

extern afStatus_t ZDP_MgmtPermitJoinReq( zAddrType_t *dstAddr,
                               byte duration,
                               byte TcSignificance,
                               byte SecurityEnable );

extern afStatus_t ZDP_MgmtNwkUpdateReq( zAddrType_t *dstAddr,
                                        uint32 ChannelMask,
                                        uint8 ScanDuration,
                                        uint8 ScanCount,
                                        uint8 NwkUpdateId,
                                        uint16 NwkManagerAddr );


afStatus_t ZDP_AddrRsp( byte cId, byte seq, zAddrType_t *dst, byte stat,
  uint8 *ieee, byte reqType, uint16 nwkAddr, byte devCnt, byte strtIdx,
  uint16 *devAddr, byte secOpt );

extern afStatus_t ZDP_NodeDescMsg( zdoIncomingMsg_t *inMsg,
                    uint16 nwkAddr, NodeDescriptorFormat_t *pNodeDesc );

extern afStatus_t ZDP_PowerDescMsg( zdoIncomingMsg_t *inMsg,
 uint16 nwkAddr, NodePowerDescriptorFormat_t *pPowerDesc );

extern afStatus_t ZDP_SimpleDescMsg( zdoIncomingMsg_t *inMsg,
                     byte Status, SimpleDescriptionFormat_t *pSimpleDesc );

extern afStatus_t ZDP_EPRsp( uint16 MsgType, byte TransSeq, zAddrType_t *dstAddr, byte Status,
                                uint16 nwkAddr, byte Count, uint8 *pEPList,
                                byte SecurityEnable );

extern afStatus_t ZDP_GenericRsp( byte TransSeq, zAddrType_t *dstAddr,
                    byte status, uint16 aoi, uint16 rspID, byte SecurityEnable );

extern afStatus_t ZDP_MgmtNwkDiscRsp( byte TransSeq, zAddrType_t *dstAddr,
                            byte Status,
                            byte NetworkCount,
                            byte StartIndex,
                            byte NetworkCountList,
                            networkDesc_t *NetworkList,
                            byte SecurityEnable );


extern ZStatus_t ZDP_MgmtLqiRsp( byte TransSeq, zAddrType_t *dstAddr,
                          byte Status,
                          byte NeighborLqiEntries,
                          byte StartIndex,
                          byte NeighborLqiCount,
                          ZDP_MgmtLqiItem_t* NeighborList,
                          byte SecurityEnable );


extern ZStatus_t ZDP_MgmtRtgRsp( byte TransSeq, zAddrType_t *dstAddr,
                            byte Status,
                            byte RoutingTableEntries,
                            byte StartIndex,
                            byte RoutingListCount,
                            rtgItem_t *RoutingTableList,
                            byte SecurityEnable );


extern ZStatus_t ZDP_MgmtBindRsp( byte TransSeq, zAddrType_t *dstAddr,
                            byte Status,
                            byte BindingTableEntries,
                            byte StartIndex,
                            byte BindingTableListCount,
                            apsBindingItem_t *BindingTableList,
                            byte SecurityEnable );

extern afStatus_t ZDP_MgmtNwkUpdateNotify( uint8 TransSeq, zAddrType_t *dstAddr,
                                    uint8 status, uint32 scannedChannels,
                                    uint16 totalTransmissions, uint16 transmissionFailures,
                                    uint8 listCount, uint8 *energyValues, uint8 txOptions,
                                    uint8 securityEnable );

extern ZStatus_t ZDP_UserDescRsp( byte TransSeq, zAddrType_t *dstAddr,
                uint16 nwkAddrOfInterest, UserDescriptorFormat_t *userDesc,
                byte SecurityEnable );


ZStatus_t ZDP_ServerDiscRsp( byte transID, zAddrType_t *dstAddr, byte status,
                           uint16 aoi, uint16 serverMask, byte SecurityEnable );


extern void ZDP_IncomingData( afIncomingMSGPacket_t *pData );

extern ZStatus_t ZDO_RegisterForZDOMsg( uint8 taskID, uint16 clusterID );
extern ZStatus_t ZDO_RemoveRegisteredCB( uint8 taskID, uint16 clusterID );

#ifdef __cplusplus
}
#endif
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不吃橘子的橘猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值