Exemple #1
0
/*
 * 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);
}
Exemple #2
0
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);
}
Exemple #3
0
/*
 * 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);
}
Exemple #4
0
/*
 * 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)
			{
				;
			}
		}
	}
}
Exemple #5
0
//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);
    }
}
Exemple #6
0
U8 *mac_scan_get_energy_list()
{
    mac_pcb_t *pcb = mac_pcb_get();

    return pcb->energy_list;
}