/* Cesar project {{{ * * Copyright (C) 2009 Spidcom * * <<>> * * }}} */ /** * \file mac/pbproc/src/fsm_rx_sound.c * \brief FSM RX SOUND part. * \ingroup mac_pbproc */ #include "common/std.h" #include "inc/context.h" #include "inc/fc.h" #include "inc/fsm_rx_sound.h" #include "hal/phy/pbdma.h" /** * Restart VCS for next MPDU. * \param ctx pbproc context */ static void pbproc_frso_vcs_restart (pbproc_t *ctx); void pbproc_frso_init (pbproc_t *ctx) { dbg_assert (ctx); ctx->fsm.states[PBPROC_FSM_STATE_RX_SOUND].pbdma_cb = CALLBACK (pbproc_frso__rx_sound__pbdma); } /** * Decrement sound counter and check if sound transmission is completed. * \param sta station context * \param src received sound reason code * \param int_group interval group in which the frame is received * \return true if sound is completed */ static bool pbproc_frso_sound_complete (sta_t *sta, uint src, int int_group) { if (sta) { u8 zero_counter = 0; u8 *counter; tonemaps_t *tms = sta->rx_tonemaps; if (src != TONEMAP_SRC_INTERVAL_UNAVAILABLE && src != TONEMAP_SRC_INTERVAL_UNUSABLE) counter = &tms->sound_frame_counter; else { if (int_group == PBPROC_NO_INT_MATCH) counter = &zero_counter; else counter = &tms->group_interval_sound_frame_counter[int_group]; } if (*counter) (*counter)--; bool complete = *counter == 0; return complete; } else return true; } void ARCH_ILRAM_PRIO (2) pbproc_frso__handle (pbproc_t *ctx, u32 rx_date, const pbproc_fc_sound_t *sound) { dbg_claim (ctx); dbg_claim (sound); /* Update stats. */ ctx->stats.rx_sound++; /* Get tonemap. */ tonemap_t *tm; phy_mod_t mod = sound->pbsz ? PHY_MOD_MINI_ROBO : PHY_MOD_ROBO; tm = &ctx->config->tonemask_info.tonemap_robo[mod]; /* Compute symbol number. */ phy_pb_size_t pb_size = sound->pbsz ? PHY_PB_SIZE_136 : PHY_PB_SIZE_520; uint fl_tck = MAC_FL_TO_TCK (sound->fl_av); uint bits_per_pb = tm->bits_per_pb[pb_size]; uint symb_nb = (1 * bits_per_pb + tm->bits_per_symbol - 1) / tm->bits_per_symbol; /* Save early needed received MPDU parameters. */ ctx->recv_mpdu.rx_params.mpdu_cnt = sound->mpdu_cnt; ctx->recv_mpdu.ack_date = rx_date + ctx->times.pre_fcs_tck + fl_tck; /* Ignore frames ending after the allocation, handle pool exhaustion. */ bool pool_shortage = ctx->rx_pool_size < 1 + ctx->chandata_nb; if (pool_shortage || (!ctx->alloc.merge && less_mod2p32 (ctx->alloc.end_date + MAC_TOLERANCE_TCK, rx_date + 2 * ctx->times.pre_fcs_tck + fl_tck))) { if (pool_shortage) ctx->stats.rx_pool_shortage++; else ctx->stats.rx_out_of_alloc++; /* Unblock hardware. */ phy_rx_prepare_short (ctx->phy); /* Prepare for next. */ pbproc_frso_vcs_restart (ctx); /* Return to IDLE state. */ pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_IDLE); /* Stop now. */ return; } /* May skip channel data reception. */ bool chandata = ctx->chandata_nb != 0; /* Default to true to keep the sender to flood with SOUND frames. */ bool sound_complete = true; int int_group = PBPROC_NO_INT_MATCH; if (chandata) { /* Unblock hardware. */ phy_rx_prepare_sound (ctx->phy, 1, tm->phy_combo_params[pb_size], tm->gil, symb_nb); sta_t *sta = mac_store_sta_get_noref (ctx->store, sound->stei); /* Match the frame on the corresponding interval. */ int_group = pbproc_get_interval_group (sta, rx_date + ctx->times.pre_fcs_tck - ctx->alloc.beacon_period_start_date, fl_tck); /* Is sound completed? */ sound_complete = pbproc_frso_sound_complete (sta, sound->src, int_group); /* Update stats. */ if (sound_complete) ctx->stats.rx_sound_complete++; } else { /* Skip frame. */ phy_rx_prepare_short (ctx->phy); /* Update stats. */ ctx->stats.rx_sound_drop++; } /* Save received MPDU parameters. */ ctx->recv_mpdu.rx_params.preamble_ntb = rx_date + ctx->config->ntb_offset_tck; ctx->recv_mpdu.rx_params.beacon_period_start_ntb = ctx->alloc.beacon_period_start_date + ctx->config->ntb_offset_tck; ctx->recv_mpdu.rx_params.fl_tck = fl_tck; ctx->recv_mpdu.rx_params.tei = sound->stei; ctx->recv_mpdu.rx_params.lid = sound->lid; ctx->recv_mpdu.rx_params.snid = sound->snid; ctx->recv_mpdu.rx_params.bcast = false; ctx->recv_mpdu.rx_params.cfp = sound->cfs; ctx->recv_mpdu.rx_params.multi_net_bcast = false; ctx->recv_mpdu.rx_params.sound = true; ctx->recv_mpdu.rx_params.eks = MAC_EKS_CLEAR; ctx->recv_mpdu.rx_params.pending_seg_info = sound->ppb; ctx->recv_mpdu.rx_params.ble = 0; ctx->recv_mpdu.rx_params.pb_size = pb_size; ctx->recv_mpdu.rx_params.tmi_av = mod; ctx->recv_mpdu.rx_params.bdf = sound->bdf; ctx->recv_mpdu.rx_params.hp10df = false; ctx->recv_mpdu.rx_params.hp11df = false; ctx->recv_mpdu.rx_params.mfs_cmd_data = MFS_FSM_CMD_NOP; ctx->recv_mpdu.rx_params.mfs_cmd_mme = MFS_FSM_CMD_NOP; ctx->recv_mpdu.rx_params.sound_src = sound->src; ctx->recv_mpdu.rx_params.sound_req_tm = sound->req_tm; ctx->recv_mpdu.rx_params.sound_complete = sound_complete; ctx->recv_mpdu.rx_params.interval_group = int_group; ctx->recv_mpdu.pb_nb = 0; /* Keep a block to store received MPDU parameters. */ dbg_assert (ctx->rx_pool_size >= 1); pb_t *last = ctx->rx_pool_head; ctx->recv_mpdu.rx_desc = (pbproc_rx_desc_t *) last; /* Prepare channel data blocks and give them to PB DMA. */ if (chandata) { uint i; /* Take from RX pool. */ ctx->recv_mpdu.chandata_head = last->next; ctx->recv_mpdu.chandata_nb = ctx->chandata_nb; /* Fill configuration. */ for (i = 0; i < ctx->chandata_nb; i++) { last = last->next; last->phy_pb.chandata.conf = ctx->chandata_conf[i]; } /* Start PB DMA. */ phy_pbdma_start_chandata ( ctx->phy, &ctx->recv_mpdu.chandata_head->phy_pb.chandata); /* Unchain used blocks. */ slist_slice (ctx->rx_pool_, last, 1 + ctx->recv_mpdu.chandata_nb, paste_size); } else { ctx->recv_mpdu.chandata_head = NULL; ctx->recv_mpdu.chandata_nb = 0; } /* Program VCS. */ pbproc_frso_vcs_restart (ctx); /* Wait end of frame if receiving channel data, else continue now. */ if (chandata) pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_RX_SOUND); else pbproc_frso__rx_sound__pbdma (ctx); } void ARCH_ILRAM_PRIO (2) pbproc_frso__rx_sound__pbdma (pbproc_t *ctx) { dbg_claim (ctx); const bool hybrid = ctx->alloc.hybrid; if (ctx->recv_mpdu.rx_params.mpdu_cnt == 0) { /* Anticipate FC 1.0. */ if (hybrid) phy_tx_fc10 (ctx->phy, ctx->recv_mpdu.ack_date, 0); /* Prepare SOUND acknowledgment. */ pbproc_fc_t fc; fc.words[0] = BF_FILL ( PBPROC_FC_SOUND_W0, (DT_AV, PBPROC_FC_DT_SOUND), (ACCESS, false), (SNID, ctx->alloc.snid), (STEI, ctx->config->tei), (DTEI, ctx->recv_mpdu.rx_params.tei), (LID, ctx->recv_mpdu.rx_params.lid)); fc.words[1] = BF_FILL ( PBPROC_FC_SOUND_W1, (CFS, ctx->recv_mpdu.rx_params.cfp), (PBSZ, false), (BDF, ctx->detect.beacon_detected), (SAF, true), (SCF, ctx->recv_mpdu.rx_params.sound_complete), (REQ_TM, 0), (FL_AV, 0), (MPDU_CNT, 0), (RESERVED0, 0), (PPB, 0)); fc.words[2] = BF_FILL ( PBPROC_FC_SOUND_W2, (SRC, 0), (RESERVED1, 0)); fc.words[3] = BF_FILL ( PBPROC_FC_SOUND_W3, (RESERVED2, 0)); /* Send it. */ phy_tx_param_short (ctx->phy, PHY_FC_MODE (hybrid, ctx->config->fc_symbols_nb)); phy_tx_frame (ctx->phy, ctx->recv_mpdu.ack_date, false, true, fc.words); } /* Received frame is given to upper layer in DSR. */ if (ctx->recv_mpdu.chandata_nb) { pbproc_rx_desc_t *rx = ctx->recv_mpdu.rx_desc; rx->rx->params = ctx->recv_mpdu.rx_params; rx->rx->mfs = NULL; rx->rx->mfs_mme = NULL; rx->rx->pb_first = NULL; rx->rx->pb_last = NULL; rx->rx->pb_nb = 0; 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.chandata_head); } /* Return to IDLE. */ pbproc_fsm_change_state (ctx, PBPROC_FSM_STATE_IDLE); } static void ARCH_ILRAM_PRIO (2) pbproc_frso_vcs_restart (pbproc_t *ctx) { dbg_claim (ctx); /* Restart VCS. */ if (ctx->recv_mpdu.rx_params.mpdu_cnt == 0) { ca_access_vcs_restart (ctx->ca, ctx->recv_mpdu.ack_date + ctx->times.pre_fcs_tck + MAC_CIFS_TCK); } else { ca_access_vcs_restart_eifs (ctx->ca, ctx->recv_mpdu.ack_date); phy_rx_activate (ctx->phy, false, ctx->recv_mpdu.ack_date + PHY_RX_ACTIVATE_DELAY_AFTER_BURST_TCK, true); } }