/* * @brief helper function to start missed beacon timer */ void mac_start_missed_beacon_timer(void) { uint32_t sync_loss_time; uint8_t timer_status; /* Stop the missed beacon timer. */ pal_timer_stop(T_Missed_Beacon); #if (DEBUG > 0) if (pal_is_timer_running(T_Missed_Beacon)) { ASSERT("Missed BCN tmr running" == 0); } #endif /* Calculate the time allowed for missed beacons. */ if (tal_pib.BeaconOrder < NON_BEACON_NWK) { /* * This the regualar case where we already have a Beacon Order. * In this case the Sync Loss time is a function of the actual * Beacon Order. */ sync_loss_time = TAL_GET_BEACON_INTERVAL_TIME(tal_pib.BeaconOrder); } else { /* * This the "pathological" case where we don NOT have a Beacon Order. * This happens regularly in case of synchronization before association * if the Beacon Order was not set be the network layer or application. * * In this case the Sync Loss time is based on the highest possible * Beacon Order, which is 15 - 1, since 15 means no Beacon network. */ sync_loss_time = TAL_GET_BEACON_INTERVAL_TIME(NON_BEACON_NWK - 1); } sync_loss_time *= aMaxLostBeacons; sync_loss_time = TAL_CONVERT_SYMBOLS_TO_US(sync_loss_time); timer_status = pal_timer_start(T_Missed_Beacon, sync_loss_time, TIMEOUT_RELATIVE, (FUNC_PTR)mac_t_missed_beacons_cb, NULL); if (MAC_SUCCESS != timer_status) { #if (DEBUG > 0) ASSERT(MAC_SUCCESS == timer_status); #endif /* Sync timer could not be started hence report sync-loss */ mac_sync_loss(MAC_BEACON_LOSS); } }
/** * \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 Starts the beacon loss timer */ static void start_beacon_loss_timer(void) { uint32_t timer_duration_us; #if (_DEBUG_ > 0) retval_t timer_status; #endif /* debug pin to switch on: define ENABLE_DEBUG_PINS, pal_config.h */ PIN_BEACON_LOSS_TIMER_START(); timer_duration_us = TAL_CONVERT_SYMBOLS_TO_US(TAL_GET_BEACON_INTERVAL_TIME(tal_pib .BeaconOrder)); timer_duration_us *= aMaxLostBeacons; timer_duration_us += CSMA_BEACON_LOSS_GUARD_TIME_US; #if (_DEBUG_ > 0) timer_status = #endif pal_timer_start(TAL_CSMA_BEACON_LOSS_TIMER, timer_duration_us, TIMEOUT_RELATIVE, (FUNC_PTR)beacon_loss_timer_cb, NULL); #if (_DEBUG_ > 0) if (timer_status != MAC_SUCCESS) { if (timer_status == PAL_TMR_INVALID_TIMEOUT) { Assert( "beacon loss timer start failed: PAL_TMR_INVALID_TIMEOUT" == 0); } else if (timer_status == PAL_TMR_ALREADY_RUNNING) { Assert( "beacon loss timer start failed: PAL_TMR_ALREADY_RUNNING" == 0); } else { Assert("beacon loss timer start failed: ?" == 0); } } #endif }
/* * \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 Continues processing a data indication from the TAL for * non-polling and non-scanning states of the MAC * (mac_poll_state == MAC_POLL_IDLE, mac_scan_state == MAC_SCAN_IDLE). * * @param b_ptr Pointer to the buffer header. * @param f_ptr Pointer to the frame_info_t structure. * * @return bool True if frame has been processed, or false otherwise. */ static bool process_data_ind_not_transient(buffer_t *b_ptr, frame_info_t *f_ptr) { bool processed_in_not_transient = false; /* * We are in MAC_POLL_IDLE and MAC_SCAN_IDLE now, * so continue with the real MAC states. */ switch (mac_state) { #if (MAC_START_REQUEST_CONFIRM == 1) case MAC_PAN_COORD_STARTED: { switch (mac_parse_data.frame_type) { case FCF_FRAMETYPE_MAC_CMD: { switch (mac_parse_data.mac_command) { #if (MAC_ASSOCIATION_INDICATION_RESPONSE == 1) case ASSOCIATIONREQUEST: mac_process_associate_request(b_ptr); processed_in_not_transient = true; break; #endif /* (MAC_ASSOCIATION_INDICATION_RESPONSE == 1) */ #if (MAC_DISASSOCIATION_BASIC_SUPPORT == 1) case DISASSOCIATIONNOTIFICATION: mac_process_disassociate_notification(b_ptr); processed_in_not_transient = true; break; #endif /* (MAC_DISASSOCIATION_BASIC_SUPPORT == 1) */ #if (MAC_INDIRECT_DATA_FFD == 1) case DATAREQUEST: if (indirect_data_q.size > 0) { mac_process_data_request(b_ptr); processed_in_not_transient = true; } else { mac_handle_tx_null_data_frame(); } break; #endif /* (MAC_INDIRECT_DATA_FFD == 1) */ #if (MAC_ORPHAN_INDICATION_RESPONSE == 1) case ORPHANNOTIFICATION: mac_process_orphan_notification(b_ptr); processed_in_not_transient = true; break; #endif /* (MAC_ORPHAN_INDICATION_RESPONSE == 1) */ case BEACONREQUEST: mac_process_beacon_request(b_ptr); processed_in_not_transient = true; break; #if (MAC_PAN_ID_CONFLICT_AS_PC == 1) case PANIDCONFLICTNOTIFICAION: mac_sync_loss(MAC_PAN_ID_CONFLICT); break; #endif /* (MAC_PAN_ID_CONFLICT_AS_PC == 1) */ #ifdef GTS_SUPPORT case GTSREQUEST: mac_process_gts_request(b_ptr); processed_in_not_transient = true; #endif /* GTS_SUPPORT */ default: break; } } break; case FCF_FRAMETYPE_DATA: mac_process_data_frame(b_ptr); processed_in_not_transient = true; break; #if (MAC_PAN_ID_CONFLICT_AS_PC == 1) case FCF_FRAMETYPE_BEACON: /* PAN-Id conflict detection as PAN-Coordinator. */ /* Node is not scanning. */ check_for_pan_id_conflict_as_pc(false); break; #endif /* (MAC_PAN_ID_CONFLICT_AS_PC == 1) */ default: break; } } break; /* MAC_PAN_COORD_STARTED */ #endif /* (MAC_START_REQUEST_CONFIRM == 1) */ case MAC_IDLE: #if (MAC_ASSOCIATION_REQUEST_CONFIRM == 1) case MAC_ASSOCIATED: #endif /* (MAC_ASSOCIATION_REQUEST_CONFIRM == 1) */ #if (MAC_START_REQUEST_CONFIRM == 1) case MAC_COORDINATOR: #endif /* (MAC_START_REQUEST_CONFIRM == 1) */ { /* Is it a Beacon from our parent? */ switch (mac_parse_data.frame_type) { #if (MAC_SYNC_REQUEST == 1) case FCF_FRAMETYPE_BEACON: { uint32_t beacon_tx_time_symb; /* Check for PAN-Id conflict being NOT a PAN * Corodinator. */ #if (MAC_PAN_ID_CONFLICT_NON_PC == 1) if (mac_pib.mac_AssociatedPANCoord && (MAC_IDLE != mac_state)) { check_for_pan_id_conflict_non_pc(false); } #endif /* (MAC_PAN_ID_CONFLICT_NON_PC == 1) */ /* Check if the beacon is received from my * parent. */ if ((mac_parse_data.src_panid == tal_pib.PANId) && (((mac_parse_data.src_addr_mode == FCF_SHORT_ADDR) && (mac_parse_data.src_addr. short_address == mac_pib.mac_CoordShortAddress)) || ((mac_parse_data.src_addr_mode == FCF_LONG_ADDR) && (mac_parse_data.src_addr. long_address == mac_pib.mac_CoordExtendedAddress)))) { beacon_tx_time_symb = TAL_CONVERT_US_TO_SYMBOLS( f_ptr->time_stamp); #if (_DEBUG_ > 0) retval_t set_status = #endif set_tal_pib_internal(macBeaconTxTime, (void *)&beacon_tx_time_symb); #if (_DEBUG_ > 0) Assert(MAC_SUCCESS == set_status); #endif if ((MAC_SYNC_TRACKING_BEACON == mac_sync_state) || (MAC_SYNC_BEFORE_ASSOC == mac_sync_state) ) { uint32_t nxt_bcn_tm; uint32_t beacon_int_symb; /* Process a received beacon. */ mac_process_beacon_frame(b_ptr); /* Initialize beacon tracking * timer. */ { retval_t tmr_start_res = FAILURE; #ifdef BEACON_SUPPORT if (tal_pib.BeaconOrder < NON_BEACON_NWK) { beacon_int_symb = TAL_GET_BEACON_INTERVAL_TIME( tal_pib.BeaconOrder); } else #endif /* BEACON_SUPPORT */ { beacon_int_symb = TAL_GET_BEACON_INTERVAL_TIME( BO_USED_FOR_MAC_PERS_TIME); } pal_timer_stop( T_Beacon_Tracking_Period); #if (_DEBUG_ > 0) if (pal_is_timer_running( T_Beacon_Tracking_Period)) { Assert( "Bcn tmr running" == 0); } #endif do { /* * Calculate the * time for next * beacon * transmission */ beacon_tx_time_symb = tal_add_time_symbols( beacon_tx_time_symb, beacon_int_symb); /* * Take into * account the * time taken by * the radio to * wakeup from * sleep state */ nxt_bcn_tm = tal_sub_time_symbols( beacon_tx_time_symb, TAL_RADIO_WAKEUP_TIME_SYM << ( tal_pib . BeaconOrder + 2)); tmr_start_res = pal_timer_start( T_Beacon_Tracking_Period, TAL_CONVERT_SYMBOLS_TO_US( nxt_bcn_tm), TIMEOUT_ABSOLUTE, ( FUNC_PTR)mac_t_tracking_beacons_cb, NULL); } while (MAC_SUCCESS != tmr_start_res); #ifdef GTS_DEBUG port_pin_toggle_output_level( DEBUG_PIN1); port_pin_set_output_level( DEBUG_PIN2, 0); #endif } /* * Initialize superframe timer * if required only * for devices because * Superframe timer is already * running for * coordinator. */ /* TODO */ if (MAC_ASSOCIATED == mac_state) { mac_superframe_state = MAC_ACTIVE_CAP; /* Check whether the * radio needs to be * woken up. */ mac_trx_wakeup(); /* Set transceiver in rx * mode, otherwise it * may stay in * TRX_OFF). */ tal_rx_enable(PHY_RX_ON); if (tal_pib. SuperFrameOrder < tal_pib . BeaconOrder) { pal_timer_start( T_Superframe, TAL_CONVERT_SYMBOLS_TO_US( TAL_GET_SUPERFRAME_DURATION_TIME( tal_pib . SuperFrameOrder)), TIMEOUT_RELATIVE, ( FUNC_PTR)mac_t_start_inactive_device_cb, NULL); #ifdef GTS_DEBUG port_pin_set_output_level( DEBUG_PIN2, 1); #endif } #ifdef GTS_SUPPORT if (mac_final_cap_slot < FINAL_CAP_SLOT_DEFAULT) { uint32_t gts_tx_time = ( TAL_CONVERT_SYMBOLS_TO_US( TAL_GET_SUPERFRAME_DURATION_TIME( tal_pib . SuperFrameOrder)) >> 4) * ( mac_final_cap_slot + 1); pal_timer_start( T_CAP, gts_tx_time, TIMEOUT_RELATIVE, ( FUNC_PTR)mac_t_gts_cb, NULL); #ifdef GTS_DEBUG port_pin_set_output_level( DEBUG_PIN3, 1); #endif } #endif /* GTS_SUPPORT */ } /* Initialize missed beacon * timer. */ mac_start_missed_beacon_timer(); /* A device that is neither * scanning nor polling shall go * to sleep now. */ if ( (MAC_COORDINATOR != mac_state) && (MAC_SCAN_IDLE == mac_scan_state) && (MAC_POLL_IDLE == mac_poll_state) ) { /* * If the last received * beacon frame from our * parent * has indicated pending * broadbast data, we * need to * stay awake, until the * broadcast data has * been received. */ if (! mac_bc_data_indicated) { /* Set radio to * sleep if * allowed */ mac_sleep_trans(); } } } else if (MAC_SYNC_ONCE == mac_sync_state) { mac_process_beacon_frame(b_ptr); /* Do this after processing the * beacon. */ mac_sync_state = MAC_SYNC_NEVER; /* A device that is neither * scanning nor polling shall go * to sleep now. */ if ( (MAC_COORDINATOR != mac_state) && (MAC_SCAN_IDLE == mac_scan_state) && (MAC_POLL_IDLE == mac_poll_state) ) { /* * If the last received * beacon frame from our * parent * has indicated pending * broadbast data, we * need to * stay awake, until the * broadcast data has * been received. */ if (! mac_bc_data_indicated) { /* Set radio to * sleep if * allowed */ mac_sleep_trans(); } } } else { /* Process the beacon frame */ bmm_buffer_free(b_ptr); } processed_in_not_transient = true; } else {