void zb_nwk_mesh_routing_init() { TRACE_MSG(TRACE_NWK1, ">> mesh_r_init", (FMT__0)); /* Check for expired route discovery entries */ TRACE_MSG(TRACE_NWK1, "schd r disc expiry f %d", (FMT__D, ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_route_disc, 0, ZB_NWK_EXPIRY_ROUTE_DISCOVERY))); /* Check for expired pending entries */ TRACE_MSG(TRACE_NWK1, "schd pend expiry f %d", (FMT__D, ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_pending, 0, ZB_NWK_EXPIRY_PENDING))); TRACE_MSG(TRACE_NWK1, "<< mesh_r_init", (FMT__0)); }
/* This function called periodically to find expired pending elements */ void zb_nwk_mesh_expiry_pending(zb_uint8_t param) { ZVUNUSED(param); TRACE_MSG(TRACE_NWK1, ">> exp_pend", (FMT__0)); if ( ZG->nwk.nib.pending_table_cnt ) { zb_ushort_t i; for (i = 0; i < ZB_NWK_PENDING_TABLE_SIZE; i++) { if ( ZG->nwk.nib.pending_table[i].used ) { TRACE_MSG(TRACE_NWK1, "pend ent %d dst_addr %d exp %d", (FMT__D_D_D, i, ZG->nwk.nib.pending_table[i].dest_addr, ZG->nwk.nib.pending_table[i].expiry)); if ( ZG->nwk.nib.pending_table[i].expiry == 0 ) { TRACE_MSG(TRACE_NWK1, "free buf %p", (FMT__P, ZG->nwk.nib.pending_table[i].buf)); zb_free_buf(ZG->nwk.nib.pending_table[i].buf); NWK_ARRAY_PUT_ENT(ZG->nwk.nib.pending_table, &ZG->nwk.nib.pending_table[i], ZG->nwk.nib.pending_table_cnt); } else { ZG->nwk.nib.pending_table[i].expiry--; } } } } /* Schedule to call later */ ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_pending, 0, ZB_NWK_EXPIRY_PENDING); TRACE_MSG(TRACE_NWK1, "<< exp_pend", (FMT__0)); }
zb_ret_t zb_mlme_orphan_scan() { zb_ret_t ret = RET_OK; zb_mlme_scan_params_t *scan_params; /* 7.5.2.1.4 - discard all frames except realignment command frames - switch to the channel - send an orphan notification command - enable receiver for at most macResponseWaitTime symbols and waits for coordinator realignment command - if realignment command received - end, otherwise set next channel */ TRACE_MSG(TRACE_MAC1, ">> mlme_orphan_scan", (FMT__0)); /* clear answer flag */ scan_params = ZB_GET_BUF_PARAM(MAC_CTX().pending_buf, zb_mlme_scan_params_t); MAC_CTX().unscanned_channels = scan_params->channels; MAC_CTX().rt_ctx.orphan_scan.got_realignment = 0; /* TODO: Configiure transiver to accept only realignment commands */ ret = ZB_SCHEDULE_ALARM(zb_mlme_scan_step, 0, ZB_MAC_PIB_RESPONSE_WAIT_TIME); TRACE_MSG(TRACE_MAC1, "<< mlme_orphan_scan %i", (FMT__D, ret)); return ret; }
zb_ret_t zb_mlme_ed_scan() { zb_ret_t ret = RET_OK; zb_mac_scan_confirm_t *scan_confirm; /* mac spec 7.5.2.1.1 ED channel scan - discard all frames received over the PHY data service (UBEC stack accepts only beacon frames) - check one-by-one all logical channels, if it is specified in the requested channel mask, switch to this channel, set phyCurrentChannel = new_channel_number; phyCurrentPage = 0 alwayes for ZB - perform ED measurement for current channel during [(aBaseSuperframeDuration * (2^n + 1)) symbols] time. - save maximum ED value to confirmation buffer - perform scan confirm on procedure finish */ TRACE_MSG(TRACE_MAC1, ">> zb_mlme_ed_scan", (FMT__0)); { zb_mlme_scan_params_t *scan_params = ZB_GET_BUF_PARAM(MAC_CTX().pending_buf, zb_mlme_scan_params_t); MAC_CTX().unscanned_channels = scan_params->channels; /* timeout is calculated in beacon intervals */ MAC_CTX().rt_ctx.ed_scan.scan_timeout = (1l << scan_params->scan_duration) + 1; } ZB_BUF_REUSE(MAC_CTX().pending_buf); scan_confirm = ZB_GET_BUF_PARAM(MAC_CTX().pending_buf, zb_mac_scan_confirm_t); ZB_ASSERT(scan_confirm); ZB_BZERO(scan_confirm, sizeof(zb_mac_scan_confirm_t)); scan_confirm->unscanned_channels = MAC_CTX().unscanned_channels; #ifndef ZB_NS_BUILD MAC_CTX().rt_ctx.ed_scan.channel_number = ZB_MAC_START_CHANNEL_NUMBER; MAC_CTX().rt_ctx.ed_scan.save_channel = MAC_CTX().current_channel; MAC_CTX().rt_ctx.ed_scan.max_rssi_value = 0; ret = ZB_SCHEDULE_ALARM(zb_mlme_scan_step, 0, MAC_CTX().rt_ctx.ed_scan.scan_timeout); #else /* ZB_NS_BUILD */ ZB_BZERO(&scan_confirm->list.energy_detect[0], sizeof(scan_confirm->list.energy_detect)); scan_confirm->result_list_size = ZB_MAC_SUPPORTED_CHANNELS; ret = ZB_SCHEDULE_CALLBACK(zb_mlme_scan_confirm, ZB_REF_FROM_BUF(MAC_CTX().pending_buf)); #ifdef ZB_CHANNEL_ERROR_TEST /* channel interference test, show energy on current channel */ TRACE_MSG(TRACE_MAC3, "ch_err_test %hd, logical_channel %hd, ch index %hd", (FMT__H_H_H, ZB_MAC_GET_CHANNEL_ERROR_TEST(), ZB_MAC_GET_CURRENT_LOGICAL_CHANNEL(), ZB_MAC_GET_CURRENT_LOGICAL_CHANNEL() - ZB_MAC_START_CHANNEL_NUMBER)); if (ZB_MAC_GET_CHANNEL_ERROR_TEST()) { scan_confirm->list.energy_detect[ZB_MAC_GET_CURRENT_LOGICAL_CHANNEL() - ZB_MAC_START_CHANNEL_NUMBER] = ZB_CHANNEL_BUSY_ED_VALUE + 1; } #endif #endif /* ZB_NS_BUILD */ TRACE_MSG(TRACE_MAC1, "<< zb_mlme_ed_scan, ret %i", (FMT__D, ret)); return ret; }
/* This function called periodically to find expired discovery table entries */ void zb_nwk_mesh_expiry_route_disc(zb_uint8_t param) { ZVUNUSED(param); TRACE_MSG(TRACE_NWK1, ">> exp_r_disc", (FMT__0)); if ( ZG->nwk.nib.route_disc_table_cnt ) { zb_ushort_t i; TRACE_MSG(TRACE_NWK1, "disc tbl ent cnt %d", (FMT__D, ZG->nwk.nib.route_disc_table_cnt)); for (i = 0; i < ZB_NWK_ROUTE_DISCOVERY_TABLE_SIZE; i++) { if ( ZG->nwk.nib.route_disc_table[i].used ) { TRACE_MSG(TRACE_NWK1, "ent %d exp_time %d", (FMT__D_D, i, ZG->nwk.nib.route_disc_table[i].expiration_time)); if ( ZG->nwk.nib.route_disc_table[i].expiration_time ) { ZG->nwk.nib.route_disc_table[i].expiration_time--; } else { zb_nwk_routing_t *route_ent; /* free corresponding route record if it's state is not active */ NWK_ARRAY_FIND_ENT(ZG->nwk.nib.routing_table, route_ent, (route_ent->dest_addr == ZG->nwk.nib.route_disc_table[i].dest_addr) && (route_ent->status != ZB_NWK_ROUTE_STATE_ACTIVE) ); /* free expired route discovery entry */ TRACE_MSG(TRACE_NWK1, "free disc ent %d dst_addr %d", (FMT__D_D, i, ZG->nwk.nib.route_disc_table[i].dest_addr)); NWK_ARRAY_PUT_ENT(ZG->nwk.nib.route_disc_table, &ZG->nwk.nib.route_disc_table[i], ZG->nwk.nib.route_disc_table_cnt); if ( route_ent ) { /* report nwk status to the higher layers */ ZG->nwk.handle.status_ind_addr = route_ent->dest_addr; ZB_GET_OUT_BUF_DELAYED(nwk_route_disc_failed); TRACE_MSG(TRACE_NWK1, "free routing entrie, addr %d", (FMT__D, route_ent->dest_addr)); NWK_ARRAY_PUT_ENT(ZG->nwk.nib.routing_table, route_ent, ZG->nwk.nib.routing_table_cnt); goto done; } } } } } /* Schedule to call later */ ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_route_disc, 0, ZB_NWK_EXPIRY_ROUTE_DISCOVERY); done: TRACE_MSG(TRACE_NWK1, "<< exp_r_disc", (FMT__0)); }
void zb_zdo_reschedule_poll_parent(zb_uint16_t timeout) { /* reschedule alarm only if we are not waiting for the poll confirm and * really need polls */ if (!(MAC_PIB().mac_rx_on_when_idle || ZDO_CTX().inside_poll)) { ZB_SCHEDULE_ALARM_CANCEL(zb_zdo_poll_parent, 0); /* If FFD have some pending data for us, we schedule poll w/o timeout */ ZB_SCHEDULE_ALARM(zb_zdo_poll_parent, 0, ZB_MAC_GET_PENDING_DATA()? 1 : timeout); } }
void nwk_route_disc_failed(zb_uint8_t param) { zb_nlme_status_indication_t *status = ZB_GET_BUF_PARAM(ZB_BUF_FROM_REF(param), zb_nlme_status_indication_t); TRACE_MSG(TRACE_NWK1, ">> nwk_route_disc_failed", (FMT__0)); status->status = ZB_NWK_COMMAND_STATUS_NO_ROUTE_AVAILABLE; status->network_addr = ZG->nwk.handle.status_ind_addr; /* notify */ ZB_SCHEDULE_CALLBACK(zb_nlme_status_indication, param); ZB_SCHEDULE_ALARM_CANCEL(zb_nwk_mesh_expiry_route_disc, 0); ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_route_disc, 0, ZB_NWK_EXPIRY_ROUTE_DISCOVERY); TRACE_MSG(TRACE_NWK1, ">> nwk_route_disc_failed", (FMT__0)); }
/* This function called periodically to find expired route requests */ void zb_nwk_mesh_expiry_rreq(zb_uint8_t param) { TRACE_MSG(TRACE_NWK1, ">> expiry_rreq p %hd", (FMT__H, param)); TRACE_MSG(TRACE_NWK1, "rreq cnt %d", (FMT__D, ZG->nwk.nib.rreq_cnt)); if ( ZG->nwk.nib.rreq_cnt ) { zb_ushort_t i = 0; for (i = ZG->nwk.nib.rreq_num; i < ZB_NWK_RREQ_TABLE_SIZE; i++) { if ( ZG->nwk.nib.rreq[i].used ) { /* check we have buffer to send */ if ( param == (zb_uint8_t)(-1) ) { /* wait for buffer */ TRACE_MSG(TRACE_NWK1, "wait f/out buf", (FMT__0)); ZB_GET_OUT_BUF_DELAYED(zb_nwk_mesh_expiry_rreq); goto done; } TRACE_MSG(TRACE_NWK1, "resend %d request", (FMT__D, i)); zb_nwk_mesh_resend_rreq(ZB_BUF_FROM_REF(param), &ZG->nwk.nib.rreq[i]); param = -1; } } ZG->nwk.nib.rreq_num = 0; if ( ZG->nwk.nib.rreq_cnt ) { /* Schedule to call later */ ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_rreq, -1, ZB_NWK_RREQ_RETRY_INTERVAL); } } else { /* No route requests, do not schedule callback */ } done: TRACE_MSG(TRACE_NWK1, "<< expiry_rreq", (FMT__0)); }
void zb_aps_send_command(zb_uint8_t param, zb_uint16_t dest_addr, zb_uint8_t command, zb_bool_t secure) { zb_buf_t *buf = (zb_buf_t *)ZB_BUF_FROM_REF(param); zb_ushort_t need_ack = !ZB_NWK_IS_ADDRESS_BROADCAST(dest_addr); #ifdef ZB_DISABLE_APS_ACK_REQ /* Specially for test with Daintree: do not ask ACK for key transport: Daintree wants to encrypt ACK by its predefined key before it receive key from us */ if (command == APS_CMD_TRANSPORT_KEY) { need_ack = 0; } #endif TRACE_MSG(TRACE_SECUR3, ">>zb_aps_send_command param %hd cmd %hd secur %hd to %d need_ack %hd", (FMT__H_H_H_D_H, param, command, secure, dest_addr, need_ack)); if (need_ack) { zb_uint8_t i; for (i = 0 ; i < ZB_N_APS_RETRANS_ENTRIES ; ++i) { if (ZG->aps.retrans.hash[i].state == ZB_APS_RETRANS_ENT_FREE) { ZG->aps.retrans.hash[i].addr = dest_addr; ZG->aps.retrans.hash[i].aps_counter = ZB_AIB_APS_COUNTER(); ZG->aps.retrans.hash[i].buf = param; ZG->aps.retrans.hash[i].nwk_insecure = !secure; ZG->aps.retrans.hash[i].aps_retries = ZB_N_APS_MAX_FRAME_ENTRIES; ZG->aps.retrans.hash[i].state = ZB_APS_RETRANS_ENT_SENT_MAC_NOT_CONFIRMED_ALRM_RUNNING; TRACE_MSG(TRACE_APS2, "Store buf %hd len %hd in retrans hash %d", (FMT__H_H, param, ZB_BUF_LEN(buf), i)); DUMP_TRAF("sending aps cmd", ZB_BUF_BEGIN(buf), ZB_BUF_LEN(buf)); break; } } if (i == ZB_N_APS_RETRANS_ENTRIES) { TRACE_MSG(TRACE_APS2, "ACK table is FULL", (FMT__0)); } else { ZB_SCHEDULE_ALARM(zb_aps_ack_timer_cb, i, ZB_N_APS_ACK_WAIT_DURATION); } } /* Fill APS command header - see 2.2.5.2.2 APS Command Frame Format. At the same time alloc and fill aux security header */ { zb_aps_command_header_t *hdr; #ifdef ZB_SECURITY #ifdef APS_FRAME_SECURITY buf->u.hdr.encrypt_type = ZB_SECUR_NO_ENCR; if (secure) { /* Allocate here space for APS command header, aux header and command id * (it is in payload). */ secure = zb_aps_command_add_secur(buf, command); hdr = (zb_aps_command_header_t *)ZB_BUF_BEGIN(buf); } else #endif #endif { /* no security - just aps command header */ ZB_BUF_ALLOC_LEFT(buf, sizeof (*hdr), hdr); hdr->fc = 0; hdr->aps_command_id = command; } ZB_APS_FC_SET_COMMAND(hdr->fc, need_ack); hdr->aps_counter = ZB_AIB_APS_COUNTER(); } ZB_AIB_APS_COUNTER_INC(); fill_nldereq(param, dest_addr, secure); TRACE_MSG(TRACE_SECUR3, "send APS cmd %hd secur %hd to %d", (FMT__H_H_D, command, secure, dest_addr)); ZB_SCHEDULE_CALLBACK(zb_nlde_data_request, param); }
/* * Generate and send a route request command frame. * Add route request entry into the rreq list to be able to track this request. */ static zb_ret_t zb_nwk_mesh_send_rreq(zb_buf_t *cbuf, zb_nwk_cmd_rreq_t *nwk_cmd_rreq, zb_uint16_t src_addr, zb_uint8_t seq_num, zb_uint8_t path_cost, zb_uint8_t radius) { zb_ret_t ret = RET_OK; TRACE_MSG(TRACE_NWK1, ">> send_rreq cbuf %p rreq %p s_addr %d path_cost %hd radius %hd", (FMT__P_P_D_H_H, cbuf, nwk_cmd_rreq, src_addr, path_cost, radius)); #if 0 /* check we have room in rreq table */ if ( ZG->nwk.nib.rreq_cnt < ZB_NWK_RREQ_TABLE_SIZE ) #endif { zb_nwk_hdr_t *nwhdr; zb_nwk_cmd_rreq_t *rreq_cmd; #if 0 zb_nwk_rreq_t *rreq; #endif zb_bool_t secure = ZB_FALSE; #ifdef ZB_SECURITY secure = (ZG->aps.authenticated && ZG->nwk.nib.secure_all_frames && ZG->nwk.nib.security_level); #endif nwhdr = nwk_alloc_and_fill_hdr(cbuf, ZB_NWK_BROADCAST_ROUTER_COORDINATOR, NULL, NULL, ZB_FALSE, secure, ZB_TRUE); rreq_cmd = (zb_nwk_cmd_rreq_t *)nwk_alloc_and_fill_cmd(cbuf, ZB_NWK_CMD_ROUTE_REQUEST, sizeof(zb_nwk_cmd_rreq_t)); rreq_cmd->opt = 0; rreq_cmd->rreq_id = nwk_cmd_rreq->rreq_id; rreq_cmd->dest_addr = nwk_cmd_rreq->dest_addr; ZB_NWK_ADDR_TO_LE16(rreq_cmd->dest_addr); rreq_cmd->path_cost = path_cost; nwhdr->radius = radius; nwhdr->src_addr = src_addr; /* Not sure it is right, but let's assign original seq_num. Else request * can be dropped as a dup at receiver's side. */ if (src_addr != ZB_NIB_NETWORK_ADDRESS()) { nwhdr->seq_num = seq_num; } #if 0 /* Save info to retransmit request ZB_MWK_RREQ_RETRIES times */ NWK_ROUTING_ARRAY_GET_ENT(ZG->nwk.nib.rreq, rreq, ZG->nwk.nib.rreq_cnt); ZB_ASSERT(rreq); if ( rreq ) { rreq->originator = src_addr; rreq->radius = radius; rreq->retries = 1; memcpy(&rreq->cmd, rreq_cmd, sizeof(rreq->cmd)); /* schedule resend function */ if ( ZG->nwk.nib.rreq_cnt == 1 ) { ZB_SCHEDULE_ALARM_CANCEL(zb_nwk_mesh_expiry_rreq, ZB_ALARM_ANY_PARAM); ZB_SCHEDULE_ALARM(zb_nwk_mesh_expiry_rreq, -1, ZB_NWK_RREQ_RETRY_INTERVAL); } } #endif /* transmit route request packet */ ZB_SET_BUF_PARAM(cbuf, ZB_NWK_INTERNAL_NSDU_HANDLE, zb_uint8_t); ZB_SCHEDULE_CALLBACK(zb_nwk_forward, ZB_REF_FROM_BUF(cbuf)); } #if 0 else { zb_free_buf(cbuf); TRACE_MSG(TRACE_NWK1, "rreq buffer is full", (FMT__0)); ret = RET_NO_MEMORY; } #endif TRACE_MSG(TRACE_NWK1, "<< send_rreq %d", (FMT__D, ret)); return ret; }
/* this is a universal routine for ed/active/orphan scans */ void zb_mlme_scan_step(zb_uint8_t param) { zb_uint8_t channel_number; zb_mlme_scan_params_t *scan_params; zb_ret_t ret = RET_OK; zb_uint16_t timeout; zb_uint32_t *unscanned_channels; TRACE_MSG(TRACE_MAC1, ">> zb_mlme_scan_step", (FMT__0)); ZVUNUSED(param); channel_number = ZB_MAC_START_CHANNEL_NUMBER; scan_params = ZB_GET_BUF_PARAM(MAC_CTX().pending_buf, zb_mlme_scan_params_t); /* Table 2.80 Fields of the Mgmt_NWK_Disc_req Command (the other scans requests are using the same parameters A value used to calculate the length of time to spend scanning each channel. The time spent scanning each channel is (aBaseSuperframeDuration * (2^n + 1)) symbols, where n is the value of the ScanDuration parameter. */ timeout = (1l << scan_params->scan_duration) + 1; MAC_CTX().mlme_scan_in_progress = 1; unscanned_channels = &MAC_CTX().unscanned_channels; for (;channel_number < ZB_MAC_START_CHANNEL_NUMBER + ZB_MAC_MAX_CHANNEL_NUMBER;channel_number++) { if (*unscanned_channels & 1l<<channel_number) { TRACE_MSG(TRACE_MAC2, "set channel %hd", (FMT__H, channel_number)); ZB_TRANSCEIVER_SET_CHANNEL(channel_number); *unscanned_channels &=~(1l<<channel_number); if (scan_params->scan_type == ACTIVE_SCAN) { ret = zb_beacon_request_command(); if (ret == RET_OK) { /* check beacon request TX status */ /* There's nothing to do during active scan, so, synchronous */ ret = zb_check_cmd_tx_status(); } } ZB_SCHEDULE_ALARM_CANCEL(zb_mlme_scan_step, 0); ret = ZB_SCHEDULE_ALARM(zb_mlme_scan_step, 0, timeout); break; } } if (channel_number == (ZB_MAC_START_CHANNEL_NUMBER + ZB_MAC_MAX_CHANNEL_NUMBER)) { zb_mac_scan_confirm_t *scan_confirm; /* There's no need to restore channel after active or orphan scan, because we will choose new channel, according to scan results. */ /* ZB_TRANSCEIVER_SET_CHANNEL(MAC_CTX().rt_ctx.ed_scan.save_channel);*/ scan_confirm = ZB_GET_BUF_PARAM(MAC_CTX().pending_buf, zb_mac_scan_confirm_t); TRACE_MSG(TRACE_MAC3, "beacon found %hd", (FMT__H, MAC_CTX().rt_ctx.active_scan.beacon_found)); if (scan_params->scan_type == ED_SCAN || MAC_CTX().rt_ctx.active_scan.beacon_found || MAC_CTX().rt_ctx.orphan_scan.got_realignment) { scan_confirm->status = MAC_SUCCESS; /* Q: do we really need to zero here? What about got_realignment? * * A: I think yes, because it is the only indication * for NO_BEACON status, that will not affect ED or ORPHAN scans. ED just * doesn't need any packets, and ORPHAN needs a realignment command that * is processed in appropriate function */ MAC_CTX().rt_ctx.active_scan.beacon_found = 0; } else { scan_confirm->status = MAC_NO_BEACON; } #ifdef ZB_MAC_TESTING_MODE { scan_confirm->result_list_size = desc_count; } #endif scan_confirm->scan_type = scan_params->scan_type; MAC_CTX().mlme_scan_in_progress = 0; ZB_SCHEDULE_CALLBACK(zb_mlme_scan_confirm, ZB_REF_FROM_BUF(MAC_CTX().pending_buf)); } TRACE_MSG(TRACE_MAC1, "<< zb_mlme_scan_step", (FMT__0)); }