U16 nwk_rte_tree_calc_cskip(U8 depth) { U16 exp, temp; S16 num, den; U8 i; nwk_nib_t *nib = nwk_nib_get(); exp = nib->max_depth - (depth + 1); if (nib->max_routers == 1) { return (U16)(1 + (nib->max_children * exp)); } else { if (depth < nib->max_depth) { // Init the value of temp to 1. This is the lowest value it can have. // Then calculate the value of (max_routers^exp) temp = 1; for (i=0; i<exp; i++) { temp *= nib->max_routers; } // Need to be careful. There is a potential overflow if the value of max_children is large and temp is also large. num = (S16)((1 + nib->max_children - nib->max_routers) - (nib->max_children * temp)); den = 1 - nib->max_routers; return (U16)(num/den); } } return 0; }
U16 nwk_rte_tree_calc_next_hop(U16 dest) { U16 cskip_parent; U16 dest_addr; bool route_down; nwk_nib_t *nib = nwk_nib_get(); // if cskip is uninitialized, then calculate the value for this device // and save it in the nib. if (nib->cskip == 0xFFFF) { nib->cskip = nwk_rte_tree_calc_cskip(nib->depth); } // we need the depth one layer up to calculate the parent's cskip value. // if we're the coordinator, then we don't care because we will route down // no matter what. otherwise, we use the parent's cskip value to calc // whether to route up or down. cskip_parent = (nib->depth != 0) ? nwk_rte_tree_calc_cskip(nib->depth - 1) : 0; route_down = ((dest > nib->short_addr) && (dest < (nib->short_addr + cskip_parent))); // if we're the coordinator or we calculate that we need to route down, then // look for the child that will be our next hop. otherwise, just send it to // our parent. if ((nib->short_addr == 0) || route_down) { dest_addr = nwk_rte_tree_get_dwnstrm_rtr_addr(dest); } else { dest_addr = nwk_neighbor_tbl_get_parent(); } return dest_addr; }
static U16 nwk_rte_tree_get_dwnstrm_rtr_addr(U16 dest_addr) { U8 i; U16 lower_addr, upper_addr; nwk_nib_t *nib = nwk_nib_get(); address_t addr; addr.mode = SHORT_ADDR; addr.short_addr = dest_addr; // can we get an exact match for the child device? if (nwk_neighbor_tbl_addr_exists(&addr)) { return dest_addr; } // no child exists. find the downstream rtr to send the frame to. // to get the downstream router, we need to find a router whose // address allocation contains the dest_addr. That means that dest_addr must // lie between the upper and lower addresses in the rtr's addr allocation range. for (i=0; i<nib->max_routers; i++) { lower_addr = (U16)((nib->cskip * i) + 1); upper_addr = lower_addr + nib->cskip; if ((dest_addr > lower_addr) && (dest_addr < upper_addr)) { return lower_addr; } } return INVALID_NWK_ADDR; }
/* * Select the network to join. This function is called after a network discovery * and will select the network from the list of scan descriptors. If the * use_ext_pan_id flag is set in the APS Info Base, then it will search for that * pan ID and join that network. * * Otherwise, it looks for the following criteria in evaluating a network to join: * - The scan descriptor sender is a potential parent * - It has capacity * - It is permitting joining * - The protocol version matches this device's version * - The Zigbee stack profile matches this device's profile (ie: Zigbee vs Zigbee Pro) */ static bool zdo_nwk_select() { aps_aib_t *aib = aps_aib_get(); nwk_nib_t *nib = nwk_nib_get(); zdo_pcb_t *pcb = zdo_pcb_get(); nwk_join_req_t args; mem_ptr_t *mem_ptr; bool capacity = false; bool permit_join = false; bool prot_ver_match = false; bool stack_prof_match = false; /* * currently, we will join the first network we find that * fits our join criteria */ for (mem_ptr = pcb->descr_list; mem_ptr != NULL; mem_ptr = mem_ptr->next) { /* * if we specify that we need to join a particular pan, * then we need to find a node descr that matches the * ext pan id. otherwise, if there's no such limitation, * then just go on ahead. */ if ((!aib->use_ext_pan_id) || (aib->use_ext_pan_id == SCAN_ENTRY(mem_ptr)->ext_pan_id)) { if ((aib->use_desig_parent && (aib->desig_parent == SCAN_ENTRY(mem_ptr)->coord_addr.short_addr)) || !aib->use_desig_parent) { capacity = (nib->dev_type == NWK_ROUTER) ? (SCAN_ENTRY(mem_ptr)->rtr_cap >0) : (SCAN_ENTRY(mem_ptr)->end_dev_cap > 0); permit_join = (SCAN_ENTRY(mem_ptr)->superfrm_spec & MAC_ASSOC_PERMIT_MASK) >> MAC_ASSOC_PERMIT_OFF; prot_ver_match = (SCAN_ENTRY(mem_ptr)->prot_ver == ZIGBEE_PROTOCOL_VERSION); stack_prof_match = (SCAN_ENTRY(mem_ptr)->stack_profile == ZIGBEE_STACK_PROFILE); if (SCAN_ENTRY(mem_ptr)->pot_parent && capacity && permit_join && prot_ver_match && stack_prof_match) { args.desc = pcb->curr_descr = mem_ptr; args.ext_pan_id = SCAN_ENTRY(mem_ptr)->ext_pan_id; args.join_as_rtr = (nib->dev_type == NWK_ROUTER); args.rejoin_nwk = (aib->use_ext_pan_id == 0) ? NWK_JOIN_NORMAL : NWK_JOIN_REJOIN; nwk_join_req(&args); return true; } } } }
/* * Start a broadcast transmission. We need to check if one is * currently in progress. If not, then set the flags to indicate * that a broadcast is in progress, and setup the callback timer * to expire the broadcast after the spec'd time interval. */ U8 nwk_brc_start(buffer_t *buf, nwk_hdr_t *hdr) { nwk_pcb_t *pcb = nwk_pcb_get(); nwk_nib_t *nib = nwk_nib_get(); buffer_t *brc_curr_buf; U8 index; /* * if the brc is in the table, then drop it. * otherwise, check for other error conditions * as well that would cause us to drop the broadcast. */ if (!pcb->brc_accept_new || pcb->brc_active || (hdr->radius == 0) || !nwk_brc_check_dev_match(hdr->dest_addr)) { buf_free(buf); return NWK_NOT_PERMITTED; } /* set up the brc fields to indicate the brc status */ BUF_ALLOC(brc_curr_buf, TX); /* * save off the original index of the alloc'd frame. * we'll need to restore it after the memcpy. */ index = brc_curr_buf->index; memcpy(brc_curr_buf, buf, sizeof(buffer_t)); brc_curr_buf->index = index; /* * move the buf data pointer to the correct position * in the buffer and add the len field */ brc_curr_buf->dptr = brc_curr_buf->buf + (buf->dptr - buf->buf); pcb->brc_accept_new = false; pcb->brc_active = true; pcb->brc_seq = hdr->seq_num; pcb->brc_curr_frm = brc_curr_buf; hdr->src_addr = nib->short_addr; memcpy(&pcb->brc_nwk_hdr, hdr, sizeof(nwk_hdr_t)); ctimer_set(&pcb->brc_tmr, NWK_PASSIVE_ACK_TIMEOUT, nwk_brc_expire, NULL); return NWK_SUCCESS; }
void nwk_gen_beacon(buffer_t *buf) { nwk_nib_t *nib = nwk_nib_get(); bool ed_cap, rtr_cap; ed_cap = nib->ed_cap > 0; rtr_cap = nib->rtr_cap > 0; //lint --e{826} Suppress Info 826: Suspicious pointer-to-pointer conversion (area too small) // ex: *(U16 *)buf->dptr = hdr->nwk_fcf // this removes the lint warning for this block only buf->len += NWK_BEACON_PAYLOAD_SIZE; buf->dptr -= NWK_BEACON_PAYLOAD_SIZE; *buf->dptr++ = ZIGBEE_PROTOCOL_ID; *buf->dptr++ = (ZIGBEE_PROTOCOL_VERSION << 4) | (nib->stack_profile & 0x0F); *buf->dptr++ = ((ed_cap << 7) | ((nib->depth & 0xF) << 3) | ((rtr_cap << 2))) & 0xFC; *(U64 *)buf->dptr = nib->ext_pan_ID; buf->dptr += sizeof(U64); buf->dptr -= NWK_BEACON_PAYLOAD_SIZE; }
/* * Generate a network layer beacon payload. A beacon frame contains information * from both the NWK and MAC layer. This function generates the NWK layer specific * information for the beacon. This function gets called by the MAC layer when * its generating a beacon frame in response to a beacon request. * The fields in the NWK portion of the beaon are as follows: * - Protocol ID * - Stack profile * - Protocol version * - Router capacity available * - Device depth * - End device capacity * - Extended PAN ID */ void nwk_gen_beacon(buffer_t *buf) { nwk_nib_t *nib = nwk_nib_get(); bool ed_cap, rtr_cap; ed_cap = nib->ed_cap > 0; rtr_cap = nib->rtr_cap > 0; /* * ex: *(U16 *)buf->dptr = hdr->nwk_fcf * this removes the lint warning for this block only */ buf->len += NWK_BEACON_PAYLOAD_SIZE; buf->dptr -= NWK_BEACON_PAYLOAD_SIZE; *buf->dptr++ = ZIGBEE_PROTOCOL_ID; *buf->dptr++ = (ZIGBEE_PROTOCOL_VERSION << 4) | (nib->stack_profile & 0x0F); *buf->dptr++ = ((ed_cap << 7) | ((nib->depth & 0xF) << 3) | ((rtr_cap << 2))) & 0xFC; *(U64 *)buf->dptr = nib->ext_pan_ID; buf->dptr += sizeof(U64); buf->dptr -= NWK_BEACON_PAYLOAD_SIZE; }
/* * Check if the broadcast address matches the device type. Return true * if it matches. The reason we would need to check the device type is that * Zigbee divides broadcasts up into three types. If we don't match the * broadcast device type, we need to discard the frame. * * Here are the three device types: * - Broadcast to all devices * - Broadcast only to devices that have their receiver on when idle * - Broadcast only to routers and the coordinator */ bool nwk_brc_check_dev_match(U16 dest_addr) { bool resp = false; nwk_nib_t *nib = nwk_nib_get(); mac_pib_t *pib = mac_pib_get(); switch (dest_addr) { case NWK_BROADCAST_ALL: resp = true; break; case NWK_BROADCAST_RXONIDLE: resp = (pib->rx_on_when_idle) ? true : false; break; case NWK_BROADCAST_ROUTERS_COORD: resp = ((nib->dev_type == NWK_COORDINATOR) || (nib->dev_type == NWK_ROUTER)) ? true : false; break; default: break; } return resp; }
void nwk_rte_tree_init() { nwk_nib_t *nib = nwk_nib_get(); nib->cskip = 0xFFFF; }
U16 nwk_rte_tree_calc_ed_addr(U16 cskip, U8 num) { nwk_nib_t *nib = nwk_nib_get(); return (U16)(nib->short_addr + (cskip * nib->max_routers) + (num + 1)); }
U16 nwk_rte_tree_calc_rtr_addr(U16 cskip, U8 num) { nwk_nib_t *nib = nwk_nib_get(); return (U16)(nib->short_addr + (num * cskip) + 1); }
void mac_cmd_handler(mac_cmd_t *cmd, mac_hdr_t *hdr) { buffer_t *buf_out; mac_hdr_t hdr_out; nwk_nib_t *nib = nwk_nib_get(); DBG_PRINT_SIMONLY("\n<INCOMING>"); debug_dump_mac_cmd(cmd); switch (cmd->cmd_id) { case MAC_ASSOC_REQ: if (nib->joined) { DBG_PRINT_SIMONLY("MAC: MAC Association Request Command Received.\n"); // check to make sure that association is permitted and the src address is the correct mode if ((pib.assoc_permit) && (hdr->src_addr.mode == LONG_ADDR)) { mac_assoc_ind_t assoc_args; assoc_args.capability = cmd->assoc_req.cap_info; memcpy(&assoc_args.dev_addr, &hdr->src_addr, sizeof(address_t)); mac_assoc_ind(&assoc_args); } else { mac_assoc_resp_t resp_args; memcpy(&resp_args.dev_addr, &hdr->src_addr, sizeof(address_t)); resp_args.assoc_short_addr = 0xFFFF; resp_args.status = MAC_INVALID_PARAMETER; mac_assoc_resp(&resp_args); } } break; case MAC_ASSOC_RESP: if ((pcb.mac_state == MLME_DATA_REQ) || (pcb.mac_state == MLME_ASSOC_REQ)) { pcb.mac_state = MLME_IDLE; } mac_assoc_conf(cmd->assoc_resp.short_addr, cmd->assoc_resp.assoc_status); break; case MAC_DATA_REQ: if (nib->joined) { DBG_PRINT("MAC: MAC Data Request Command Received.\n"); mac_indir_data_req_handler(&hdr->src_addr); } break; case MAC_BEACON_REQ: if (nib->joined) { DBG_PRINT("MAC: MAC Beacon Request Command Received.\n"); BUF_ALLOC(buf_out, TX); mac_gen_beacon_frm(buf_out, &hdr_out); mac_tx_handler(buf_out, &hdr_out.dest_addr, false, false, hdr_out.dsn, 0); } break; case MAC_ORPHAN_NOT: if (hdr->src_addr.mode == LONG_ADDR) { mac_orphan_ind(hdr->src_addr.long_addr); } break; case MAC_COORD_REALIGN: // check over the coord realign frame to make sure that the data is what we had previously if ((cmd->coord_realign.pan_id == pib.pan_id) && (cmd->coord_realign.short_addr == pib.short_addr)) { if ((pib.coord_addr.mode == SHORT_ADDR) && (cmd->coord_realign.coord_short_addr == pib.coord_addr.short_addr)) { pcb.coor_realign_rcvd = true; } else if ((pib.coord_addr.mode == LONG_ADDR) && (hdr->src_addr.mode == LONG_ADDR) && (hdr->src_addr.long_addr == pib.coord_addr.long_addr)) { pcb.coor_realign_rcvd = true; } else { return; } } else { return; } // stop the scan once we get a valid coord realignment command frame // the scan will end once the channel is beyond the channel range pcb.curr_scan_channel = MAC_PHY_CHANNEL_OFFSET + MAC_MAX_CHANNELS; ctimer_stop(&pcb.mlme_tmr); mac_scan(NULL); break; default: break; } }