/** * \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 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 {