bool zigbee_permit_joining(bool join_permitted) {
    /* Check that the device is operating either as router or coordinator. */
    if ((NWK_FORMED != ZIGBEE_NWK_GET_STATE()) &&
            (NWK_STARTED != ZIGBEE_NWK_GET_STATE())) {
        return false;
    }

    /* Set the Associate Permitted flag in the IEEE 802.15.4 MAC. */
    IEEE802_15_4_SET_ASSOCIATION_PERMITTED(join_permitted);
    return true;
}
bool zigbee_network_discovery_request(nlme_network_discovery_req_t *ndr) {
    /* Verify that the device is in IDLE state. */
    if (NWK_IDLE != ZIGBEE_NWK_GET_STATE()) {
        return false;
    }
    
    /* Perform sanity check on function parameters. */
    if (NULL == ndr) {
        return false;
    }
    
    if (NULL == (ndr->nlme_callback_discovery_confirm)) {
        return false;
    }
    
    /* Store NWK parameters. */
    nwk_param.discovery.nlme_callback_discovery_confirm = ndr->nlme_callback_discovery_confirm;
    nwk_param.discovery.ndc = &(ndr->ndc);
    
    /* Build and send MLME_SCAN.request. */
    mlme_scan_req_t *msr = &(ndr->msr);
    msr->ScanType = MLME_SCAN_TYPE_ACTIVE;
    msr->ScanChannel = ndr->ChannelToScan;
    msr->ScanDuration = ndr->ScanDuration; 
    msr->mlme_scan_confirm = mac_scan_confirm_callback;
    
    /* Set the MLME_BEACON_NOTIFY.indication callback. */
    ieee802_15_4_set_mlme_beacon_notify_indication(mac_beacon_notify_callback);
    
    /* Issue MLME_SCAN.request primitive. */
    bool discovery_status = false;
    if (true != ieee802_15_4_scan_request(msr)) {
    } else {
        ZIGBEE_NWK_SET_STATE(NWK_BUSY_DISCOVERING);
        discovery_status = true;
    }
    
    return discovery_status;
}
bool zigbee_start_router_request(void) {
    /* The device must be joined before it can be started as a router. */
    if (NWK_JOINED != ZIGBEE_NWK_GET_STATE()) { return false; }
    
    /* The device is joined to a coordinator or another router. */
    bool start_router_status = false;
    if (MAC_SUCCESS != ieee802_15_4_start_request(IEEE802_15_4_GET_PAN_ID(), IEEE802_15_4_GET_CHANNEL(), true)) {    
    } else if (MAC_SUCCESS != ieee802_15_4_rx_enable()) {
    } else {
        
        /* Calculate the router's depth in the network and set up the Cskip algorithm. */
        uint8_t depth = zigbee_neighbor_table_find_depth(IEEE802_15_4_GET_COORD_SHORT_ADDRESS(), \
                                                         IEEE802_15_4_GET_SHORT_ADDRESS());
        
        uint16_t c_skip = zigbee_nib_c_skip(depth);
        NWK_NIB_SET_ADDRESS_INCREMENT(c_skip);
         
        ZIGBEE_NWK_SET_STATE(NWK_STARTED);
        NWK_NIB_SET_NODE_ROLE(ZIGBEE_TYPE_COORD);
        start_router_status = true;
    }
    
    return start_router_status; 
}
bool zigbee_join_request(nlme_join_req_t *njr) {
    /* Perform sanity check on parameter set. */
    if (NULL == njr) {
        return false;
    }
    
    if (NULL == (njr->nlme_callback_join_confirm)) {
        return false;
    }
    
    if (NWK_JOIN_THROUGH_ASSOCIATION == (njr->RejoinNetwork)) {
        /* Association. */
        
        /* Verify that the device is in idle mode. */
        if (NWK_IDLE != ZIGBEE_NWK_GET_STATE()) {
            return false;
        }
        
        /* Serach the Neighbor Table for a potential item with the suggested
         * PAN ID.
         */
        bool join_candidate_found = false;
        
        zigbee_neighbor_table_item_t *nb_item = NEIGHBOR_TABLE_GET_FIRST();
        
        while ((true != NEIGHBOR_TABLE_IS_LAST_ELEMENT(nb_item)) && (true != join_candidate_found)) {
            
            if (((nb_item->ndesc.PanID) == (njr->PANId)) && (true == nb_item->ndesc.PermitJoining)) {
                join_candidate_found = true;
                break;
            } else {
                nb_item = NEIGHBOR_TABLE_GET_NEXT(nb_item);
            }
        }
        
        /* Verify that a candidate with the correct PAN ID and that does permit
         * joining was found.
         */
        if (true != join_candidate_found) {
            return false;
        }
        
        /* Build MLME_ASSOCIATE.request. */
        njr->mar.LogicalChannel = nb_item->ndesc.LogicalChannel;
        njr->mar.CoordAddrMode = WPAN_ADDRMODE_SHORT;
        njr->mar.CoordPANId = njr->PANId;
        njr->mar.CoordAddress = 0;
        njr->mar.CoordAddress = nb_item->NetworkAddress;
        
        uint8_t capabilities = 0;                  
        if (true == (njr->JoinAsRouter)) { capabilities |= (1 << 1); }
        if (0x01 == (njr->PowerSource))  { capabilities |= (1 << 2); }
        if (true == (njr->RxOnWhenIdle)) { capabilities |= (1 << 3); }
        
        njr->mar.CapabilityInformation = capabilities;
        
        njr->mar.mlme_associate_confirm = mac_associate_confim_callback;
        
        /* Set some of the internal storage pointers. */
        nwk_param.join.nlme_callback_join_confirm = njr->nlme_callback_join_confirm;
        nwk_param.join.njc = &(njr->njc);
        nwk_param.join.parent_address = njr->mar.CoordAddress;
        
        /* Issue request. */
        if (true != ieee802_15_4_associate_request(&(njr->mar))) {
            return false;
        } else {
            /* Set new NWK state. */
            return true;
        }
    } else if (NWK_JOIN_THROUGH_ORPHANINING == (njr->RejoinNetwork)) {
        /* Orphan Scan. */
        /* Not implemented yet. */
        return false;
    } else if (NWK_JOIN_THROUGH_REJOIN == (njr->RejoinNetwork)) {
        /* NWK Rejoin procedure. */
        /* Not implemented yet. */
        return false;
    } else {
        /* Unknown option. */
        return false;
    }
}