/* Maria project {{{ * * Copyright (C) 2007 Spidcom * * <<>> * * }}} */ /** * \file mac/pbproc/src/fsm_tx_data.c * \brief FSM TX data part. * \ingroup mac_pbproc */ #include "common/std.h" #include "inc/context.h" #include "inc/fc.h" #include "inc/fsm_top.h" #include "inc/fsm_handle_fc.h" #include "inc/prep_mpdu.h" #include "inc/pbproc.h" #include "inc/fsm_tx_data.h" void pbproc_ftda_init (pbproc_t *ctx) { dbg_assert (ctx); ctx->fsm.states[PBPROC_FSM_STATE_TX_WAIT_ACCESS_CONF].rx_fc_cb = CALLBACK (pbproc_ftda__tx_wait_access_conf__rx_fc); ctx->fsm.states[PBPROC_FSM_STATE_TX_WAIT_ACCESS_CONF].access_cb = CALLBACK (pbproc_ftda__tx_wait_access_conf__access); ctx->fsm.states[PBPROC_FSM_STATE_TX_WAIT_ACCESS_CONF].access_conf_cb = CALLBACK (pbproc_ftda__tx_wait_access_conf__access_conf); ctx->fsm.states[PBPROC_FSM_STATE_TX_WAIT_SACKD].rx_fc_cb = CALLBACK (pbproc_ftda__tx_wait_sackd__rx_fc); ctx->fsm.states[PBPROC_FSM_STATE_TX_WAIT_SACKD].access_cb = CALLBACK (pbproc_ftda__tx_wait_sackd__access); ctx->fsm.states[PBPROC_FSM_STATE_TX_WAIT_TX_END].pbdma_cb = CALLBACK (pbproc_ftda__tx_wait_tx_end__pbdma); ctx->fsm.states[PBPROC_FSM_STATE_TX_BURST].access_conf_cb = CALLBACK (pbproc_ftda__tx_burst__access_conf); } void ARCH_ILRAM pbproc_ftda__handle (pbproc_t *ctx) { pbproc_prep_mpdu_single_t *mpdu = ctx->prep_mpdu.current; dbg_claim (ctx); if (PHY_FC_MODE_IS_HYBRID (ctx->prep_mpdu.fc_mode)) phy_tx_fc10 (ctx->phy, ctx->prep_mpdu.tx_date, ctx->prep_mpdu.fc10); phy_pbdma_start (ctx->phy, ctx->prep_mpdu.bypass_aes, ctx->prep_mpdu.nek, mpdu->pb_nb_total, ctx->prep_mpdu.wack ? 0 : mpdu->pb_nb_total, &mpdu->main_head->phy_pb, NULL, false); if (mpdu->tmi >= PHY_MOD_ROBO_NB) phy_set_tonemap (ctx->phy, ctx->prep_mpdu.tonemap); phy_tx_param (ctx->phy, ctx->prep_mpdu.fc_mode, ctx->prep_mpdu.phy_combo_params, ctx->prep_mpdu.gil, ctx->prep_mpdu.symb_nb); bool prp = ctx->access.prp && !ctx->prep_mpdu.rts_cts; phy_tx_frame (ctx->phy, ctx->prep_mpdu.tx_date, true, prp, ctx->prep_mpdu.fc_av.words); /* Always program a timer because TX can be canceled due to false alarms, * see maria:#706. */ ca_access_vcs_restart_eifs (ctx->ca, ctx->prep_mpdu.tx_date); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_TX_WAIT_ACCESS_CONF); ctx->stats.tx_data++; if (ctx->prep_mpdu.main_mfs_cmd == MFS_FSM_CMD_RE_SYNC) ctx->stats.tx_data_re_sync++; } void ARCH_ILRAM pbproc_ftda__tx_wait_access_conf__rx_fc (pbproc_t *ctx, u32 rx_date, const pbproc_fc_t *fc_av) { dbg_claim (ctx); /* Update backoff. */ pbproc_backoff_lost (ctx); /* Handle FC. */ pbproc_fhfc_handle_fc (ctx, rx_date, fc_av); /* Now, we have time, cancel transmission. */ pbproc_prep_mpdu_cancel_burst (ctx); ctx->stats.tx_data_cancel++; } void ARCH_ILRAM pbproc_ftda__tx_wait_access_conf__access (pbproc_t *ctx) { dbg_claim (ctx); pbproc_prep_mpdu_cancel_burst (ctx); pbproc_ftop__idle__access (ctx); ctx->stats.prp_lost++; ctx->stats.tx_data_cancel++; } void ARCH_ILRAM pbproc_ftda__tx_wait_access_conf__access_conf (pbproc_t *ctx) { dbg_claim (ctx); /* Start DMA. */ phy_tx_prepare (ctx->phy); /* Chain remaining PB. */ pbproc_prep_mpdu_chain (ctx); uint stats_num_pbs = ctx->prep_mpdu.current->pb_nb_total; if (ctx->prep_mpdu.mpdu_count != 0) { phy_access_timer_cancel (ctx->phy); ca_access_hold (ctx->ca); /* Burst, prepare next frame. */ ctx->access.access_date += ctx->prep_mpdu.flp_tck; ctx->access.duration_tck -= ctx->prep_mpdu.flp_tck; pbproc_prep_mpdu (ctx, ctx->prep_mpdu.main_mfs); dbg_claim (ctx->prep_mpdu.valid); /* Sent it to hardware. DMA are not started or it would damage the * current MPDU. */ PBPROC_TRACE (FTDA_TX_BURST, ctx->prep_mpdu.tx_date, ctx->prep_mpdu.flp_tck, ctx->prep_mpdu.current->pb_nb_total); if (PHY_FC_MODE_IS_HYBRID (ctx->prep_mpdu.fc_mode)) phy_tx_fc10 (ctx->phy, ctx->prep_mpdu.tx_date, ctx->prep_mpdu.fc10); if (ctx->prep_mpdu.current->pb_nb_total) phy_tx_param (ctx->phy, ctx->prep_mpdu.fc_mode, ctx->prep_mpdu.phy_combo_params, ctx->prep_mpdu.gil, ctx->prep_mpdu.symb_nb); else phy_tx_param_short (ctx->phy, ctx->prep_mpdu.fc_mode); phy_tx_frame (ctx->phy, ctx->prep_mpdu.tx_date, true, false, ctx->prep_mpdu.fc_av.words); /* Wait for ACCESS CONF to continue. */ pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_TX_BURST); ctx->stats.tx_data_burst++; } else { if (ctx->prep_mpdu.wack) { /* With ACK. */ ca_backoff_deferred (ctx->ca, phy_access_backoff_slot_count (ctx->phy) - 2); ca_access_vcs_restart_eifs (ctx->ca, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck); phy_rx_activate (ctx->phy, false, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck, true); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_TX_WAIT_SACKD); ctx->stats.tx_data_wack++; } else { /* Without ACK. */ ca_access_hold (ctx->ca); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_TX_WAIT_TX_END); if (ctx->prep_mpdu.main_mfs->beacon) ctx->stats.tx_data_beacon++; else ctx->stats.tx_data_woack++; } ctx->prep_mpdu.main_mfs->stats.num_bursts++; } /* Update link stats. */ ctx->prep_mpdu.main_mfs->stats.num_mpdus++; ctx->prep_mpdu.main_mfs->stats.num_pbs += stats_num_pbs; /* If there is a pending SPOC update and enough time, program it. */ if (ctx->prep_mpdu.ifs_tck >= PHY_PREPARE_DELAY_TCK) pbproc_spoc_schedule_update (ctx, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck - ctx->prep_mpdu.ifs_tck); } static bool ARCH_ILRAM pbproc_ftda_parse_sack (pbproc_t *ctx, const pbproc_fc_t *fc_av, pbproc_fc_ack_data_t *ack_data) { dbg_claim (ack_data); pbproc_prep_mpdu_t *prep = &ctx->prep_mpdu; bool fc_ok; u32 sack_w0 = fc_av->words[0]; if (fc_av && ((sack_w0 & BF_MASKS (PBPROC_FC_SACK_W0, DT_AV, ACCESS, SNID, DTEI)) == (u32) BF_FILL (PBPROC_FC_SACK_W0, (DT_AV, PBPROC_FC_DT_SACK), (ACCESS, false), (SNID, ctx->alloc.snid), (DTEI, prep->stei))) ) { /** FC is for us, parse the data. */ ack_data->sackt[0] = BF_GET (PBPROC_FC_SACK_W0__SACKT0, sack_w0); ack_data->sackt[1] = BF_GET (PBPROC_FC_SACK_W0__SACKT1, sack_w0); ack_data->sackt[2] = BF_GET (PBPROC_FC_SACK_W0__SACKT2, sack_w0); ack_data->sackt[3] = BF_GET (PBPROC_FC_SACK_W0__SACKT3, sack_w0); uint sacki_length = PBPROC_FC_SACK_SACKI_BITS; u32 sacki_last = fc_av->sack.sacki_last; u32 sacki_msb = sacki_last; if (sack_w0 & BF_MASK (PBPROC_FC_SACK_W0__RRTF)) { static const uint rrtl_map_tck[PBPROC_FC_SACK_RRTL_NB] = { 4096, 5120, 6144, 7168, 8192, 10240, 12288, 14336, 16384, 20480, 24576, 28672, 32768, 40960, 49152, 57344 }; ack_data->rrtl_tck = rrtl_map_tck[sacki_msb >> 4]; sacki_msb <<= 4; sacki_length -= 4; } else ack_data->rrtl_tck = 0; if (MAC_LID_IS_PLID (prep->main_mfs->common.lid)) { ack_data->rxwsz = sacki_msb >> 4; sacki_length -= 4; } pbproc_sacki_dec_init (&ack_data->sacki_dec, fc_av->sack.sacki[0], fc_av->sack.sacki[1], sacki_last, sacki_length); if (prep->main_mfs->common.mme) ack_data->mfs_rsp = BF_GET (PBPROC_FC_SACK_W0__MFS_RSP_MGMT, sack_w0); else ack_data->mfs_rsp = BF_GET (PBPROC_FC_SACK_W0__MFS_RSP_DATA, sack_w0); fc_ok = true; } else { /** FC is not for us, it will be ignored. */ fc_ok = false; } return fc_ok; } void ARCH_ILRAM pbproc_ftda__tx_wait_sackd__rx_fc (pbproc_t *ctx, u32 rx_date, const pbproc_fc_t *fc_av) { dbg_claim (ctx); pbproc_fc_ack_data_t ack_data; /* Is it our SACK. */ if (less_mod2p32 (rx_date, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck + MAC_TOLERANCE_TCK) && pbproc_ftda_parse_sack (ctx, fc_av, &ack_data)) { phy_rx_prepare_short (ctx->phy); /* Update MFS parameters and handle SACKD. */ pbproc_prep_mpdu_handle_sack (ctx, &ack_data); /* Next. */ if (!ctx->access.cfp) { ca_backoff_success (ctx->ca); ca_access_vcs_restart (ctx->ca, rx_date + ctx->times.pre_fcs_tck + MAC_CIFS_TCK); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_IDLE); } else { //handle_access (); } ctx->stats.tx_data_wack_ack++; } else { pbproc_fhfc_handle_fc (ctx, rx_date, fc_av); pbproc_prep_mpdu_cancel_burst (ctx); ctx->stats.tx_data_wack_noack++; } } void ARCH_ILRAM pbproc_ftda__tx_wait_sackd__access (pbproc_t *ctx) { dbg_claim (ctx); pbproc_prep_mpdu_cancel_burst (ctx); pbproc_ftop__idle__access (ctx); ctx->stats.tx_data_wack_noack++; } void ARCH_ILRAM pbproc_ftda__tx_wait_tx_end__pbdma (pbproc_t *ctx) { dbg_claim (ctx); uint sack_tck = !ctx->prep_mpdu.main_mfs->beacon ? ctx->times.pre_fcs_tck + MAC_CIFS_TCK : 0; pbproc_prep_mpdu_ack_all_burst (ctx); ca_backoff_success (ctx->ca); ca_access_vcs_restart (ctx->ca, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck + sack_tck); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_IDLE); } void ARCH_ILRAM pbproc_ftda__tx_burst__access_conf (pbproc_t *ctx) { dbg_claim (ctx); phy_access_timer_cancel (ctx->phy); pbproc_prep_mpdu_single_t *mpdu = ctx->prep_mpdu.current; /* Check for burst interruption. */ if (mpdu->pb_nb_total) { phy_pbdma_start (ctx->phy, ctx->prep_mpdu.bypass_aes, ctx->prep_mpdu.nek, mpdu->pb_nb_total, ctx->prep_mpdu.wack ? 0 : mpdu->pb_nb_total, &mpdu->main_head->phy_pb, NULL, false); if (mpdu->tmi >= PHY_MOD_ROBO_NB) phy_set_tonemap (ctx->phy, ctx->prep_mpdu.tonemap); pbproc_ftda__tx_wait_access_conf__access_conf (ctx); } else { /* Always with ACK. */ ca_backoff_deferred (ctx->ca, phy_access_backoff_slot_count (ctx->phy) - 2); ca_access_vcs_restart_eifs (ctx->ca, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck); phy_rx_activate (ctx->phy, false, ctx->prep_mpdu.tx_date + ctx->prep_mpdu.flp_tck, true); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_TX_WAIT_SACKD); ctx->stats.tx_data_wack++; ctx->stats.tx_data_burst_interrupted++; /* Update link stats. */ ctx->prep_mpdu.main_mfs->stats.num_bursts++; ctx->prep_mpdu.main_mfs->stats.num_mpdus++; } }