/* Cesar project {{{ * * Copyright (C) 2007 Spidcom * * <<>> * * }}} */ /** * \file mac/pbproc/src/fsm_rx_data.c * \brief FSM RX data part. * \ingroup mac_pbproc */ #include "common/std.h" #include "hal/gpio/gpio.h" #include "inc/context.h" #include "inc/fc.h" #include "inc/fsm_rx_data.h" #include "inc/fsm_handle_fc.h" #include "inc/fsm_top.h" #include "hal/phy/pbdma.h" /** * Restart VCS for next MPDU and go to next state. * \param ctx pbproc context */ static void pbproc_frda_next (pbproc_t *ctx); /** * Anticipate SACK sending for hybrid mode. * \param ctx pbproc context */ static void pbproc_frda_send_sack_anticip (pbproc_t *ctx); /** * Send SACK from current SACKD. * \param ctx pbproc context */ static void pbproc_frda_send_sack (pbproc_t *ctx); /** * Initialise SACKD, reset if not part of last burst. * \param ctx pbproc context * \param sof received MPDU SOF */ static void pbproc_frda_sackd_init (pbproc_t *ctx, const pbproc_fc_sof_t *sof); /** * Give up reception and eventually prepare a SACK to signal the problem. * \param ctx pbproc context * \param rx_date start of preamble date * \param sof SOF FC * \param wack with acknowledge * \param sacki_uniform uniform SACK information * * Go to next FSM state. */ static void pbproc_frda_error (pbproc_t *ctx, u32 rx_date, const pbproc_fc_sof_t *sof, bool wack, uint sacki_uniform); /** * Unchain received PB, and give them to the upper layer. * \param ctx pbproc context */ static void pbproc_frda_commit (pbproc_t *ctx); void pbproc_frda_init (pbproc_t *ctx) { dbg_assert (ctx); ctx->fsm.states[PBPROC_FSM_STATE_RX_DATA_WACK].pbdma_cb = pbproc_frda__rx_data_wack__pbdma; ctx->fsm.states[PBPROC_FSM_STATE_RX_DATA_WACK_LAST_PB].pbdma_cb = pbproc_frda__rx_data_wack_last_pb__pbdma; ctx->fsm.states[PBPROC_FSM_STATE_RX_DATA_WOACK].pbdma_cb = pbproc_frda__rx_data_woack__pbdma; ctx->fsm.states[PBPROC_FSM_STATE_RX_BURST].rx_fc_cb = pbproc_frda__rx_burst__rx_fc; ctx->fsm.states[PBPROC_FSM_STATE_RX_BURST].access_cb = pbproc_frda__rx_burst__access; } void ARCH_ILRAM pbproc_frda__handle (pbproc_t *ctx, u32 rx_date, const pbproc_fc_sof_t *sof) { bool wack, unassociated; dbg_claim (ctx); dbg_claim (sof); /* TODO features. */ dbg_claim (sof->pbsz == false); dbg_claim (sof->num_sym != 0); dbg_claim (sof->bbf == false); dbg_claim (sof->dcppcf == false); dbg_claim (sof->rsr == false); /* Update stats. */ ctx->stats.rx_data++; /* Unassociated? */ unassociated = sof->stei == 0 || sof->mnbf; /* With ACK? */ wack = sof->snid == ctx->alloc.snid && sof->dtei == ctx->config->tei; /* Ignore frames ending after the allocation. */ uint fl_tck = MAC_FL_TO_TCK (sof->fl_av); if (less_mod2p32 (ctx->alloc.end_date, rx_date + 2 * ctx->times.pre_fcs_tck + fl_tck + MAC_CIFS_TCK)) { ctx->stats.rx_out_of_alloc++; pbproc_frda_error (ctx, rx_date, sof, false, PBPROC_FC_SACKI_UNIFORM_ALL_ERROR); return; } /* Burst? */ ctx->recv_mpdu.mpdu_cnt = sof->mpdu_cnt; /* Get tonemap. */ uint tmi = sof->tmi_av; tonemap_t *tm; uint rifs_tck; if (tmi < PHY_MOD_ROBO_NB) { tm = &ctx->config->tonemask_info.tonemap_robo[tmi]; rifs_tck = MAC_RIFS_DEFAULT_TCK; } else if (sof->mcf || unassociated) { pbproc_frda_error (ctx, rx_date, sof, wack, PBPROC_FC_SACKI_UNIFORM_TMI_DEFAULT_ROBO); return; } else { dbg_claim (wack); dbg_claim (tmi < TONEMAP_INDEX_NB); sta_t *sta = mac_store_sta_get (ctx->store, sof->stei); if (sta) { tm = sta->rx_tonemaps->tm[tmi]; blk_release (sta); rifs_tck = MAC_RIFS_SPC_ANY_TCK; if (!tm) { pbproc_frda_error (ctx, rx_date, sof, true, PBPROC_FC_SACKI_UNIFORM_TMI_RESTART); return; } } else { pbproc_frda_error (ctx, rx_date, sof, true, PBPROC_FC_SACKI_UNIFORM_TMI_RESTART); return; } } /* Use BIFS for bursts. */ if (ctx->recv_mpdu.mpdu_cnt) rifs_tck = MAC_BIFS_TCK; /* Compute symbol and PB number. */ uint symb_nb; if (sof->num_sym == 1) symb_nb = 1; else if (sof->num_sym == 2) symb_nb = 2; else symb_nb = ((fl_tck - rifs_tck - 2 * MAC_DX567_TCK) / ctx->symbol_tck[tm->gil] + 2); uint bits_per_pb = tm->bits_per_pb[PHY_PB_SIZE_520]; /* To take into account that there is only one segment in the last * symbol, here is the trick: */ uint pb_nb = ((symb_nb - 1) * tm->bits_per_symbol + bits_per_pb) / bits_per_pb; /* Prepare decryption key. */ bool bypass_aes = true; u32 *key = NULL; bool nek_ok = false; do { if (sof->eks < MAC_EKS_NB) { if (unassociated) break; sta_t *sta = mac_store_sta_get (ctx->store, sof->stei); mac_nek_t (*nek)[2]; if (sta) { nek = sta->nek; blk_release (sta); if (!nek) nek = &ctx->config->nek; } else nek = &ctx->config->nek; if ((*nek)[0].eks == sof->eks) key = (*nek)[0].nek_dec; else if ((*nek)[1].eks == sof->eks) key = (*nek)[1].nek_dec; else break; bypass_aes = false; nek_ok = true; } else if (sof->eks == MAC_EKS_CLEAR) { nek_ok = true; } } while (0); /* Acknowledge multicast frame even if it can not be decrypted. */ if (!nek_ok && !(wack && sof->mcf)) { pbproc_frda_error (ctx, rx_date, sof, wack, PBPROC_FC_SACKI_UNIFORM_NEK_ERROR); return; } /* Check for PB Pool shortage. */ if (ctx->rx_pool_size < 1 + ctx->chandata_nb + pb_nb) { ctx->stats.rx_pool_shortage++; pbproc_frda_error (ctx, rx_date, sof, wack, PBPROC_FC_SACKI_UNIFORM_ALL_ERROR); return; } /* First thing to do: unblock the hardware. */ if (tmi >= PHY_MOD_ROBO_NB) phy_set_tonemap (ctx->phy, tm->tmdma_desc_head); phy_rx_prepare (ctx->phy, pb_nb, tm->phy_combo_params[PHY_PB_SIZE_520], tm->gil, symb_nb, tm->tcc_halfit); /* Save received MPDU parameters. */ ctx->recv_mpdu.rx_params.preamble_ntb = rx_date + ctx->config->ntb_offset_tck; ctx->recv_mpdu.rx_params.tei = sof->stei; ctx->recv_mpdu.rx_params.lid = sof->lid; ctx->recv_mpdu.rx_params.snid = sof->snid; ctx->recv_mpdu.rx_params.bcast = sof->mcf; ctx->recv_mpdu.rx_params.cfp = sof->cfs; ctx->recv_mpdu.rx_params.multi_net_bcast = sof->mnbf; ctx->recv_mpdu.rx_params.sound = false; ctx->recv_mpdu.rx_params.eks = sof->eks; ctx->recv_mpdu.rx_params.pending_seg_nb = sof->ppb; ctx->recv_mpdu.rx_params.ble = sof->ble; ctx->recv_mpdu.rx_params.pb_size = PHY_PB_SIZE_520; ctx->recv_mpdu.rx_params.tmi_av = sof->tmi_av; ctx->recv_mpdu.rx_params.mpdu_cnt = sof->mpdu_cnt; ctx->recv_mpdu.rx_params.bdf = sof->bdf; ctx->recv_mpdu.rx_params.hp10df = sof->hp10df; ctx->recv_mpdu.rx_params.hp11df = sof->hp11df; ctx->recv_mpdu.rx_params.mfs_cmd_data = sof->mfs_cmd_data; ctx->recv_mpdu.rx_params.mfs_cmd_mme = sof->mfs_cmd_mgmt; ctx->recv_mpdu.pb_nb = pb_nb; ctx->recv_mpdu.ack_date = rx_date + ctx->times.pre_fcs_tck + fl_tck; ctx->recv_mpdu.drop = !nek_ok; /* Keep a block to store received MPDU parameters. */ pb_t *pool = ctx->rx_pool_head; ctx->recv_mpdu.rx_desc = (pbproc_rx_desc_t *) pool; pool = pool->next; /* Prepare channel data blocks and give them to PB DMA. */ if (ctx->chandata_nb && nek_ok && !unassociated) { uint i; /* Take from RX pool. */ ctx->recv_mpdu.chandata_head = pool; ctx->recv_mpdu.chandata_nb = ctx->chandata_nb; /* Fill configuration. */ for (i = 0; i < ctx->chandata_nb; i++) { pool->phy_pb.chandata.conf = ctx->chandata_conf[i]; pool = pool->next; } } else { ctx->recv_mpdu.chandata_head = NULL; ctx->recv_mpdu.chandata_nb = 0; } /* If there is more than a few PB, program next access using PB DMA before * last symbol reception to prepare SACKD. */ bool sack_last_pb = wack && pb_nb > PBPROC_SACKD_ANTICIP_PB_THRESHOLD; uint pb_it; if (sack_last_pb) pb_it = pb_nb - PBPROC_SACKD_ANTICIP_PB_NB; else if (ctx->recv_mpdu.chandata_nb) pb_it = 0; else pb_it = pb_nb; ctx->recv_mpdu.pb_head = pool; phy_pbdma_start (ctx->phy, bypass_aes, key, pb_nb, pb_nb, pb_it, &pool->phy_pb, &ctx->recv_mpdu.chandata_head->phy_pb.chandata, true); if (wack) { /* Initialise SACKD. */ pbproc_frda_sackd_init (ctx, sof); /* Wait. */ pbproc_fsm_change_state (ctx, sack_last_pb ? PBPROC_FSM_STATE_RX_DATA_WACK : PBPROC_FSM_STATE_RX_DATA_WACK_LAST_PB); ctx->stats.rx_data_wack++; } else { /* Wait end of frame. */ pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_RX_DATA_WOACK); ctx->stats.rx_data_woack++; } /* Signal received traffic. */ GPIO_SET (LED_TRAFFIC, 1); } void ARCH_ILRAM pbproc_frda__rx_data_wack__pbdma (pbproc_t *ctx) { dbg_claim (ctx); /* Prepare SACKD. */ ctx->recv_mpdu.sackd.any_pb_crc_error = ctx->recv_mpdu.sackd.any_pb_crc_error || ctx->pbdma_status.pb_crc_error; pbproc_sacki_enc_process (&ctx->recv_mpdu.sackd.sacki_enc, phy_pbdma_get_crc_bitmap (ctx->phy), ctx->recv_mpdu.pb_nb - PBPROC_SACKD_ANTICIP_PB_NB, false); /* Program next access using PB DMA after last PB reception. */ uint pb_nb = ctx->recv_mpdu.pb_nb; phy_pbdma_update (ctx->phy, pb_nb, ctx->recv_mpdu.chandata_nb ? 0 : pb_nb); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_RX_DATA_WACK_LAST_PB); } void ARCH_ILRAM pbproc_frda__rx_data_wack_last_pb__pbdma (pbproc_t *ctx) { dbg_claim (ctx); /* Prepare FC 1.0 in advance. */ if (ctx->recv_mpdu.mpdu_cnt == 0) pbproc_frda_send_sack_anticip (ctx); /* Prepare SACKD. */ ctx->recv_mpdu.sackd.any_pb_crc_error = ctx->recv_mpdu.sackd.any_pb_crc_error || ctx->pbdma_status.pb_crc_error; if (!ctx->recv_mpdu.sackd.any_pb_crc_error) { /* All OK, use uniform encoding. */ ctx->recv_mpdu.sackd.sackt[ctx->recv_mpdu.mpdu_cnt] = PBPROC_FC_SACKT_UNIFORM; pbproc_sacki_enc_rewind (&ctx->recv_mpdu.sackd.sacki_enc); u32 code = PBPROC_FC_SACKI_UNIFORM_ALL_OK; pbproc_sacki_enc_copy (&ctx->recv_mpdu.sackd.sacki_enc, &code, PBPROC_FC_SACKI_UNIFORM_SIZE); PBPROC_TRACE (FRDA_SACK_UNIFORM, code); } else { /* Finish compression to determine smallest SACKD. */ const volatile u32 *crc_bmp = phy_pbdma_get_crc_bitmap (ctx->phy); pbproc_sacki_enc_process (&ctx->recv_mpdu.sackd.sacki_enc, crc_bmp, ctx->recv_mpdu.pb_nb, true); if (pbproc_sacki_enc_better (&ctx->recv_mpdu.sackd.sacki_enc, ctx->recv_mpdu.pb_nb)) { /* Compressed is better. */ ctx->recv_mpdu.sackd.sackt[ctx->recv_mpdu.mpdu_cnt] = PBPROC_FC_SACKT_MIXED_COMPRESSED; PBPROC_TRACE (FRDA_SACK_MIXED_COMPRESSED); } else { /* Non compressed is better. */ ctx->recv_mpdu.sackd.sackt[ctx->recv_mpdu.mpdu_cnt] = PBPROC_FC_SACKT_MIXED; pbproc_sacki_enc_rewind (&ctx->recv_mpdu.sackd.sacki_enc); pbproc_sacki_enc_copy (&ctx->recv_mpdu.sackd.sacki_enc, crc_bmp, ctx->recv_mpdu.pb_nb); PBPROC_TRACE (FRDA_SACK_MIXED); } } /* TODO: Handle bidir. */ /* Send SACK if not a burst MPDU. */ if (ctx->recv_mpdu.mpdu_cnt == 0) pbproc_frda_send_sack (ctx); /* Unchain and give received frame to upper layer. */ pbproc_frda_commit (ctx); /* Prepare for next. */ pbproc_frda_next (ctx); } void ARCH_ILRAM pbproc_frda__rx_data_woack__pbdma (pbproc_t *ctx) { dbg_claim (ctx); /* Unchain and give received frame to upper layer. */ pbproc_frda_commit (ctx); /* Prepare for next. */ pbproc_frda_next (ctx); } static void ARCH_ILRAM pbproc_frda_commit (pbproc_t *ctx) { dbg_claim (ctx); if (!ctx->recv_mpdu.drop) { pbproc_rx_desc_t *rx = ctx->recv_mpdu.rx_desc; /* Unchain used PB. */ pb_t *tail = PARENT_OF (pb_t, phy_pb, phy_pbdma_get_tail (ctx->phy)); slist_slice (ctx->rx_pool_, tail, 1 + ctx->recv_mpdu.chandata_nb + ctx->recv_mpdu.pb_nb, paste_size); ctx->recv_mpdu.pb_tail = tail; /* Received frame is given to upper layer in DSR. */ rx->rx->params = ctx->recv_mpdu.rx_params; rx->rx->mfs = NULL; rx->rx->mfs_mme = NULL; rx->rx->pb_first = ctx->recv_mpdu.pb_head; rx->rx->pb_last = ctx->recv_mpdu.pb_tail; rx->rx->pb_nb = ctx->recv_mpdu.pb_nb; rx->rx->chandata_first = ctx->recv_mpdu.chandata_head; rx->rx->chandata_nb = ctx->recv_mpdu.chandata_nb; slist_push_back (ctx->commit.rx_, rx); pbproc_fsm_schedule_deferred (ctx); } dbg_invalid_ptr (ctx->recv_mpdu.pb_head); dbg_invalid_ptr (ctx->recv_mpdu.pb_tail); dbg_invalid_ptr (ctx->recv_mpdu.chandata_head); } void ARCH_ILRAM pbproc_frda__rx_burst__rx_fc (pbproc_t *ctx, u32 rx_date, const pbproc_fc_t *fc_av) { dbg_claim (ctx); /* Easy... */ pbproc_fhfc_handle_fc (ctx, rx_date, fc_av); } void ARCH_ILRAM pbproc_frda__rx_burst__access (pbproc_t *ctx) { dbg_claim (ctx); /* Handle access. */ pbproc_ftop__idle__access (ctx); /* Update stats. */ ctx->stats.rx_data_burst_stop++; } static void ARCH_ILRAM pbproc_frda_sackd_init (pbproc_t *ctx, const pbproc_fc_sof_t *sof) { dbg_claim (ctx); dbg_claim (sof); /* Clear current SACKD if this frame is not part of the previous burst. */ bool reinit = false; pbproc_sackd_t *sackd = &ctx->recv_mpdu.sackd; if (!sackd->valid || sackd->tei != sof->stei || sackd->lid != sof->lid || sackd->bcast != sof->mcf || sackd->burst_cnt != sof->burst_cnt || sackd->last_mpdu_cnt <= sof->mpdu_cnt) { sackd->sackt[0] = PBPROC_FC_SACKT_NOT_RECEIVED; sackd->sackt[1] = PBPROC_FC_SACKT_NOT_RECEIVED; sackd->sackt[2] = PBPROC_FC_SACKT_NOT_RECEIVED; sackd->sackt[3] = PBPROC_FC_SACKT_NOT_RECEIVED; reinit = true; } /* Record SACKD parameters. */ sackd->valid = true; sackd->tei = sof->stei; sackd->lid = sof->lid; sackd->bcast = sof->mcf; sackd->cfp = sof->cfs; sackd->burst_cnt = sof->burst_cnt; sackd->last_mpdu_cnt = sof->mpdu_cnt; /* Initialise SACKD preparation. */ sackd->any_pb_crc_error = false; pbproc_sacki_enc_init (&sackd->sacki_enc, PBPROC_FC_SACK_SACKI_BITS - (MAC_LID_IS_PLID (sof->lid) ? 4 : 0), reinit); } static void ARCH_ILRAM pbproc_frda_error (pbproc_t *ctx, u32 rx_date, const pbproc_fc_sof_t *sof, bool wack, uint sacki_uniform) { dbg_claim (ctx); dbg_claim (sof); dbg_claim (sacki_uniform < PBPROC_FC_SACKI_UNIFORM_NB); /* First thing to do: unblock the hardware. */ phy_rx_prepare_short (ctx->phy); /* ACK date. */ ctx->recv_mpdu.ack_date = rx_date + ctx->times.pre_fcs_tck + MAC_FL_TO_TCK (sof->fl_av); /* With acknowledge? */ if (wack) { /* Prepare SACKD. */ pbproc_frda_sackd_init (ctx, sof); ctx->recv_mpdu.sackd.sackt[ctx->recv_mpdu.mpdu_cnt] = PBPROC_FC_SACKT_UNIFORM; pbproc_sacki_enc_copy (&ctx->recv_mpdu.sackd.sacki_enc, &sacki_uniform, PBPROC_FC_SACKI_UNIFORM_SIZE); PBPROC_TRACE (FRDA_SACK_UNIFORM, sacki_uniform); /* Send SACK if not a burst MPDU. */ if (ctx->recv_mpdu.mpdu_cnt == 0) { pbproc_frda_send_sack_anticip (ctx); pbproc_frda_send_sack (ctx); } } /* Prepare for next. */ pbproc_frda_next (ctx); /* Update stats. */ ctx->stats.rx_data_error++; } static void ARCH_ILRAM pbproc_frda_send_sack_anticip (pbproc_t *ctx) { dbg_claim (ctx); if (ctx->alloc.hybrid) phy_tx_fc10 (ctx->phy, ctx->recv_mpdu.ack_date, 0); } static void ARCH_ILRAM pbproc_frda_send_sack (pbproc_t *ctx) { dbg_claim (ctx); dbg_claim (ctx->recv_mpdu.sackd.valid); /* SACK FC. */ pbproc_fc_t fc; fc.words[0] = BF_FILL ( PBPROC_FC_SACK_W0, (DT_AV, PBPROC_FC_DT_SACK), (ACCESS, false), (SNID, ctx->alloc.snid), (DTEI, ctx->recv_mpdu.sackd.tei), (CFS, ctx->recv_mpdu.sackd.cfp), (BDF, false), /* TODO */ (SVN, 0), (RRTF, 0), (MFS_RSP_DATA, MFS_FSM_RSP_ACK), (MFS_RSP_MGMT, MFS_FSM_RSP_ACK), (SACKT3, ctx->recv_mpdu.sackd.sackt[3]), (SACKT2, ctx->recv_mpdu.sackd.sackt[2]), (SACKT1, ctx->recv_mpdu.sackd.sackt[1]), (SACKT0, ctx->recv_mpdu.sackd.sackt[0])); fc.words[1] = BF_FILL ( PBPROC_FC_SACK_W1, (SACKI0, ctx->recv_mpdu.sackd.sacki_enc.si[0])); fc.words[2] = BF_FILL ( PBPROC_FC_SACK_W2, (SACKI1, ctx->recv_mpdu.sackd.sacki_enc.si[1])); fc.words[3] = BF_FILL ( PBPROC_FC_SACK_W3, (SACKI2, ctx->recv_mpdu.sackd.sacki_enc.si[2])); /* RX window size. */ if (MAC_LID_IS_PLID (ctx->recv_mpdu.sackd.lid)) { u8 window_size_encoded = MFS_RX_WINDOW_SIZE_DATA_DEFAULT_ENCODED; mfs_rx_t *mfs = NULL; if (ctx->recv_mpdu.sackd.tei) mfs = mac_store_mfs_get_rx (ctx->store, ctx->recv_mpdu.sackd.bcast, false, ctx->recv_mpdu.sackd.lid, ctx->recv_mpdu.sackd.tei); if (mfs) { window_size_encoded = mfs->window_size_encoded; blk_release (mfs); } fc.words[3] |= BF_SHIFT (PBPROC_FC_SACK_W3__RXWSZ_NO_RRTL, window_size_encoded); } /* Send it. */ phy_tx_param_short (ctx->phy, PHY_FC_MODE (ctx->alloc.hybrid, ctx->config->fc_symbols_nb)); phy_tx_frame (ctx->phy, ctx->recv_mpdu.ack_date, false, false, fc.words); /* Ensure SACKD will be cleared. */ /* TODO: keep it for RSR. */ ctx->recv_mpdu.sackd.valid = false; } static void ARCH_ILRAM pbproc_frda_next (pbproc_t *ctx) { dbg_claim (ctx); /* Burst? */ if (ctx->recv_mpdu.mpdu_cnt == 0) { /* Prepare for next MPDU. */ ca_access_vcs_restart (ctx->ca, ctx->recv_mpdu.ack_date, ctx->times.pre_fcs_tck + MAC_CIFS_TCK, PBPROC_ANTICIP_TCK, false); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_IDLE); } else { /* Wait for next MPDU part of the burst. */ ca_access_vcs_restart (ctx->ca, ctx->recv_mpdu.ack_date, ctx->times.eifs_tck, PBPROC_ANTICIP_TCK, true); phy_rx_activate (ctx->phy, false, ctx->recv_mpdu.ack_date, true); pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_RX_BURST); } }