/** * @file * @brief Implement the MLME-RX-ENABLE messages. * * $Id: rx_enable.c,v 1.15 2007/05/15 09:02:48 sschneid Exp $ */ /** * \author * Atmel Corporation: http://www.atmel.com * Support email: avr@atmel.com */ /* * Copyright (c) 2006, Atmel Corporation All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The name of ATMEL may not be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY ATMEL ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE EXPRESSLY AND * SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* === Includes ============================================================ */ #include "mac.h" #include "phy.h" #include "timer_const.h" #if APP_TYPE >= APP_L2 || defined(DOXYGEN) /* === Globals ============================================================= */ /* * This variable temporarily remembers the duration for which the receiver * is to be enabled */ static uint32_t RxOnDurationTime; /* * This variable temporarily remembers the time when the receiver is * due to be turned off. */ static uint32_t RxOffTime; /* === Prototypes ========================================================== */ /* === Implementation ====================================================== */ /** * @brief Implement the MLME-RX-ENABLE.request primitive. * * The MLME-RX-ENABLE.request primitive is generated by the next * higher layer and issued to the MLME to enable the receiver for a * fixed duration, at a time relative to the start of the current or * next superframe on a beacon-enabled PAN or immediately on a * nonbeacon-enabled PAN. The receiver is enabled exactly once per * primitive request. * * @param m Pointer to the MLME-RX-ENABLE.request message */ void mlme_rx_enable_request(uint8_t *m) { uint32_t symbols_since_beacon; uint32_t symbols_remaining_in_cap; uint32_t start_time; uint16_t offTime; mlme_rx_enable_req_t *rxe = (mlme_rx_enable_req_t *)m; mlme_rx_enable_conf_t rec; // Reject the request when the MAC is currently in any of the // transient states (scan, CSMA-CA). switch (mac_state) { case MAC_W: case MAC_Ya: case MAC_Za: case MAC_Zp: case MAC_A: case MAC_Tracking_Beacon: /* OK to enable receiver here. */ break; default: // Send the confirm immediately. rec.size = sizeof(mlme_rx_enable_conf_t) - sizeof(rec.size); rec.cmdcode = MLME_RX_ENABLE_CONFIRM; rec.status = MAC_TX_ACTIVE; bios_pushback_event(&rec); return; } // Remember the duration for which the receiver is to be enabled RxOnDurationTime = rxe->RxOnDuration; // Handle rxe->RxOnDuration == 0 and non-beacon enabled PANs because // the PHY has to be awake in both cases. if ( (rxe->RxOnDuration == 0) || (mac_pib_macBeaconOrder == NON_BEACON_NETWORK) ) { // remember the original MAC state mac_original_state = mac_state; // do we need to wake up the radio first? if (mac_radio_sleep_state == RADIO_SLEEPING) { mac_state = MAC_WAKEUP_RX_ENABLE; // wake up radio first mac_phy_wakeup(); } else { mac_awake_rx_enable(); } return; } // Handle beacon enabled PANs /* * Deterimine if (RxOnTime + RxOnDuration) is less than the beacon * interval. mac_RollOverTimeThis holds the BI in symbol time. According * to 7.1.10.1.3: * * On a beacon-enabled PAN, the MLME first determines whether * (RxOnTime + RxOnDuration) is less than the beacon interval, defined * by macBeaconOrder. If it is not less, the MLME issues the * MLME-RX-ENABLE.confirm primitive with a status of INVALID_PARAMETER. */ offTime = rxe->RxOnTime + rxe->RxOnDuration; if (offTime >= mac_RollOverTime) { // Send the confirm immediately. rec.size = sizeof(mlme_rx_enable_conf_t) - sizeof(rec.size); rec.cmdcode = MLME_RX_ENABLE_CONFIRM; rec.status = MAC_INVALID_PARAMETER; bios_pushback_event(&rec); return; } // mac_pib_macBeaconTxTime contains the system (symbol) time when // the last beacon was sent or received. symbols_since_beacon = bios_sub_time(bios_gettime(), mac_pib_macBeaconTxTime); // Actually, MLME-RX-ENABLE.requests in a beacon enabled PAN do // only make sense if this MAC is currently tracking beacons, so // that mac_pib_macBeaconTxTime is up to date. If it appears that // the last known mac_pib_macBeaconTxTime does not relate to the // current superframe, reject the request. if (symbols_since_beacon > mac_RollOverTime) { // Send the confirm immediately. rec.size = sizeof(mlme_rx_enable_conf_t) - sizeof(rec.size); rec.cmdcode = MLME_RX_ENABLE_CONFIRM; rec.status = MAC_INVALID_PARAMETER; bios_pushback_event(&rec); return; } // Determine the number of symbols remaining in the CAP. symbols_remaining_in_cap = (mac_SOInactiveTime / aNumSuperframeSlots) * (mac_FinalCapSlot + 1); symbols_remaining_in_cap -= symbols_since_beacon; if (symbols_remaining_in_cap < offTime) { // If it doesn't fit into the CAP, see whether deferred // handling is allowed or not. if (rxe->DeferPermit) { start_time = bios_add_time(mac_pib_macBeaconTxTime, mac_RollOverTime + rxe->RxOnTime); } else { rec.size = sizeof(mlme_rx_enable_conf_t) - sizeof(rec.size); rec.cmdcode = MLME_RX_ENABLE_CONFIRM; rec.status = MAC_OUT_OF_CAP; bios_pushback_event(&rec); return; } } else { // Request fits into the CAP. start_time = bios_add_time(mac_pib_macBeaconTxTime, rxe->RxOnTime); } // Start a timer to turn on RX at the appointed time // "rxe->RxOnTime" from the start of the next superframe. bios_start_absolute_timer(T_RxOnTime, start_time); // Remember the time we are supposed to turn off the receiver. RxOffTime = bios_add_time(start_time, rxe->RxOnDuration); // We were successful so far. The MLME-RX-ENABLE.confirm message // will be generated once the T_RxOnTime timer expired, and the // receiver has actually been enabled. } /** * @brief Continues handling of MLME-RX-ENABLE.request once the radio is awake */ void mac_awake_rx_enable(void) { plme_set_trx_state_req_t txr; plme_set_trx_state_conf_t tsc; mlme_rx_enable_conf_t rec; // If RxOnDurationTime is 0 we are supposed to disable the // receiver. if (RxOnDurationTime == 0) { // Turn the radio off. txr.size = sizeof(plme_set_trx_state_req_t) - sizeof(txr.size); txr.cmdcode = PLME_SET_TRX_STATE_REQUEST; txr.state = PHY_TRX_OFF; plme_set_trx_state_request_internal(&txr, &tsc); // Send the confirm immediately. rec.size = sizeof(mlme_rx_enable_conf_t) - sizeof(rec.size); rec.cmdcode = MLME_RX_ENABLE_CONFIRM; if ((tsc.status == PHY_SUCCESS) || (tsc.status == PHY_TRX_OFF)) { rec.status = MAC_SUCCESS; } else { rec.status = MAC_TX_ACTIVE; } // set the MAC state to it's original state mac_state = mac_original_state; // go to sleep? if(!mac_pib_macRxOnWhenIdle) { // set sleep=true mac_phy_init_sleep(); } bios_pushback_event(&rec); return; } // Handle non-beacon enabled and beacon enabled PANs firstly together // Turn the receiver on immediately. txr.size = sizeof(plme_set_trx_state_req_t) - sizeof(txr.size); txr.cmdcode = PLME_SET_TRX_STATE_REQUEST; txr.state = PHY_RX_ON; plme_set_trx_state_request_internal(&txr, &tsc); // Send the confirm immediately. rec.size = sizeof(mlme_rx_enable_conf_t) - sizeof(rec.size); rec.cmdcode = MLME_RX_ENABLE_CONFIRM; if ((tsc.status == PHY_SUCCESS) || (tsc.status == PHY_RX_ON)) { rec.status = MAC_SUCCESS; // Set a timer event upon exipry the receiver will be turned off. // RxOnDuration is contained in the incomming message. // Distinction of non-beacon enabled and beacon enabled PANs at this point. if (mac_pib_macBeaconOrder == NON_BEACON_NETWORK) { // Handle non-beacon enabled PANs bios_starttimer(T_RxOffTime, RxOnDurationTime); } else { // Handle beacon enabled PANs bios_start_absolute_timer(T_RxOffTime, RxOffTime); } } else { rec.status = MAC_TX_ACTIVE; } // set the MAC state to it's original state mac_state = mac_original_state; bios_pushback_event(&rec); return; } /** * @brief The RX "on" timer. * * This actually turns the radio receiver on - i.e. this is the * beginning of the RX_ON period. */ void mac_t_rx_enable_duration_start(void) { // remember the original MAC state mac_original_state = mac_state; // do we need to wake up the radio first? if (mac_radio_sleep_state == RADIO_SLEEPING) { mac_state = MAC_WAKEUP_RX_ENABLE; // wake up radio first mac_phy_wakeup(); } else { mac_awake_rx_enable(); } } /** * @brief The RX "off" timer. * * This actually turns the radio receiver off - i.e. this is the end * of the RX_ON period. */ void mac_t_rx_enable_duration_expired(void) { plme_set_trx_state_req_t txr; plme_set_trx_state_conf_t tsc; // Turn off the radio now. txr.size = sizeof(plme_set_trx_state_req_t) - sizeof(txr.size); txr.cmdcode = PLME_SET_TRX_STATE_REQUEST; txr.state = PHY_TRX_OFF; plme_set_trx_state_request_internal(&txr, &tsc); // Nobody is going to be interested in the success of that. // go to sleep? if(!mac_pib_macRxOnWhenIdle) { // set sleep=true mac_phy_init_sleep(); } } #endif /* APP_TYPE >= APP_L2 || defined(DOXYGEN) */ /* EOF */