/* * Send out the NWK frame to the MAC layer for transmission. This function * does final assembly of the NWK header and generates the MAC request * based on the given information. */ static void nwk_tx(buffer_t *buf, nwk_hdr_t *hdr, bool indirect) { mac_pib_t *pib = mac_pib_get(); mac_data_req_t req; /* finish filling out the nwk hdr and generate it */ hdr->nwk_frm_ctrl.protocol_ver = ZIGBEE_PROTOCOL_VERSION; hdr->nwk_frm_ctrl.mcast_flag = false; // multicast not supported hdr->nwk_frm_ctrl.security = false; hdr->nwk_frm_ctrl.src_rte = false; hdr->nwk_frm_ctrl.dest_ieee_addr_flag = false; hdr->nwk_frm_ctrl.src_ieee_addr_flag = false; nwk_gen_header(buf, hdr); debug_dump_nwk_hdr(hdr); req.src_addr.mode = SHORT_ADDR; req.dest_addr.mode = SHORT_ADDR; req.src_addr.short_addr = hdr->mac_hdr->src_addr.short_addr; req.dest_addr.short_addr = hdr->mac_hdr->dest_addr.short_addr; req.src_pan_id = pib->pan_id; req.dest_pan_id = pib->pan_id; req.msdu_handle = hdr->handle; req.buf = buf; /* * it its a broadcast, then no ack request. * otherwise, ack requests on all other transfers. */ req.tx_options = (req.dest_addr.short_addr != 0xFFFF) ? MAC_ACK_REQUEST : 0x0; req.tx_options |= indirect ? MAC_INDIRECT_TRANS : 0x0; /* kick it to the curb! */ mac_data_req(&req); }
/* * Check that the frame's dest addr is for us or a broadcast address. Otherwise * every frame sent by the simulator to us will trigger an interrupt. This * function will allow us to discard frames not meant for us. */ static bool drvr_check_addr(buffer_t *buf) { buffer_t *tmp; mac_hdr_t hdr; U8 index; mac_pib_t *pib = mac_pib_get(); BUF_ALLOC(tmp, TX); index = tmp->index; memcpy(tmp, buf, sizeof(buffer_t)); tmp->index = index; mac_parse_hdr(tmp, &hdr); buf_free(tmp); if ((hdr.mac_frm_ctrl.frame_type == MAC_BEACON) || (hdr.mac_frm_ctrl.frame_type == MAC_ACK)) { return true; } else if (hdr.dest_addr.mode == SHORT_ADDR) { if ((hdr.dest_addr.short_addr == pib->short_addr) || (hdr.dest_addr.short_addr == MAC_BROADCAST_ADDR)) return true; } else if (hdr.dest_addr.mode == LONG_ADDR) { if ((hdr.dest_addr.long_addr == pib->ext_addr)) return true; } return false; }
/* Set the PHY channel to one of the 16 channels available for 802.15.4 */ void mac_set_channel(U8 channel) { mac_pib_t *pib = mac_pib_get(); if ((channel >= MAC_PHY_CHANNEL_OFFSET) && (channel <= (MAC_PHY_CHANNEL_OFFSET + MAC_MAX_CHANNELS))) pib->curr_channel = channel; }
/* * This is the function that does the actual transmission of the * frame. It sends the data to the driver which will then send it * over the air. If the channel is busy, then it will back off for * a random delay and then retry the transfer. If it exceeds the * maximum backoffs, it will abort the transmission and send a data * confirm with a failure status. After transmission, if no ack is * needed, then a data confirm will immediately get issued to the * next higher layer that requested the transmission. If an ack is * required, the data confirm won't be sent until a proper ack is received. */ void mac_out(buffer_t *buf, bool ack_req, U8 dsn, U8 handle) { U8 i; U16 csma_time; mac_pib_t *pib = mac_pib_get(); mac_pcb_t *pcb = mac_pcb_get(); for (i = 0; i < aMaxCsmaBackoffs; i++) { /* check if the channel is clear */ if (drvr_get_cca()) { /* send the frame if the CCA clears */ drvr_tx(buf); /* collect a transmission stat here */ pcb->total_xmit++; /* * if there's no ack request, * then we're finished. free the buf. */ if (!ack_req) { mac_data_conf(MAC_SUCCESS, handle); buf_free(buf); } return; } else { /* * channel busy. do a busy wait and try again. * Shift left of signed quantity (int) due to * left shift of constant "1". its okay. */ csma_time = drvr_get_rand() % (U16)((1 << pib->min_be) - 1); busy_wait(csma_time); } } /* * exceeded max csma backoffs. clean up and send a * confirm with a fail message. */ if (ack_req) { /* * if the ack request is set, then we need to * check the retry queue and remove the entry. */ mac_retry_rem(dsn); /* collect a transmit fail stat here */ pcb->total_fail++; } else buf_free(buf); mac_data_conf(MAC_CHANNEL_ACCESS_FAILURE, handle); }
void nwk_permit_join_req(U8 duration) { mac_pib_t *pib = mac_pib_get(); switch (duration) { case NWK_PERMIT_JOIN_DISABLE: pib->assoc_permit = false; break; case NWK_PERMIT_JOIN_ENABLE: pib->assoc_permit = true; break; default: pib->assoc_permit = true; ctimer_set(&permit_join_tmr, duration * CLOCK_SECOND, nwk_permit_join_disable, NULL); break; } }
/* * 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_permit_join_disable() { mac_pib_t *pib = mac_pib_get(); pib->assoc_permit = false; }
void mac_set_short_addr(U16 addr) { mac_pib_t *pib = mac_pib_get(); pib->short_addr = addr; }
void mac_set_pan_id(U16 pan_id) { mac_pib_t *pib = mac_pib_get(); pib->pan_id = pan_id; }
/* * Handle the rx events from the mac process. If the driver * receives a valid frame, it will send an event to the mac * process. The mac process will then call the event handler * which retrieves the frame from the rx queue and parses it. * Once parsed, it will be handled according to the frame type. * If its a command frame, it gets sent to the command handler, * a data frame gets sent to the next higher layer, etc... */ static void mac_eventhandler(process_event_t event) { buffer_t *buf, *buf_out; mac_hdr_t hdr; mac_cmd_t cmd; bool frm_pend; mac_pcb_t *pcb = mac_pcb_get(); mac_pib_t *pib = mac_pib_get(); if (event == event_mac_rx) { DBG_PRINT("MAC_EVENTHANDLER: Rx event occurred.\n"); buf = mac_queue_buf_pop(); if (buf) { DBG_PRINT_RAW("\n<INCOMING>"); debug_dump_buf(buf->dptr, buf->len); /* decode the packet */ mac_parse_hdr(buf, &hdr); debug_dump_mac_hdr(&hdr); /* * check if an ack is needed. if so, then generate * an ack and queue it for transmission. the frm * pending bit will be set for any frame coming from * an address that has an indirect frame for it. this * is against the spec, but it will speed up the ack * transmission. * NOTE: the ack response section may change due to the * tight ack timing requirements. */ if (hdr.mac_frm_ctrl.ack_req) { BUF_ALLOC(buf_out, TX); DBG_PRINT("MAC: ACK Required.\n"); frm_pend = mac_indir_frm_pend(&hdr.src_addr); mac_gen_ack(buf_out, frm_pend, hdr.dsn); mac_out(buf_out, false, hdr.dsn, 0); } /* * process accordingly. if a scan is in progress, * all frames except for beacon frames will be * discarded. */ switch(hdr.mac_frm_ctrl.frame_type) { case MAC_COMMAND: if (pcb->mac_state != MLME_SCAN) { /* * need to handle the case that this is an indirect * transfer, which means that we need to stop the * poll timer and send a status to the poll confirm. */ if ((pcb->mac_state == MLME_DATA_REQ) && (hdr.src_addr.mode == SHORT_ADDR) && (hdr.src_addr.short_addr == pib->coord_addr.short_addr)) { ctimer_stop(&pcb->mlme_tmr); mac_poll_conf(MAC_SUCCESS); } mac_parse_cmd(buf, &cmd); mac_cmd_handler(&cmd, &hdr); } buf_free(buf); break; case MAC_BEACON: /* discard the beacon if we're not doing a scan */ if (pcb->mac_state == MLME_SCAN) { mac_parse_beacon(buf, &hdr); mac_beacon_notify_ind(buf, mac_scan_descr_find_addr(&hdr.src_addr)); } buf_free(buf); break; case MAC_ACK: mac_retry_ack_handler(hdr.dsn); /* * we need to do some special ops depending on the * state we're in if we get an ACK. */ if (pcb->mac_state == MLME_ASSOC_REQ) { if (pcb->assoc_req_dsn == hdr.dsn) ctimer_set(&pcb->mlme_tmr, pib->resp_wait_time, mac_poll_req, NULL); } else if (pcb->mac_state == MLME_DATA_REQ) { if (hdr.mac_frm_ctrl.frame_pending) ctimer_set(&pcb->mlme_tmr, aMacMaxFrameTotalWaitTime, mac_poll_timeout, NULL); } buf_free(buf); break; case MAC_DATA: if (pcb->mac_state != MLME_SCAN) { /* * need to handle the case that this is an indirect * transfer, which means that we need to stop the poll * timer and send a status to the poll confirm. */ if ((pcb->mac_state == MLME_DATA_REQ) && (hdr.src_addr.mode == SHORT_ADDR) && (hdr.src_addr.short_addr == pib->coord_addr.short_addr)) { ctimer_stop(&pcb->mlme_tmr); mac_poll_conf(MAC_SUCCESS); } mac_data_ind(buf, &hdr); } else buf_free(buf); break; default: /* TODO: Add a statistic here to capture an error'd rx */ break; } } /* * there's a possibility that more than one frame is in the * buffer. if they came in before this function gets executed. * So process until the queue is empty. */ if (!mac_queue_is_empty()) { while (process_post(&mac_process, event_mac_rx, NULL) != PROCESS_ERR_OK) { ; } } } }
//lint -e{715} Info 715: Symbol 'ptr' not referenced //lint -e{818} Info 818: Pointer parameter ptr' could be declared as pointing to const void mac_scan(void *ptr) { mac_cmd_t cmd; mac_hdr_t hdr; buffer_t *buf; address_t src_addr, dest_addr; mac_scan_conf_t scan_conf; U32 duration; mac_pcb_t *pcb = mac_pcb_get(); mac_pib_t *pib = mac_pib_get(); // increment the current scan channel on entry into this function. pcb->curr_scan_channel++; // check if we are initializing the scan. if so, then start the init procedure. if (pcb->mac_state != MLME_SCAN) { // on a new scan, first save the original pan id pcb->original_pan_id = pib->pan_id; // then set the pan id to the broadcast pan id. // NOTE: the broadcast addr is same as broadcast pan id pib->pan_id = MAC_BROADCAST_ADDR; // init the curr scan channel to the first one pcb->curr_scan_channel = MAC_PHY_CHANNEL_OFFSET; pcb->mac_state = MLME_SCAN; pcb->nwk_cnt = 0; } // search the channel mask to find the next allowable channel. // if the curr scan channel is not in the mask, then increment the channel and check again. keep doing // it until we either find an allowable channel or we exceed the max channels that are supported. for (;pcb->curr_scan_channel < (MAC_PHY_CHANNEL_OFFSET + MAC_MAX_CHANNELS); pcb->curr_scan_channel++) { //lint -e{701} Info 701: Shift left of signed quantity (int) // shift the bitmask and compare to the channel mask if (pcb->channel_mask & (1 << pcb->curr_scan_channel)) { break; } } // we may get here if the curr scan channel exceeds the max channels. if thats the case, then we will // automatically end the active scan. if (pcb->curr_scan_channel < (MAC_PHY_CHANNEL_OFFSET + MAC_MAX_CHANNELS)) { // set the channel on the radio mac_set_channel(pcb->curr_scan_channel); // generate and send the beacon request // get a free buffer, build the beacon request command, and then // send it using the mac_data_req service. BUF_ALLOC(buf, TX); dest_addr.mode = SHORT_ADDR; dest_addr.short_addr = MAC_BROADCAST_ADDR; if (pcb->scan_type == MAC_ACTIVE_SCAN) { cmd.cmd_id = MAC_BEACON_REQ; src_addr.mode = NO_PAN_ID_ADDR; } else if (pcb->scan_type == MAC_ORPHAN_SCAN) { cmd.cmd_id = MAC_ORPHAN_NOT; src_addr.mode = LONG_ADDR; src_addr.long_addr = pib->ext_addr; } mac_gen_cmd(buf, &cmd); mac_gen_cmd_header(buf, &hdr, false, &src_addr, &dest_addr); mac_tx_handler(buf, &hdr.dest_addr, false, false, hdr.dsn, ZIGBEE_INVALID_HANDLE); // set the callback timer duration = (pcb->scan_type == MAC_ACTIVE_SCAN) ? MAC_SCAN_TIME(pcb->duration) : aMacResponseWaitTime; ctimer_set(&pcb->mlme_tmr, duration, mac_scan, NULL); } else { pcb->mac_state = MLME_IDLE; // Send the nwk scan confirm scan_conf.scan_type = pcb->scan_type; scan_conf.energy_list = NULL; if (pcb->scan_type == MAC_ACTIVE_SCAN) { scan_conf.status = MAC_SUCCESS; } else if (pcb->scan_type == MAC_ORPHAN_SCAN) { scan_conf.status = (pcb->coor_realign_rcvd) ? MAC_SUCCESS : MAC_NO_BEACON; pcb->coor_realign_rcvd = false; } // restore the original pan ID. pib->pan_id = pcb->original_pan_id; mac_scan_conf(&scan_conf); } }