/* * 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 mac_scan_energy() { U8 i, curr_ed; mac_scan_conf_t scan_conf; mac_pcb_t *pcb = mac_pcb_get(); // reset the energy list memset(pcb->energy_list, 0, sizeof(pcb->energy_list)); // Check all the channels for the ED scan. First see if its in our allowed // channels list. If it is, then set a timer, set the channel, and then // poll the RSSI until the timer expires. Keep the max value that we get. for (i=0; i<MAC_MAX_CHANNELS; i++) { // check to see if we found a set bit in the channel mask, and also that the bit is between // 11 and 26 pcb->curr_scan_channel = i + MAC_PHY_CHANNEL_OFFSET; //lint -e{701} Info 701: Shift left of signed quantity (int) // this is done on purpose to shift the bitmask to the corresponding channel in the channel mask if (pcb->channel_mask & (1 << pcb->curr_scan_channel)) { // inform everyone that we are currently scanning pcb->mac_state = MLME_SCAN; // set the channel to the current scan channel mac_set_channel(pcb->curr_scan_channel); // enable transceiver in receive mode so we can get ED measurements mac_rx_enb(true, false); // set the timer so that we know when to stop the energy scan timer_set(&pcb->mlme_tmr.etimer.timer, MAC_SCAN_TIME(pcb->duration)); // poll the etimer in a busy wait as we constantly get the rssi values. // keep the max value that we find. while (!timer_expired(&pcb->mlme_tmr.etimer.timer)) { curr_ed = drvr_get_ed(); if (curr_ed > pcb->energy_list[i]) { pcb->energy_list[i] = curr_ed; } } pcb->mac_state = MLME_IDLE; } } // send scan confirm for energy detect scan_conf.scan_type = MAC_ENERGY_SCAN; scan_conf.energy_list = pcb->energy_list; scan_conf.status = MAC_SUCCESS; mac_rx_enb(true, true); // set the trx in auto ack mode mac_scan_conf(&scan_conf); }
/* * This function will start a network discovery operation. It will start an active scan * which records all the networks on the given channels. Once the scan is finished, * it will call mlme_scan_confirm which will then send the results up to the ZDO. */ void nwk_disc_req(U32 channel_mask, U8 duration) { nwk_pcb_t *nwk_pcb = nwk_pcb_get(); mac_pcb_t *mac_pcb = mac_pcb_get(); /* * set the state. we will need this later when we do * the confirm to know what function we are confirming. */ nwk_pcb->nlme_state = NLME_NWK_DISC; /* * need to fill out the mac pcb with the channel mask * and duration because the function uses a callback * timer so the argument needs to be void */ mac_pcb->channel_mask = nwk_pcb->channel_mask = channel_mask; mac_pcb->duration = nwk_pcb->duration = duration; mac_pcb->scan_type = MAC_ACTIVE_SCAN; /* clear the pan descriptor list and do the active scan. */ mac_scan_descr_clear(); mac_scan(NULL); }
/* * 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); } }
U8 *mac_scan_get_energy_list() { mac_pcb_t *pcb = mac_pcb_get(); return pcb->energy_list; }