/** * @brief Switches the PLL on */ static void switch_pll_on(void) { uint32_t start_time; uint32_t current_time; /* Check if trx is in TRX_OFF; only from PLL_ON the following procedure is applicable */ if (pal_trx_bit_read(SR_TRX_STATUS) != TRX_OFF) { ASSERT("Switch PLL_ON failed, because trx is not in TRX_OFF" == 0); return; } /* Clear all pending trx interrupts */ pal_trx_reg_read(RG_IRQ_STATUS); /* Get current IRQ mask */ uint8_t trx_irq_mask = pal_trx_reg_read(RG_IRQ_MASK); /* Enable transceiver's PLL lock interrupt */ pal_trx_reg_write(RG_IRQ_MASK, TRX_IRQ_0_PLL_LOCK); ENTER_TRX_REGION(); // Disable trx interrupt handling /* Switch PLL on */ pal_trx_reg_write(RG_TRX_STATE, CMD_PLL_ON); pal_get_current_time(&start_time); /* Wait for transceiver interrupt: check for IRQ line */ while (PAL_TRX_IRQ_HIGH() == false) { /* Handle errata "potential long PLL settling duration". */ pal_get_current_time(¤t_time); if (pal_sub_time_us(current_time, start_time) > PLL_LOCK_DURATION_MAX_US) { uint8_t reg_value; reg_value = pal_trx_reg_read(RG_PLL_CF); if (reg_value & 0x01) { reg_value &= 0xFE; } else { reg_value |= 0x01; } pal_trx_reg_write(RG_PLL_CF, reg_value); pal_get_current_time(&start_time); } /* Wait until trx line has been raised. */ } /* Clear PLL lock interrupt at trx */ pal_trx_reg_read(RG_IRQ_STATUS); /* Clear MCU's interrupt flag */ pal_trx_irq_flag_clr(); LEAVE_TRX_REGION(); // Enable trx interrupt handling again /* Restore transceiver's interrupt mask. */ pal_trx_reg_write(RG_IRQ_MASK, trx_irq_mask); }
/** * \brief Checks if node is receiving beacons * * \return true: beacons are received otherwise false */ static bool check_beacon_reception(void) { uint32_t time_between_beacons_sym; uint32_t next_beacon_time_sym; uint8_t number_of_lost_beacon = 0; uint32_t now_time; time_between_beacons_sym = TAL_GET_BEACON_INTERVAL_TIME( tal_pib.BeaconOrder); next_beacon_time_sym = tal_add_time_symbols(tal_pib.BeaconTxTime, time_between_beacons_sym); pal_get_current_time(&now_time); now_time = TAL_CONVERT_US_TO_SYMBOLS(now_time); /* If the last beacon was not received, calculate/estimate the next * beacon time */ while (next_beacon_time_sym < now_time) { tal_pib.BeaconTxTime = next_beacon_time_sym; next_beacon_time_sym = tal_add_time_symbols( tal_pib.BeaconTxTime, time_between_beacons_sym); number_of_lost_beacon++; if (number_of_lost_beacon == aMaxLostBeacons) { /* sync loss */ return false; } } return true; }
/** * @brief Checks if node is receiving beacons * * @return true: beacons are received otherwise false */ static bool check_beacon_reception(void) { uint32_t time_between_beacons_sym; uint32_t next_beacon_time_sym; //uint8_t number_of_lost_beacon = 0; uint32_t now_time; time_between_beacons_sym = TAL_GET_BEACON_INTERVAL_TIME(tal_pib_BeaconOrder); next_beacon_time_sym = tal_add_time_symbols(tal_pib_BeaconTxTime, time_between_beacons_sym); pal_get_current_time(&now_time); now_time = TAL_CONVERT_US_TO_SYMBOLS(now_time); /* If the last beacon was not received, calculate/estimate the next beacon time */ while (next_beacon_time_sym < now_time) { tal_pib_BeaconTxTime = next_beacon_time_sym; next_beacon_time_sym = tal_add_time_symbols(tal_pib_BeaconTxTime, time_between_beacons_sym); /* * The following lines are excluded until, * the node is able to sync before transmission. */ /* number_of_lost_beacon++; if (number_of_lost_beacon == aMaxLostBeacons) // sync loss { return false; } */ } return true; }
/** * \brief Sends the frame at the next backoff boundary */ static void send_frame_at_next_backoff_boundary(void) { uint8_t ack_is_requested; uint32_t now_time_us; /* * Locate the next backoff boundary for the frame transmissiom; * this backoff boundary is the starttime for the frame fransmission. * Use a blocking approach, since next backoff boundary should be close. */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, PRE_TX_DURATION_US) < cca_starttime_us); /* re-programm the interrupt handler */ pal_trx_irq_init((FUNC_PTR)ack_reception_handler_cb); pal_trx_irq_en(); /* debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h */ PIN_TX_START(); /* Check if an acknowledgement is requested for this frame. */ ack_is_requested = *(tal_frame_to_tx + 1) & FCF_ACK_REQUEST; if (ack_is_requested > 0) { tal_csma_state = FRAME_SENDING_WITH_ACK; } else { tal_csma_state = FRAME_SENDING_NO_ACK; } /* download and send frame, no CSMA and no frame_retry */ send_frame(NO_CSMA_NO_IFS, false); }
/** * @brief Switches the PLL on */ static void switch_pll_on(void) { trx_irq_reason_t irq_status; uint32_t start_time, now; /* Check if trx is in TRX_OFF; only from PLL_ON the following procedure is applicable */ if (pal_trx_bit_read(SR_TRX_STATUS) != TRX_OFF) { ASSERT("Switch PLL_ON failed, because trx is not in TRX_OFF" == 0); return; } /* use the IRQ status register checking for the actual PLL status */ pal_trx_irq_dis(); pal_trx_reg_write(RG_IRQ_MASK, TRX_IRQ_PLL_LOCK); /* allow PLL lock IRQ only*/ pal_trx_reg_read(RG_IRQ_STATUS); /* clear PLL lock bit */ /* Switch PLL on */ pal_trx_reg_write(RG_TRX_STATE, CMD_PLL_ON); /* Check if PLL has been locked. */ pal_get_current_time(&start_time); while (1) { irq_status = (trx_irq_reason_t)pal_trx_reg_read(RG_IRQ_STATUS); if (irq_status & TRX_IRQ_PLL_LOCK) { break; // PLL is locked now } /* Check if polling needs too much time. */ pal_get_current_time(&now); if (pal_sub_time_us(now, start_time) > (10 * PLL_LOCK_TIME_US)) { /* leave poll loop and throw assertion */ #if (DEBUG > 0) ASSERT("PLL switch failed" == 0); #endif break; } } pal_trx_irq_flag_clr(); pal_trx_reg_write(RG_IRQ_MASK, TRX_IRQ_TRX_END); /* enable TRX_END interrupt */ pal_trx_irq_en(); }
/** * \brief Sends the frame at the next backoff boundary */ static void send_frame_at_next_backoff_boundary(void) { uint32_t now_time_us; /* * Locate the next backoff boundary for the frame transmissiom; * this backoff boundary is the starttime for the frame fransmission. * Use a blocking approach, since next backoff boundary should be close. */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, PRE_TX_DURATION_US) < cca_starttime_us); /* debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h */ PIN_TX_START(); tal_csma_state = FRAME_SENDING; /* download and send frame, no CSMA and no frame_retry */ send_frame(NO_CSMA_NO_IFS, false); }
/** * \brief Performs CCA twice */ static uint8_t perform_cca_twice(void) { uint8_t cca_status; uint8_t cca_done; uint8_t CW = 2; uint32_t now_time_us; do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (SLEEP_TO_TRX_OFF_TYP_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); #if ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) if (tal_beacon_transmission) { #if (_DEBUG_ > 0) Assert("Ongoing beacon transmission, slotted CSMA busy" == 0); #endif return PHY_BUSY; } #endif /* ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) */ /* Ensure that trx is at least in TRX_OFF mode at this time. */ if (tal_trx_status == TRX_SLEEP) { set_trx_state(CMD_TRX_OFF); } do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (TRX_OFF_TO_PLL_ON_TIME_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* * Set trx to PLL_ON. * If trx is busy and trx cannot be set to PLL_ON, assess channel as * busy. */ if (set_trx_state(CMD_PLL_ON) != PLL_ON) { return PHY_BUSY; } /* no interest in receiving frames while doing CCA */ trx_bit_write(SR_RX_PDT_DIS, RX_DISABLE); /* disable frame reception * indication */ /* do CCA twice */ do { /* wait here until 16us before backoff boundary */ /* assume TRX is in PLL_ON */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, CCA_PRE_START_DURATION_US) < cca_starttime_us); set_trx_state(CMD_RX_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, * pal_config.h */ PIN_CCA_START(); /* Start CCA */ trx_bit_write(SR_CCA_REQUEST, CCA_START); /* wait until CCA is done and get status */ pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(CCA_DURATION_SYM)); do { /* poll until CCA is really done; */ cca_done = trx_bit_read(SR_CCA_DONE); } while (cca_done != CCA_COMPLETED); /* between both CCA switch trx to PLL_ON to reduce power * consumption */ set_trx_state(CMD_PLL_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, * pal_config.h */ PIN_CCA_END(); /* check if channel was idle or busy */ if (trx_bit_read(SR_CCA_STATUS) == CCA_CH_IDLE) { /* do next CCA at next backoff boundary */ cca_starttime_us = pal_add_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( aUnitBackoffPeriod)); CW--; cca_status = PHY_IDLE; } else { /* PHY busy */ cca_status = PHY_BUSY; set_trx_state(CMD_RX_AACK_ON); break; /* if channel is busy do no do CCA for the second * time */ } } while (CW > 0); /* * Keep trx ready for transmission if channel is idle. * The transceiver is still in PLL_ON. * If the channel is not idle, the trx handling is done in * csma_backoff(). */ /* * Clear CCA interrupt flag. * This is only necessary for debugging, because only in debug mode * interrupt that are not handled cause an assert in the ISR. */ #if (_DEBUG_ > 0) trx_reg_read(RG_IRQ_STATUS); #endif /* * Since we are not interested in any frames that might be received * during CCA, reject any information that indicates a previous frame * reception. */ trx_bit_write(SR_RX_PDT_DIS, RX_ENABLE); /* enable frame reception * indication */ return cca_status; }
/** * \brief Calculates backoff duration and handles the start of the CCA */ static void csma_backoff_calculation(void) { uint32_t current_CAP_duration_sym; uint32_t current_CAP_end_sym; uint32_t next_backoff_boundary_us; uint32_t now_time_sym; uint32_t guard_time_before_next_beacon; /* \TODO consider CFP and BLE mode */ current_CAP_duration_sym = TAL_GET_SUPERFRAME_DURATION_TIME( tal_pib.SuperFrameOrder); current_CAP_end_sym = tal_add_time_symbols(tal_pib.BeaconTxTime, current_CAP_duration_sym); /* * Add some guard time to ensure that the transaction is completed * before * the timer fires that is going to track the next beacon. */ guard_time_before_next_beacon = TAL_RADIO_WAKEUP_TIME_SYM << (tal_pib.BeaconOrder + 2); guard_time_before_next_beacon += TAL_CONVERT_US_TO_SYMBOLS( PRE_BEACON_GUARD_TIME_US); current_CAP_end_sym = tal_sub_time_symbols(current_CAP_end_sym, guard_time_before_next_beacon); /* Calculate next backoff period boundary. */ { uint32_t time_since_last_beacon_sym; uint32_t next_backoff_boundary_period; pal_get_current_time(&now_time_sym); now_time_sym = TAL_CONVERT_US_TO_SYMBOLS(now_time_sym); time_since_last_beacon_sym = tal_sub_time_symbols(now_time_sym, tal_pib.BeaconTxTime); next_backoff_boundary_period = time_since_last_beacon_sym / aUnitBackoffPeriod; if ((time_since_last_beacon_sym % aUnitBackoffPeriod) > 0) { next_backoff_boundary_period++; } next_backoff_boundary_us = TAL_CONVERT_SYMBOLS_TO_US( pal_add_time_us(tal_pib.BeaconTxTime, (next_backoff_boundary_period * aUnitBackoffPeriod))); } /* Check if we are still within the CAP. */ if (next_backoff_boundary_us >= TAL_CONVERT_SYMBOLS_TO_US(current_CAP_end_sym)) { /* current CAP is over, wait for next CAP */ tal_csma_state = BACKOFF_WAITING_FOR_BEACON; start_beacon_loss_timer(); } else { /* next backoff boundary is within current CAP */ uint32_t remaining_periods_in_CAP; /* \TODO check if variable * size can be reduced */ /* Check if the remaining backoff time will expire in current * CAP. */ remaining_periods_in_CAP = tal_sub_time_symbols( current_CAP_end_sym, now_time_sym) / aUnitBackoffPeriod; if (remaining_backoff_periods > remaining_periods_in_CAP) { /* * Reduce the backoff peridos by the remaining duration * in * the current CAP and continue in next CAP. */ remaining_backoff_periods -= remaining_periods_in_CAP; tal_csma_state = BACKOFF_WAITING_FOR_BEACON; start_beacon_loss_timer(); } else { /* there are enough backoff periods in current CAP */ uint32_t time_after_transaction_sym; /* \TODO check if * variable size * can be reduced **/ uint32_t transaction_duration_sym; /* \TODO check if * variable size can * be reduced */ /* Add some guard time to wakeup the transceiver. */ transaction_duration_sym = (transaction_duration_periods * aUnitBackoffPeriod) + TAL_CONVERT_US_TO_SYMBOLS( SLEEP_TO_TRX_OFF_TYP_US + CCA_GUARD_DURATION_US); time_after_transaction_sym = tal_add_time_symbols(TAL_CONVERT_US_TO_SYMBOLS( next_backoff_boundary_us), transaction_duration_sym); /* Check if the entire transaction fits into the current * CAP. */ if (time_after_transaction_sym < current_CAP_end_sym) { retval_t timer_status; uint32_t callback_start_time; /* Calculate the time needed to backoff. */ cca_starttime_us = pal_add_time_us( next_backoff_boundary_us, TAL_CONVERT_SYMBOLS_TO_US( remaining_backoff_periods * aUnitBackoffPeriod)); /* * Ensure that wakeup time is available before * CCA. * The required duration depends on the current * trx status. * Assume here the worst case: trx is in SLEEP. */ /* * \TODO depending on the duration that we need * to backoff, * set trx to SLEEP, TRX_OFF or PLL_ON * meanwhile. */ while (pal_sub_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( now_time_sym)) < (SLEEP_TO_TRX_OFF_TYP_US + CCA_GUARD_DURATION_US)) { cca_starttime_us = pal_add_time_us( cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( aUnitBackoffPeriod)); } /* * Start the CCA timer. * Add some time to locate the next backoff * boundary * once CCA timer fires. */ callback_start_time = pal_sub_time_us(cca_starttime_us, (SLEEP_TO_TRX_OFF_TYP_US + CCA_PREPARATION_DURATION_US)); timer_status = pal_timer_start(TAL_CSMA_CCA, callback_start_time, TIMEOUT_ABSOLUTE, (FUNC_PTR)cca_timer_handler_cb, NULL); if (timer_status == MAC_SUCCESS) { tal_csma_state = BACKOFF_WAITING_FOR_CCA_TIMER; } else if (timer_status == PAL_TMR_INVALID_TIMEOUT) { /* Start the CCA immediately. */ cca_timer_handler_cb(NULL); } else { tal_csma_state = CSMA_ACCESS_FAILURE; Assert("CCA timer start problem" == 0); } /* debug pin to switch on: define * ENABLE_DEBUG_PINS, pal_config.h */ PIN_BACKOFF_START(); } else { /* Restart again after next beacon. */ NB = 0; remaining_backoff_periods = (uint8_t)(rand() & ((1 << BE) - 1)); tal_csma_state = BACKOFF_WAITING_FOR_BEACON; start_beacon_loss_timer(); } } } }
/* * \brief Requests to TAL to transmit frame * * This function is called by the MAC to deliver a frame to the TAL * to be transmitted by the transceiver. * * \param tx_frame Pointer to the frame_info_t structure updated by the MAC * layer * \param csma_mode Indicates mode of csma-ca to be performed for this frame * \param perform_frame_retry Indicates whether to retries are to be performed * for * this frame * * \return MAC_SUCCESS if the TAL has accepted the data from the MAC for frame * transmission * TAL_BUSY if the TAL is busy servicing the previous MAC request */ retval_t tal_tx_frame(frame_info_t *tx_frame, csma_mode_t csma_mode, bool perform_frame_retry) { if (tal_state != TAL_IDLE) { return TAL_BUSY; } /* * Store the pointer to the provided frame structure. * This is needed for the callback function. */ mac_frame_ptr = tx_frame; /* Set pointer to actual mpdu to be downloaded to the transceiver. */ tal_frame_to_tx = tx_frame->mpdu; last_frame_length = tal_frame_to_tx[0] - 1; /* * In case the frame is too large, return immediately indicating * invalid status. */ if (tal_frame_to_tx == NULL) { return MAC_INVALID_PARAMETER; } #ifdef BEACON_SUPPORT /* Check if beacon mode is used */ if (csma_mode == CSMA_SLOTTED) { if (!slotted_csma_start(perform_frame_retry)) { return MAC_CHANNEL_ACCESS_FAILURE; } } else { #if (MAC_INDIRECT_DATA_FFD == 1) /* * Check if frame is using indirect transmission, but do not use * the * indirect_in_transit flag. This flag is not set for null data * frames. */ if ((tal_pib.BeaconOrder < NON_BEACON_NWK) && (csma_mode == NO_CSMA_WITH_IFS) && (perform_frame_retry == false)) { /* * Check if indirect transmission can be completed * before the next * beacon transmission. */ uint32_t time_between_beacons_sym; uint32_t next_beacon_time_sym; uint32_t now_time_sym; uint32_t duration_before_beacon_sym; /* Calculate the entire transaction duration. Re-use * function of slotted CSMA. * The additional two backoff periods used for CCA are * kept as a guard time. */ calculate_transaction_duration(); /* Calculate the duration until the next beacon needs to * be transmitted. */ time_between_beacons_sym = TAL_GET_BEACON_INTERVAL_TIME( tal_pib.BeaconOrder); next_beacon_time_sym = tal_add_time_symbols( tal_pib.BeaconTxTime, time_between_beacons_sym); pal_get_current_time(&now_time_sym); now_time_sym = TAL_CONVERT_US_TO_SYMBOLS(now_time_sym); duration_before_beacon_sym = tal_sub_time_symbols( next_beacon_time_sym, now_time_sym); /* Check if transaction can be completed before next * beacon transmission. */ if ((now_time_sym >= next_beacon_time_sym) || ((transaction_duration_periods * aUnitBackoffPeriod) > duration_before_beacon_sym)) { /* * Transaction will not be completed before next * beacon transmission. * Therefore the transmission is not executed. */ return MAC_CHANNEL_ACCESS_FAILURE; } } #endif /* #if (MAC_INDIRECT_DATA_FFD == 1) */ send_frame(csma_mode, perform_frame_retry); } #else /* No BEACON_SUPPORT */ send_frame(csma_mode, perform_frame_retry); #endif /* BEACON_SUPPORT / No BEACON_SUPPORT */ return MAC_SUCCESS; }
/** * @brief Implement the MLME-RX-ENABLE.request primitive. * * The MLME-RX-ENABLE.request primitive is generated by the next * higher layer and issued to MAC 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) { mlme_rx_enable_req_t *rxe; rxe = (mlme_rx_enable_req_t *)BMM_BUFFER_POINTER((buffer_t *)m); /* If RxOnDuration is zero, the receiver shall be disabled */ if (0 == rxe->RxOnDuration) { /* * Turn the radio off. This is doney by calling the * same function as for the expiration of the Rx on timer. */ mac_t_rx_off_cb(NULL); /* Send the confirm immediately. */ gen_rx_enable_conf((buffer_t *)m, (uint8_t)MAC_SUCCESS); return; } /* * Reject the request when the MAC is currently in any of the * polling states or scanning. */ if ((MAC_POLL_IDLE != mac_poll_state) || (MAC_SCAN_IDLE != mac_scan_state) ) { /* Send the confirm immediately. */ gen_rx_enable_conf((buffer_t *)m, (uint8_t)MAC_TX_ACTIVE); return; } #ifdef BEACON_SUPPORT if (NON_BEACON_NWK == tal_pib.BeaconOrder) { handle_rx_on(rxe->RxOnDuration, m); } else { /* We have a beacon-enabled network. */ uint32_t curr_beacon_int_time_symbols = TAL_GET_BEACON_INTERVAL_TIME(tal_pib.BeaconOrder); uint32_t now_time_symbols; uint32_t symbols_since_beacon; uint32_t rx_on_time_symbols; retval_t timer_status; /* * Determine if (RxOnTime + RxOnDuration) is less than the beacon * interval. * 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 MAC_INVALID_PARAMETER. */ rx_off_time_symbols = rxe->RxOnTime + rxe->RxOnDuration; if (rx_off_time_symbols >= curr_beacon_int_time_symbols) { /* Send the confirm immediately. */ gen_rx_enable_conf((buffer_t *)m, (uint8_t)MAC_INVALID_PARAMETER); return; } pal_get_current_time(&now_time_symbols); now_time_symbols = TAL_CONVERT_US_TO_SYMBOLS(now_time_symbols); symbols_since_beacon = tal_sub_time_symbols(now_time_symbols, tal_pib.BeaconTxTime); /* * Actually, MLME-RX-ENABLE.request in a beacon enabled PAN does * only make sense if the MAC is currently tracking beacons, so * that macBeaconTxTime is up to date. If it appears that * the last known macBeaconTxTime does not relate to the * current superframe, reject the request. */ if (symbols_since_beacon > curr_beacon_int_time_symbols) { /* Send the confirm immediately. */ gen_rx_enable_conf((buffer_t *)m, (uint8_t)MAC_INVALID_PARAMETER); return; } rx_on_time_symbols = tal_add_time_symbols(tal_pib.BeaconTxTime, rxe->RxOnTime); /* Check whether RxOnTime can still be handled in current CAP. */ pal_get_current_time(&now_time_symbols); now_time_symbols = TAL_CONVERT_US_TO_SYMBOLS(now_time_symbols); if (tal_add_time_symbols(rx_on_time_symbols, TAL_CONVERT_US_TO_SYMBOLS(MIN_TIMEOUT)) < now_time_symbols) { /* RxOnTime not possible within this CAP, see whether deferred * handling is allowed or not.. */ if (!(rxe->DeferPermit)) { gen_rx_enable_conf((buffer_t *)m, (uint8_t)MAC_PAST_TIME); return; } else { /* * The MAC defers until the next superframe and attempts to enable * the receiver in that superframe. */ rx_on_time_symbols = tal_add_time_symbols(rx_on_time_symbols, curr_beacon_int_time_symbols); } } /* * Since the Rx-Enable timer could already be running, * it is stopped first, before it will be started (again). */ pal_timer_stop(T_Rx_Enable); do { /* * Start a timer to turn Rx ON at the time "rxe->RxOnTime" from the start * of the next superframe. * Return value to be checked, because Rx on time could be too short * or in the past already. */ timer_status = pal_timer_start(T_Rx_Enable, TAL_CONVERT_SYMBOLS_TO_US(rx_on_time_symbols), TIMEOUT_ABSOLUTE, (FUNC_PTR())mac_t_rx_on_cb, (void *)m); if (MAC_SUCCESS != timer_status) { rx_on_time_symbols = tal_add_time_symbols(rx_on_time_symbols, curr_beacon_int_time_symbols); } } while (MAC_SUCCESS != timer_status); /* Remember the time to turn off the receiver. */ rx_off_time_symbols = tal_add_time_symbols(rx_on_time_symbols, rxe->RxOnDuration); /* The remaining stuff will be done once the Rx On Timer expires. */ } #else /* No BEACON_SUPPORT */ handle_rx_on(rxe->RxOnDuration, m); #endif /* BEACON_SUPPORT / No BEACON_SUPPORT */ } /* mlme_rx_enable_request() */
/** * @brief Transceiver interrupt handler * * This function handles the transceiver interrupt. It reads all IRQs from the * transceivers and stores them to a variable. If a transceiver is currently * sleeping, then the IRQs are not handled. * The actual processing of the IRQs is triggered from tal_task(). */ void trx_irq_handler_cb(void) { #ifdef IRQ_DEBUGGING uint32_t now; pal_get_current_time(&now); #endif /* Get all IRQS values */ uint8_t irqs_array[4]; trx_read(RG_RF09_IRQS, irqs_array, 4); /* Handle BB IRQS */ for (uint8_t trx_id = 0; trx_id < NUM_TRX; trx_id++) { if (tal_state[trx_id] == TAL_SLEEP) { continue; } bb_irq_t irqs = (bb_irq_t)irqs_array[trx_id + 2]; if (irqs != BB_IRQ_NO_IRQ) { if (irqs & BB_IRQ_RXEM) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_RXEM)); /* * avoid * Pa091 */ } if (irqs & BB_IRQ_RXAM) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_RXAM)); /* * avoid * Pa091 */ } if (irqs & BB_IRQ_AGCR) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_AGCR)); /* * avoid * Pa091 */ #ifdef IRQ_DEBUGGING per[trx_id].agcr++; printf("AGCR %" PRIu32 "\n", now); #endif #if (defined RF215V1) && (!defined BASIC_MODE) /* Workaround for errata reference #4830 */ if ((irqs & BB_IRQ_RXFE) == 0) { uint16_t reg_offset = RF_BASE_ADDR_OFFSET * trx_id; trx_bit_write( reg_offset + SR_BBC0_AMCS_AACK, 0); trx_bit_write( reg_offset + SR_BBC0_AMCS_AACK, 1); } #endif } if (irqs & BB_IRQ_AGCH) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_AGCH)); /* * avoid * Pa091 */ #ifdef IRQ_DEBUGGING per[trx_id].agch++; printf("AGCH %" PRIu32 "\n", now); #endif } if (irqs & BB_IRQ_RXFS) { #ifdef ENABLE_TSTAMP pal_get_current_time(&fs_tstamp[trx_id]); #endif irqs &= (uint8_t)(~((uint32_t)BB_IRQ_RXFS)); /* * avoid * Pa091 */ #ifdef IRQ_DEBUGGING per[trx_id].rxfs++; printf("RXFS %" PRIu32 "\n", now); #endif } if (irqs & BB_IRQ_RXFE) { pal_get_current_time(&rxe_txe_tstamp[trx_id]); #ifdef IRQ_DEBUGGING per[trx_id].rxfe++; printf("RXFE %" PRIu32 "\n", now); #endif } if (irqs & BB_IRQ_TXFE) { /* used for IFS and for MEASURE_ON_AIR_DURATION **/ pal_get_current_time(&rxe_txe_tstamp[trx_id]); /* tal_rx_enable(trx_id, PHY_RX_ON);//abi */ } /* * Store remaining flags to global TAL variable and * handle them within tal_task() */ tal_bb_irqs[trx_id] |= irqs; } } /* Handle RF IRQS */ for (uint8_t trx_id = 0; trx_id < NUM_TRX; trx_id++) { if (tal_state[trx_id] == TAL_SLEEP) { continue; } rf_irq_t irqs = (rf_irq_t)irqs_array[trx_id]; if (irqs != RF_IRQ_NO_IRQ) { if (irqs & RF_IRQ_TRXRDY) { irqs &= (uint8_t)(~((uint32_t)RF_IRQ_TRXRDY)); /* * avoid * Pa091 */ } if (irqs & RF_IRQ_TRXERR) { } if (irqs & RF_IRQ_BATLOW) { } if (irqs & RF_IRQ_WAKEUP) { } if (irqs & RF_IRQ_IQIFSF) { } if (irqs & RF_IRQ_EDC) { } tal_rf_irqs[trx_id] |= irqs; } } } /* trx_irq_handler_cb() */
void bb_irq_handler_cb(void) { #ifdef IRQ_DEBUGGING uint32_t now; pal_get_current_time(&now); #endif /* Get all IRQS values */ uint8_t irqs_array[4]; pal_trx_read(RF215_BB, RG_RF09_IRQS, irqs_array, 4); /* Handle BB IRQS */ for (uint8_t trx_id = 0; trx_id < NUM_TRX; trx_id++) { if (tal_state[trx_id] == TAL_SLEEP) { continue; } uint8_t irqs = irqs_array[trx_id + 2]; if (irqs != BB_IRQ_NO_IRQ) { if (irqs & BB_IRQ_RXEM) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_RXEM)); /* * avoid * Pa091 */ } if (irqs & BB_IRQ_RXAM) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_RXAM)); /* * avoid * Pa091 */ } if (irqs & BB_IRQ_AGCR) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_AGCR)); /* * avoid * Pa091 */ uint16_t reg_offset = RF_BASE_ADDR_OFFSET * trx_id; /* Release AGC */ pal_trx_bit_write(RF215_RF, reg_offset + SR_RF09_AGCC_FRZC, 0); #ifdef IRQ_DEBUGGING per[trx_id].agcr++; printf("AGCR %" PRIu32 "\n", now); #endif #if (defined RF215V1) && (!defined BASIC_MODE) /* Workaround for errata reference #4830 */ if ((irqs & BB_IRQ_RXFE) == 0) { uint16_t reg_offset = RF_BASE_ADDR_OFFSET * trx_id; trx_bit_write( reg_offset + SR_BBC0_AMCS_AACK, 0); trx_bit_write( reg_offset + SR_BBC0_AMCS_AACK, 1); } #endif } if (irqs & BB_IRQ_AGCH) { irqs &= (uint8_t)(~((uint32_t)BB_IRQ_AGCH)); /* * avoid * Pa091 */ /* Hold AGC */ uint16_t reg_offset = RF_BASE_ADDR_OFFSET * trx_id; pal_trx_bit_write(RF215_RF, reg_offset + SR_RF09_AGCC_FRZC, 1); #ifdef IRQ_DEBUGGING per[trx_id].agch++; printf("AGCH %" PRIu32 "\n", now); #endif } if (irqs & BB_IRQ_RXFS) { #ifdef ENABLE_TSTAMP pal_get_current_time(&fs_tstamp[trx_id]); #endif irqs &= (uint8_t)(~((uint32_t)BB_IRQ_RXFS)); /* * avoid * Pa091 */ #ifdef IRQ_DEBUGGING per[trx_id].rxfs++; printf("RXFS %" PRIu32 "\n", now); #endif } if (irqs & BB_IRQ_RXFE) { #ifdef IRQ_DEBUGGING per[trx_id].rxfe++; printf("RXFE %" PRIu32 "\n", now); #endif pal_get_current_time(&rxe_txe_tstamp[trx_id]); /* Wait for TXPREP and clear TRXRDY IRQ */ switch_rf_to_txprep((trx_id_t)trx_id); } if (irqs & BB_IRQ_TXFE) { /* used for IFS and for MEASURE_ON_AIR_DURATION **/ pal_get_current_time(&rxe_txe_tstamp[trx_id]); /* BB interrupt handles further processing */ } /* * Store remaining flags to global TAL variable and * handle them within tal_task() */ tal_bb_irqs[trx_id] |= irqs; } } /* Handle RF IRQS */ for (uint8_t trx_id = 0; trx_id < NUM_TRX; trx_id++) { if (tal_state[trx_id] == TAL_SLEEP) { continue; } uint8_t irqs = irqs_array[trx_id]; if (irqs != RF_IRQ_NO_IRQ) { if (irqs & RF_IRQ_TRXRDY) { irqs &= (uint8_t)(~((uint32_t)RF_IRQ_TRXRDY)); /* * avoid * Pa091 */ } if (irqs & RF_IRQ_TRXERR) { #if (BOARD_TYPE == EVAL215_FPGA) printf("BB - RF_IRQ_TRXERR\n"); #endif } if (irqs & RF_IRQ_BATLOW) { irqs &= (uint8_t)(~((uint32_t)RF_IRQ_BATLOW)); /* * avoid * Pa091 */ #if (BOARD_TYPE == EVAL215_FPGA) printf("BB - RF_IRQ_BATLOW\n"); #endif } if (irqs & RF_IRQ_WAKEUP) { irqs &= (uint8_t)(~((uint32_t)RF_IRQ_WAKEUP)); /* * avoid * Pa091 */ #if (BOARD_TYPE == EVAL215_FPGA) printf("BB - RF_IRQ_WAKEUP\n"); #endif } if (irqs & RF_IRQ_IQIFSF) { #if (BOARD_TYPE == EVAL215_FPGA) #ifdef IRQ_DEBUGGING printf("BB - RF_IRQ_IQIFSF %" PRIu32 "\n", now); #endif #endif } if (irqs & RF_IRQ_EDC) { irqs &= (uint8_t)(~((uint32_t)RF_IRQ_EDC)); /* * avoid * Pa091 */ } if (irqs != 0) { } tal_rf_irqs[trx_id] |= irqs; } } } /* bb_irq_handler_cb() */
/** * @brief Performs CCA twice */ static uint8_t perform_cca_twice(void) { uint8_t cca_status; uint8_t cca_done; uint8_t CW = 2; uint32_t now_time_us; do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (SLEEP_TO_TRX_OFF_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); #ifndef RFD if (tal_beacon_transmission) { #if (DEBUG > 0) ASSERT("Ongoing beacon transmission, slotted CSMA busy" == 0); #endif return PHY_BUSY; } #endif // Ensure that trx is at least in TRX_OFF mode at this time. if (tal_trx_status == TRX_SLEEP) { set_trx_state(CMD_TRX_OFF); } do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (PLL_LOCK_TIME_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* * Set trx to PLL_ON. * If trx is busy and trx cannot be set to PLL_ON, assess channel as busy. */ if (set_trx_state(CMD_PLL_ON) != PLL_ON) { return PHY_BUSY; } // no interest in receiving frames while doing CCA pal_trx_bit_write(SR_RX_PDT_DIS, RX_DISABLE); // disable frame reception indication // do CCA twice do { // wait here until 16us before backoff boundary // assume TRX is in PLL_ON do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, CCA_PRE_START_DURATION_US) < cca_starttime_us); pal_trx_reg_write(RG_TRX_STATE, CMD_RX_ON); // debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h PIN_CCA_START(); /* Start CCA */ pal_trx_bit_write(SR_CCA_REQUEST, CCA_START); // wait until CCA is done and get status pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(CCA_DURATION_SYM)); do { // poll until CCA is really done; cca_done = pal_trx_bit_read(SR_CCA_DONE); } while (cca_done != CCA_DETECTION_DONE); // between both CCA switch trx to PLL_ON to reduce power consumption pal_trx_reg_write(RG_TRX_STATE, CMD_PLL_ON); // debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h PIN_CCA_END(); // check if channel was idle or busy if (pal_trx_bit_read(SR_CCA_STATUS) == CCA_STATUS_CHANNEL_IS_IDLE) { // do next CCA at next backoff boundary cca_starttime_us = pal_add_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US(aUnitBackoffPeriod)); CW--; cca_status = PHY_IDLE; } else // PHY busy { cca_status = PHY_BUSY; break; // if channel is busy do no do CCA for the second time } } while (CW > 0); /* * Keep trx ready for transmission if channel is idle. * The transceiver is still in PLL_ON. * If the channel is not idle, the trx handling is done in csma_backoff(). */ /* * Clear CCA interrupt flag. * This is only necessary for debugging, because only in debug mode * interrupt that are not handled cause an assert in the ISR. */ #if (DEBUG > 0) pal_trx_reg_read(RG_IRQ_STATUS); #endif /* * Since we are not interested in any frames that might be received * during CCA, reject any information that indicates a previous frame * reception. */ pal_trx_bit_write(SR_RX_PDT_DIS, RX_ENABLE); // enable frame reception indication return cca_status; }
/** * \brief Performs CCA twice */ static uint8_t perform_cca_twice(void) { tal_trx_status_t trx_status; uint8_t cca_status; uint8_t CW = 2; uint32_t now_time_us; do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (SLEEP_TO_TRX_OFF_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* Ensure that trx is at least in TRX_OFF mode at this time. */ if (tal_trx_status == TRX_SLEEP) { set_trx_state(CMD_TRX_OFF); } do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, (PLL_LOCK_TIME_US + CCA_PREPARATION_DURATION_US)) < cca_starttime_us); /* * Set trx to PLL_ON. * If trx is busy and trx cannot be set to PLL_ON, assess channel as *busy. */ if (set_trx_state(CMD_PLL_ON) != PLL_ON) { return PHY_BUSY; } /* no interest in trx interrupts while doing CCA */ pal_trx_irq_dis(); /* do CCA twice */ do { /* wait here until 16us before backoff boundary */ /* assume TRX is in PLL_ON */ do { pal_get_current_time(&now_time_us); } while (pal_add_time_us(now_time_us, CCA_PRE_START_DURATION_US) < cca_starttime_us); set_trx_state(CMD_RX_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, *pal_config.h */ PIN_CCA_START(); /* Start CCA by writing any dummy value to this register */ pal_trx_bit_write(SR_CCA_REQUEST, 1); /* wait until CCA is done and get status */ pal_timer_delay(TAL_CONVERT_SYMBOLS_TO_US(CCA_DURATION_SYM)); do { /* poll until CCA is really done; */ trx_status = (tal_trx_status_t)pal_trx_reg_read( RG_TRX_STATUS); } while ((trx_status & CCA_DONE_BIT) != CCA_DONE_BIT); /* between both CCA switch trx to PLL_ON to reduce power *consumption */ set_trx_state(CMD_PLL_ON); /* debug pin to switch on: define ENABLE_DEBUG_PINS, *pal_config.h */ PIN_CCA_END(); /* check if channel was idle or busy */ if (trx_status & CCA_STATUS_BIT) { /* do next CCA at next backoff boundary */ cca_starttime_us = pal_add_time_us(cca_starttime_us, TAL_CONVERT_SYMBOLS_TO_US( aUnitBackoffPeriod)); CW--; cca_status = PHY_IDLE; } else { /* PHY busy */ cca_status = PHY_BUSY; set_trx_state(CMD_RX_AACK_ON); break; /* if channel is busy do no do CCA for the second * time */ } } while (CW > 0); /* * Keep trx ready for transmission if channel is idle. * The transceiver is still in PLL_ON. * If the channel is not idle, the trx handling is done in *csma_backoff(). */ /* * Since we are not interested in any frames that might be received * during CCA, reject any information that indicates a previous frame * reception. */ pal_trx_reg_read(RG_IRQ_STATUS); pal_trx_irq_flag_clr(); pal_trx_irq_en(); return cca_status; }
/** * @brief Transceiver interrupt handler * * This function handles the transceiver generated interrupts. */ void trx_irq_handler_cb(void) { trx_irq_reason_t trx_irq_cause; #if (defined BEACON_SUPPORT) || (defined ENABLE_TSTAMP) #ifdef DISABLE_TSTAMP_IRQ /* * Get timestamp. * * In case Antenna diversity is used or the utilization of * the Timestamp IRQ is disabled, the timestamp needs to be read now * the "old-fashioned" way. * * The timestamping is generally only done for * beaconing networks or if timestamping is explicitly enabled. */ pal_get_current_time(&tal_rx_timestamp); #endif /* DISABLE_TSTAMP_IRQ */ #endif /* #if (defined BEACON_SUPPORT) || (defined ENABLE_TSTAMP) */ trx_irq_cause = (trx_irq_reason_t)pal_trx_reg_read(RG_IRQ_STATUS); if (trx_irq_cause & TRX_IRQ_TRX_END) { /* * TRX_END reason depends on if the trx is currently used for * transmission or reception. */ #if ((MAC_START_REQUEST_CONFIRM == 1) && (defined BEACON_SUPPORT)) if ((tal_state == TAL_TX_AUTO) || tal_beacon_transmission) #else if (tal_state == TAL_TX_AUTO) #endif { /* Get the result and push it to the queue. */ if (trx_irq_cause & TRX_IRQ_TRX_UR) { handle_tx_end_irq(true); // see tal_tx.c } else { handle_tx_end_irq(false); // see tal_tx.c } } else /* Other tal_state than TAL_TX_... */ { /* Handle rx interrupt. */ handle_received_frame_irq(); // see tal_rx.c } } #if (DEBUG > 0) /* Other IRQ than TRX_END */ if (trx_irq_cause != TRX_IRQ_TRX_END) { /* PLL_LOCK interrupt migth be set, because poll mode is enabled. */ /* if (trx_irq_cause & TRX_IRQ_PLL_LOCK) { ASSERT("unexpected IRQ: TRX_IRQ_PLL_LOCK" == 0); } */ if (trx_irq_cause & TRX_IRQ_PLL_UNLOCK) { ASSERT("unexpected IRQ: TRX_IRQ_PLL_UNLOCK" == 0); } /* RX_START interrupt migth be set, because poll mode is enabled. */ /* if (trx_irq_cause & TRX_IRQ_RX_START) { ASSERT("unexpected IRQ: TRX_IRQ_RX_START" == 0); } */ if (trx_irq_cause & TRX_IRQ_CCA_ED_READY) { ASSERT("unexpected IRQ: TRX_IRQ_CCA_ED_READY" == 0); } /* AMI interrupt might set, because poll mode is enabled. */ /* if (trx_irq_cause & TRX_IRQ_AMI) { ASSERT("unexpected IRQ: TRX_IRQ_AMI" == 0); } */ if (trx_irq_cause & TRX_IRQ_TRX_UR) { ASSERT("unexpected IRQ: TRX_IRQ_TRX_UR" == 0); } if (trx_irq_cause & TRX_IRQ_BAT_LOW) { ASSERT("unexpected IRQ: TRX_IRQ_BAT_LOW" == 0); } } #endif }/* trx_irq_handler_cb() */
/** * @brief Resets transceiver(s) * * @param trx_id Transceiver identifier * * @return MAC_SUCCESS if the transceiver returns TRX_OFF * FAILURE otherwise */ retval_t trx_reset(trx_id_t trx_id) { ENTER_TRX_REGION(); uint32_t start_time; uint32_t current_time; pal_get_current_time(&start_time); if (trx_id == RFBOTH) { TAL_RF_IRQ_CLR_ALL(RF09); TAL_RF_IRQ_CLR_ALL(RF24); tal_state[RF09] = TAL_RESET; tal_state[RF24] = TAL_RESET; /* Apply reset pulse; low active */ #ifdef IQ_RADIO RST_LOW(); PAL_WAIT_1_US(); PAL_WAIT_1_US(); RST_HIGH(); #if (BOARD_TYPE == EVAL215_FPGA) pal_timer_delay(10000); #endif RST_LOW(); PAL_WAIT_1_US(); RST_HIGH(); #else RST_LOW(); PAL_WAIT_1_US(); RST_HIGH(); #endif /* Wait for IRQ line */ while (1) { /* * @ToDo: Use a different macro for IRQ line; the *polarity might be * different after reset */ #ifdef IQ_RADIO if ((PAL_DEV_IRQ_GET(RF215_BB) == HIGH) && (PAL_DEV_IRQ_GET(RF215_RF) == HIGH)) { break; } #else if (TRX_IRQ_GET() == HIGH) { break; } #endif /* Handle timeout */ pal_get_current_time(¤t_time); /* @ToDo: Remove magic number */ if (pal_sub_time_us(current_time, start_time) > 1000) { return FAILURE; } } #ifdef IQ_RADIO trx_state[RF09] = (rf_cmd_state_t)pal_dev_reg_read(RF215_RF, RG_RF09_STATE); trx_state[RF24] = (rf_cmd_state_t)pal_dev_reg_read(RF215_RF, RG_RF24_STATE); rf_cmd_state_t bb_trx_state[NUM_TRX]; bb_trx_state[RF09] = (rf_cmd_state_t)pal_dev_reg_read(RF215_BB, RG_RF09_STATE); bb_trx_state[RF24] = (rf_cmd_state_t)pal_dev_reg_read(RF215_BB, RG_RF24_STATE); if ((bb_trx_state[RF09] != RF_TRXOFF) || (bb_trx_state[RF24] != RF_TRXOFF)) { return FAILURE; } #else trx_state[RF09] = trx_reg_read(RG_RF09_STATE); trx_state[RF24] = trx_reg_read(RG_RF24_STATE); #endif if ((trx_state[RF09] != RF_TRXOFF) || (trx_state[RF24] != RF_TRXOFF)) { return FAILURE; } /* Get all IRQ status information */ #ifdef IQ_RADIO bb_irq_handler_cb(); rf_irq_handler_cb(); #else trx_irq_handler_cb(); #endif TAL_RF_IRQ_CLR(RF09, RF_IRQ_WAKEUP); TAL_RF_IRQ_CLR(RF24, RF_IRQ_WAKEUP); } else { TAL_RF_IRQ_CLR_ALL(trx_id); tal_state[trx_id] = TAL_RESET; /* Trigger reset of device */ uint16_t reg_offset = RF_BASE_ADDR_OFFSET * trx_id; #ifdef IQ_RADIO pal_trx_reg_write(RF215_RF, reg_offset + RG_RF09_CMD, RF_RESET); pal_trx_reg_write(RF215_BB, reg_offset + RG_RF09_CMD, RF_RESET); #else trx_reg_write(reg_offset + RG_RF09_CMD, RF_RESET); #endif /* Wait for IRQ line */ while (1) { #ifdef IQ_RADIO if ((PAL_DEV_IRQ_GET(RF215_BB) == HIGH) && (PAL_DEV_IRQ_GET(RF215_RF) == HIGH)) { break; } #else if (TRX_IRQ_GET() == HIGH) { break; } #endif /* Handle timeout */ pal_get_current_time(¤t_time); /* @ToDo: Remove magic number */ if (pal_sub_time_us(current_time, start_time) > 1000) { return FAILURE; } } trx_state[trx_id] = RF_TRXOFF; /* Get all IRQ status information */ #ifdef IQ_RADIO bb_irq_handler_cb(); rf_irq_handler_cb(); #else trx_irq_handler_cb(); #endif TAL_RF_IRQ_CLR(trx_id, RF_IRQ_WAKEUP); } #ifdef IQ_RADIO pal_trx_irq_flag_clr(RF215_BB); pal_trx_irq_flag_clr(RF215_RF); #else pal_trx_irq_flag_clr(); #endif LEAVE_TRX_REGION(); return MAC_SUCCESS; }