/* Cesar project {{{ * * Copyright (C) 2012 Spidcom * * <<>> * * }}} */ /** * \file mac/sar/src/rx.c * \brief Reassembly frames. * \ingroup mac_sar */ #include "common/std.h" #include "mac/common/mfs.h" #include "mac/common/timings.h" #include "mac/sar/inc/context.h" #include "mac/sar/inc/sar.h" #include "mac/sar/inc/pb_stats.h" #include "mac/sar/inc/rx.h" #include "mac/sar/inc/brg.h" #include "config.h" /** Define the JOB length for the second job to avoid bridgedma bug see * maria:#905. */ #define SAR_BRGBUG_SECOND_JOB_LENGTH 16 /** * Store missing PBs stats in the MFS. * \param mfs the MFS to update the stats. * \param pb_missing the missing PB counter. */ #define sar_mfs_stats_missing_pbs(mfs, pb_missing) \ do { \ (mfs)->stats.num_segs_missed += (pb_missing); \ (pb_missing) = 0; \ } while (0) /** * Compute the gap between two SSNs. * \param a the first SSN, * \param b the Second SSN. * \return the GAP. * * a must be after b. */ inline uint sar_ssn_gap (u16 a, u16 b) { return (u16) (a - b - 1); } /** * Compute the gap between the PB next and the PB or between PB and SSN Min. * \param mfs the MFS to get the SSN min. * \param PB the PB to use the SSN from the header. * \return the number of PBs lost. */ inline uint sar_stats_rx_gap_between_pb_ssn_min (mfs_rx_t *mfs, pb_t *pb) { uint pb_missing; if (pb->next && less_mod2p16 (pb->next->header.ssn, mfs->ssn_min)) pb_missing = sar_ssn_gap (pb->next->header.ssn, pb->header.ssn); else pb_missing = sar_ssn_gap (mfs->ssn_min, pb->header.ssn); return pb_missing; } /** * Increment stats on MPDU. * \param rx the rx parameters. * \param mfs the mfs to use. * \param pb_nb the number of PBs received. * \param pb_dropped the number of PBs dropped. */ static inline void sar_stats_mpdu_process (pbproc_rx_params_t *rx, mfs_rx_t *mfs, uint pb_nb, uint pb_dropped) { mfs->stats.num_segs_suc += pb_nb - pb_dropped; mfs->stats.num_pbs += pb_nb; mfs->stats.num_mpdus ++; if (rx->mpdu_cnt == 0) mfs->stats.num_bursts ++; } /** * Apply the MFS command on the MFS. * \param ctx the SAR context. * \param mfs the MFS to apply the command. */ static inline void sar_rx_mfs_cmd_apply__cmd_init (sar_t *ctx, mfs_rx_t *mfs) { bool bcast; bool mme; uint lid; uint tei; bool added; mfs_t *mfs_new; bcast = mfs->common.bcast; mme = mfs->common.mme; lid = mfs->common.lid; tei = mfs->common.tei; sar_mfs_free_rx (ctx, mfs); mfs_new = mac_store_mfs_add (ctx->mac_store, false, bcast, mme, lid, tei, &added); dbg_assert (added); sar_mfs_add (ctx, mfs_new); blk_release (mfs_new); } /** * Apply the command following the MFS CMD. * \param ctx the module context. * \param param the mpdu rx parameters. * \param cmd the MFS command. * \param data true if the command is for DATA, false for MME. */ static void sar_rx_mfs_cmd_apply_cmd (sar_t *ctx, pbproc_rx_params_t *params, mfs_fsm_cmd_t cmd, bool data) { mfs_rx_t *mfs; switch (cmd) { case MFS_FSM_CMD_INIT: mfs = mac_store_mfs_get_rx (ctx->mac_store, params->bcast, !data, data ? params->lid : MAC_LID_NONE, params->tei); if (mfs) { SAR_TRACE (MFS_CMD, mfs, cmd); sar_rx_mfs_cmd_apply__cmd_init (ctx, mfs); blk_release (mfs); #if CONFIG_STATS if (data) ctx->stats.mfs_data_mfs_cmd_init_nb++; else ctx->stats.mfs_mme_mfs_cmd_init_nb++; #endif /* CONFIG_STATS */ } break; default: break; } } /** * Apply the MFS command on the MFS. * \param ctx the SAR context. * \param params the RX Parameters. */ void sar_rx_mfs_cmd_apply (sar_t *ctx, pbproc_rx_params_t *params) { dbg_assert (ctx); dbg_assert (params); dbg_assert (MAC_TEI_IS_STA(params->tei)); sar_rx_mfs_cmd_apply_cmd (ctx, params, params->mfs_cmd_data, true); sar_rx_mfs_cmd_apply_cmd (ctx, params, params->mfs_cmd_mme, false); } /** * Create a MME temporary link. * \param ctx the sar context. * \param rx the rx parameters. * \return the new temporary MFS MME. */ static inline mfs_rx_t * sar_rx_get_associated_mfs__mme_temporary_link (sar_t *ctx, sar_mpdu_t *rx) { rx->rx.mfs_mme = mac_store_mfs_unassociated_add ( ctx->mac_store, false, rx->rx.params.bcast, true, MAC_LID_NONE, rx->rx.params.tei); sar_mfs_add (ctx, rx->rx.mfs_mme); return &rx->rx.mfs_mme->rx; } /** * Create a non temporary MFS. * \param ctx the sar context. * \param rx the rx parameters. * \param pb the PB which require the MFS. * \param mme true if MME false otherwise. * \return the MFS. * * If the LID of the MFS is not a PLID for DATA MFS, this will return NULL. */ static inline mfs_rx_t* sar_rx_get_associated_mfs__from_mac_store ( sar_t *ctx, pbproc_rx_t *rx, pb_t *pb, bool mme) { mfs_rx_t *local_mfs; bool added; dbg_assert (MAC_TEI_IS_STA(rx->params.tei)); /* If the MFS is not a lid one and it request a DATA MFS return NULL. * The SAR is not allowed to create non PLID MFSs. */ if (!mme && !MAC_LID_IS_PLID (rx->params.lid)) { local_mfs = mac_store_mfs_get_rx ( ctx->mac_store, rx->params.bcast, false /* data requested. */, rx->params.lid, rx->params.tei); } else { local_mfs = mac_store_mfs_add_rx (ctx->mac_store, rx->params.bcast, mme, mme ? MAC_LID_NONE : rx->params.lid, rx->params.tei, &added); /* Configure the MFS created, only do this if the MFS has been created*/ if (added) { sar_mfs_add (ctx, PARENT_OF (mfs_t, rx, local_mfs)); /* Special behavior for bcast MFS, the ssn min should be set to * the first PB on the MPDU. This allow the station to receive PBs * with a SSN higher than 0x7fff when SSN min is 0. See #2357 */ if (local_mfs->common.bcast) local_mfs->ssn_min = pb->header.ssn; } } if (mme) rx->mfs_mme = PARENT_OF_OR_NULL (mfs_t, rx, local_mfs); else rx->mfs = PARENT_OF_OR_NULL (mfs_t, rx, local_mfs); return local_mfs; } /** * Get the associated MFS from mac store. * \param sar the SAR context. * \param rx the rx_mpdu * \param pb The PB for the one the MFS is needed. * \param mme true if MME false otherwise. * \return the MFS corresponding, or the new created one. * * If the MFS does not exists it use the rx_params LID and source TEI to add * it to the mac store. */ inline mfs_rx_t * sar_rx_get_associated_mfs (sar_t *ctx, sar_mpdu_t * rx, pb_t *pb, bool mme) { dbg_assert (rx); if (mme && rx->rx.mfs_mme) return (mfs_rx_t *) rx->rx.mfs_mme; else if (!mme && rx->rx.mfs) return (mfs_rx_t *) rx->rx.mfs; /* Temporary link. */ if (mme && ((rx->rx.params.tei == MAC_TEI_UNASSOCIATED) || (rx->rx.params.multi_net_bcast))) return sar_rx_get_associated_mfs__mme_temporary_link (ctx, rx); /* Normal link. */ else return sar_rx_get_associated_mfs__from_mac_store (ctx, &rx->rx, pb, mme); } /** * Resize the SSN window size. * \param pb the PB to make the test. * \param mfs the MFS to verify. * \param pb_nb number of PBs in the MPDU given by the pbproc. */ inline void sar_rx_mfs_resize_ssn (mfs_rx_t *mfs, pb_t *pb, uint pb_nb) { u16 ssn_max, ssn_min_last; uint pb_missing = 0; dbg_assert (pb); dbg_assert (mfs); ssn_max = mfs->ssn_min + mfs->window_size - 1; if ((pb_nb == 1) && pb->header.opsf && less_mod2p16 (ssn_max, pb->header.ssn)) { ssn_min_last = mfs->ssn_min; mfs->ssn_min = pb->header.ssn; uint nb_pb_released = 0; /* Now we can release each PB under the SSN min and increase the * pb_released counter. */ while (mfs->head && less_mod2p16 (mfs->head->header.ssn, mfs->ssn_min)) { if (less_mod2p16 (ssn_min_last, mfs->head->header.ssn)) nb_pb_released ++; mfs->head = sar_pb_release (mfs->head); } pb_missing += (u16) (mfs->ssn_min - ssn_min_last - nb_pb_released); sar_mfs_stats_missing_pbs (mfs, pb_missing); } } /** * Compute the GAP between the MFS HEAD and the MFS last SSN min. * \param ctx the module context. * \param mfs the MFS used. * \param ssn_last the last SSN min. * * \warn Call only this function function if the SSN last is lesser than the * SSN min. */ inline u16 sar_stats_rx_missing_pbs_before_head (sar_t *ctx, mfs_rx_t *mfs, u16 ssn_last) { uint gap = 0; dbg_assert (less_mod2p16 (ssn_last, mfs->ssn_min)); if (mfs->head && less_mod2p16 (mfs->head->header.ssn, mfs->ssn_min) && less_mod2p16 (ssn_last, mfs->head->header.ssn)) gap = sar_ssn_gap (mfs->head->header.ssn, ssn_last - 1); else if ((mfs->head && less_mod2p16 (mfs->ssn_min, mfs->head->header.ssn)) || !mfs->head) gap = sar_ssn_gap (mfs->ssn_min, ssn_last - 1); return gap; } /** * Set the correct SSN min using the pb ssn and the mfs_ssn_min. * \param mfs the MFS to use. */ inline void sar_rx_mfs_ssn_min_update (sar_t *ctx, mfs_rx_t *mfs) { pb_t *pb; pb_t *pb_previous = INVALID_PTR; uint pb_missing, pb_dropped = 0; bool chain_unchanged; bool bcast_restart = false; dbg_assert (mfs); pb = mfs->head; u16 ssn_min_last = mfs->ssn_min; while (pb) { chain_unchanged = true; /* PB's SSN is the MIN SSN. */ if (pb->header.ssn == mfs->ssn_min) mfs->ssn_min ++; /* PB is the OPSF. */ else if (pb->header.opsf && CONFIG_SAR_DROP_OLD_SEG) mfs->ssn_min = pb->header.ssn + 1; /* PB's SSN is greater than the SSN MAX. */ else if (less_mod2p16 (mfs->window_size + mfs->ssn_min - 1, pb->header.ssn)) { if (mfs->common.bcast) { mfs->ssn_min = (u16) (pb->header.ssn - mfs->window_size + 1); /* Restart the chain, the firsts PBs may be the current * SSN min. */ bcast_restart = true; } else { SAR_TRACE (MFS_SSN_UPDATE, mfs->ssn_min, mfs->window_size, pb->header.ssn); chain_unchanged = false; if (pb == mfs->head) mfs->head = pb = sar_pb_release (pb); else { pb_previous->next = pb->next; pb = sar_pb_release (pb); } /* The PB is dropped because it is outside the window, The * segment is considered received but the PB not. */ pb_dropped++; } } if (chain_unchanged) { pb_previous = pb; pb = pb->next; } } /* Special process for broadcast links. */ if (bcast_restart) { for (pb = mfs->head; pb; pb = pb->next) { if (pb->header.ssn == mfs->ssn_min) mfs->ssn_min++; else if (less_mod2p16 (mfs->ssn_min, pb->header.ssn)) break; } } /* Compute the gap between the last SSN Min and the current head. */ if (ssn_min_last != mfs->ssn_min) { pb_missing = sar_stats_rx_missing_pbs_before_head ( ctx, mfs, ssn_min_last); sar_mfs_stats_missing_pbs (mfs, pb_missing); } /* Decrease number of PBs received using the pb_dropped local variable. */ mfs->stats.num_pbs -= pb_dropped; } /** * Drop the PB and increment the stat associated. * \param ctx the module context. * \param pb the PB to drop. * \param stat the stat to increment. * \return the next PB. */ static pb_t* sar_rx_drop_and_stat (sar_t *ctx, pb_t *pb, uint *stat) { (*stat)++; return sar_pb_release (pb); } /** * Parse the chain of PBs received from the pb proc. * \param ctx the sar context * \param rx the mpdu_rx containing the PB to add to the MFS * * it: * - Verifies if the PB is valid * - Transmit the data to the CE. * - Update the mfs window according with the HomePlug AV specification. * - Verifies if the PB is in the window * - Insert it in the right place in the corresponding MFS. */ PRIVATE void ARCH_ILRAM2_PRIO(0) sar_rx_mpdu_process (sar_t *ctx, sar_mpdu_t * rx) { pb_t *rx_head; pb_t *pb_new; mfs_rx_t *mfs_current = NULL; pb_t *pb_current = NULL; bool mme = false; u8 pb_crc_error = 0; u32 ber_sum = 0; /* Statistics variables. */ uint pb_data_nb = 0; uint pb_data_before_ssn_min = 0; uint pb_data_duplicated = 0; uint pb_mme_nb = 0; uint pb_mme_before_ssn_min = 0; uint pb_mme_duplicated = 0; /* Pointer to statistics variable (to save time). */ uint *pb_nb = &pb_data_nb; uint *pb_before_ssn_min = &pb_data_before_ssn_min; uint *pb_duplicated = &pb_data_duplicated; /* Delay the RX PBs have to pass the mac layer. * Initialised with the mfs->common.expiration_delay_tck later. */ uint pb_delay_tck = 0; dbg_assert (ctx); dbg_assert (rx); dbg_assert (ctx->sar_measurement); /* Initialise data. */ rx_head = rx->rx.pb_first; /* The current PB pointing a PB in the MFS current. This can evolve and * the objective of this pointer is to be always lesser than the rx_head. * If the rx_head PB is valid and its SSN is lesser than the PB current * will be reinitialised to the MFS's head PB. This Allows this algorithm * to always inser the rx_head after pb_current. */ pb_current = NULL; #if CONFIG_SAR_PB_STATS sar_pb_stats_new_mpdu (&ctx->pb_stats, rx->rx.params.preamble_ntb, rx->rx.params.tei, (u8) rx->rx.pb_nb, (u16) MAC_TCK_TO_FL(rx->rx.params.fl_tck)); #endif /* Start to sort the mfs */ while (rx_head) { /* Refresh Leon cache for the PB. Load both PB header and PB * Measurements. */ arch_load_cache ((u32 *) &rx_head->header, 2); #if CONFIG_SAR_PB_STATS sar_pb_stats_add_pb_status (&ctx->pb_stats, rx_head->phy_pb.pb_rx.pb_measurement.crc_error); #endif /* If PB is not valid, crc error or Data not encrypt, Drop the PB.*/ if (!rx_head->header.vpbf || rx_head->phy_pb.pb_rx.pb_measurement.crc_error || ((!rx_head->header.mmqf) && (rx->rx.params.eks == MAC_EKS_CLEAR))) { if (rx_head->phy_pb.pb_rx.pb_measurement.crc_error) pb_crc_error ++; else ber_sum += rx_head->phy_pb.pb_rx.pb_measurement.ber; SAR_TRACE (PB_ERROR, rx_head->header.ssn); rx_head = sar_pb_release (rx_head); } else { ber_sum += rx_head->phy_pb.pb_rx.pb_measurement.ber; /* Store the Spidcom encrypted flag. */ rx_head->header.spc_encrypted = rx->rx.params.eks < MAC_EKS_NB; /* Get the current MFS. */ if ((mfs_current == NULL) || (mme != rx_head->header.mmqf)) { mfs_current = sar_rx_get_associated_mfs ( ctx, rx, rx_head, rx_head->header.mmqf); if (mfs_current) { sar_rx_mfs_resize_ssn (mfs_current, rx_head, rx->rx.pb_nb); pb_current = mfs_current->head; mme = rx_head->header.mmqf; if (mme) { pb_nb = &pb_mme_nb; pb_before_ssn_min = &pb_mme_before_ssn_min; pb_duplicated = &pb_mme_duplicated; pb_delay_tck = mfs_current->common.expiration_delay_tck; } else { pb_nb = &pb_data_nb; pb_before_ssn_min = &pb_data_before_ssn_min; pb_duplicated = &pb_data_duplicated; pb_delay_tck = mfs_current->common.expiration_delay_tck; } } } /* The MFS may not exists (case of the lid ones). */ if (mfs_current) { /* Store Expiration time in the PB header. */ rx_head->expiration_ntb = rx->arrival_ntb + pb_delay_tck; /* Increment the correct variable for stats. */ (*pb_nb)++; /* PB is lesser than the SSN min. */ if (less_mod2p16 (rx_head->header.ssn, mfs_current->ssn_min)) rx_head = sar_rx_drop_and_stat (ctx, rx_head, pb_before_ssn_min); else if (mfs_current->head == NULL) { mfs_current->head = rx_head; pb_current = rx_head; rx_head = rx_head->next; pb_current->next = NULL; } /* While rx_head->header.ssn is lesser than the MFS head's * SSN, insert it in it. */ else if (less_mod2p16 (rx_head->header.ssn, mfs_current->head->header.ssn)) { pb_current = rx_head; rx_head = rx_head->next; pb_current->next = mfs_current->head; mfs_current->head = pb_current; } else { /* Reinitialise the pointer, rx_head correspond to the * next group of PBs. */ if (less_mod2p16 (rx_head->header.ssn, pb_current->header.ssn)) pb_current = mfs_current->head; /* Go ahead until the PB's in the MFS are lesser than * the current rx_head. */ while (pb_current->next && lesseq_mod2p16 (pb_current->next->header.ssn, rx_head->header.ssn)) pb_current = pb_current->next; if (pb_current->header.ssn == rx_head->header.ssn) rx_head = sar_rx_drop_and_stat (ctx, rx_head, pb_duplicated); /* Insert the rx_head in the MFS. */ else { pb_new = rx_head; rx_head = rx_head->next; pb_new->next = pb_current->next; pb_current->next = pb_new; pb_current = pb_new; } } } else rx_head = sar_pb_release (rx_head); } } #if CONFIG_SAR_PB_STATS sar_pb_stats_eof_mpdu (&ctx->pb_stats); #endif /* Call the CE. */ ctx->sar_measurement (ctx->measure_ctx, &rx->rx.params, rx->rx.pb_nb, rx->rx.chandata_first, rx->rx.chandata_nb, pb_crc_error, ber_sum); #if CONFIG_STATS /* Increase statistics. */ ctx->stats.rx_pb_count += rx->rx.pb_nb; ctx->stats.rx_pb_crc_error_count += pb_crc_error; ctx->stats.ber_sum += ber_sum; ctx->stats.pb_mme_duplicated += pb_mme_duplicated; ctx->stats.pb_data_duplicated += pb_data_duplicated; ctx->stats.pb_data_before_ssn_min += pb_data_before_ssn_min; ctx->stats.pb_mme_before_ssn_min += pb_mme_before_ssn_min; #endif /* Link statistics. Only DATA are incremented. */ if (rx->rx.mfs) sar_stats_mpdu_process (&rx->rx.params, &rx->rx.mfs->rx, pb_data_nb, pb_data_before_ssn_min + pb_data_duplicated); if (rx->rx.mfs_mme) sar_stats_mpdu_process (&rx->rx.params, &rx->rx.mfs_mme->rx, pb_mme_nb, pb_mme_before_ssn_min + pb_mme_duplicated); } /** * Create a job descriptor for the Bridge DMA. * \param job the job to prepare. * \param mfs the MFS associated. * \param first_pb the first PB to use for the reassembly. * \param offset the offset of the Mac Frame in the first PB. * \param sar_mf the sar_mf structure to have more details on the Mac frame. */ static void sar_rx_job_desc_create (phy_bridgedma_job_t *job, mfs_rx_t *mfs, pb_t *first_pb, u16 offset, sar_mf_t *sar_mf) { /* Verify that the data are not null */ dbg_assert (job); dbg_assert (mfs); dbg_assert (first_pb); dbg_assert (sar_mf); job->first_pb_desc = (blk_t *) first_pb; job->first_pb_offset = offset; job->data_len = sar_mf->length; /* Fill the header of the job */ switch (sar_mf->type) { case SAR_MF_TYPE_DATA: job->header_len = SAR_MF_MFH_SIZE; break; default: job->header_len = SAR_MF_MFH_SIZE + SAR_MF_ATS_SIZE; } offset += sar_mf->length_complete; if (offset < BLK_SIZE) { first_pb->header.mfbo = offset; } else { first_pb->header.mfbf = 0x0; first_pb->header.mfbo = 0; } } /** * Create a RX job with all the minimum configuration for the bridge DMA. * \return sar_job_mfs object for the bridge DMA. */ PRIVATE inline sar_job_mfs_t* sar_job_mfs_create_rx (void) { sar_job_mfs_t *sar_job; sar_job = blk_alloc (); sar_job_mfs_fill (sar_job, false /* RX */); return sar_job; } /** * Verify the possibility to reassembly the frame. * \param ctx the sar context. * \param mfs the MFS used. * \param pb the pb. * \param sar_mf the sar mac frame header structure. * \return true on success. */ static inline bool sar_rx_mfs_detect_and_reconstitute_mf__possible (sar_t *ctx, mfs_rx_t *mfs, pb_t *pb, sar_mf_t *sar_mf) { dbg_assert (ctx); dbg_assert (mfs); dbg_assert (pb); dbg_assert (sar_mf); dbg_assert (pb->header.mfbf); /* Get the Mac Frame header. */ sar_mf_header (pb, pb->header.mfbo, sar_mf); /* Frame is present and the length is correct ? */ if ((sar_mf->type != SAR_MF_TYPE_NONE) && sar_mf->header_complete && (sar_mf->length <= ETH_PACKET_MAX_SIZE) && (sar_mf->length >= ETH_PACKET_MIN_SIZE_ALLOWED)) { /* Check the MF Type with the MFS type. */ if ((sar_mf->type == SAR_MF_TYPE_MME) && (mfs->common.mme == true)) return true; else { if ((sar_mf->type == SAR_MF_TYPE_DATA) && (mfs->common.ats == false)) return true; else if ((sar_mf->type == SAR_MF_TYPE_DATA_ATS) && (mfs->common.ats == true)) return true; } } return false; } /** * Create the job for the bridge DMA. * \param ctx the SAR context. * \param mfs the MFS containing the PBs. * \param sar_mf the data read from the PB. * \param pb_mf_ends the last PB containing the end of the MAC Frame. * * A MAC Frame is present in the PBs from the MFS's head to pb_mf_ends, the * function will use the informations read from the MFS's head PB to create a * job for the Bridge DMA and provide the PBs containing the MAC Frame to * reconstitute. */ PRIVATE inline sar_job_mfs_t * sar_rx_mfs_detect_and_reconstitute_mf__create_job (sar_t *ctx, mfs_rx_t *mfs, sar_mf_t *sar_mf, pb_t *pb_mf_ends) { sar_job_mfs_t *job; dbg_assert (ctx); dbg_assert (mfs); dbg_assert (sar_mf); dbg_assert (pb_mf_ends); job = sar_job_mfs_create_rx (); job->mfs = (mfs_t *) mfs; job->pb_quantity = sar_mf->qte; job->tail = pb_mf_ends; uint offset = mfs->head->header.mfbo; sar_rx_job_desc_create (&job->job, mfs, mfs->head, mfs->head->header.mfbo, sar_mf); uint icv_pos_end = sar_mf->length_complete + offset; /* Workaround for bridgedma bug see maria:#905 */ if (sar_mf->length > SAR_BRGBUG_SECOND_JOB_LENGTH && icv_pos_end % BLK_SIZE != 0 && icv_pos_end % BLK_SIZE <= 4) { sar_job_mfs_t *j2 = sar_job_mfs_create_rx (); uint second_job_start = (icv_pos_end - SAR_BRGBUG_SECOND_JOB_LENGTH - SAR_MF_ICV_SIZE); pb_t *pb; for (pb = mfs->head; second_job_start > BLK_SIZE; second_job_start -= BLK_SIZE, pb = pb->next); j2->job.first_pb_desc = &pb->blk; j2->job.header_len = 0; j2->job.data_len = SAR_BRGBUG_SECOND_JOB_LENGTH; j2->job.first_pb_offset = second_job_start; j2->job.crc_reset = false; job->job.next = &j2->job; job->job.crc_store = false; job->job.data_len -= SAR_BRGBUG_SECOND_JOB_LENGTH; } #if CONFIG_SAR_BRG_JOB_ERROR job->job.mf_header1 = 0xdeaddead; #endif /* Add a reference on the last PB without trying to check if another mac * Frame is present, the parent will do it in the next loop. */ blk_addref_desc (&pb_mf_ends->blk); blk_addref ((blk_t *) mfs); mfs->head = pb_mf_ends; return job; } /** * Try to find a MF in the chained PBs in the MFS. * \param mfs the MFS to use. * * This should try to detect and reconstitute all the MF in the chained PBs * from the MFS head to the last contiguous PB. */ PRIVATE sar_job_mfs_t * sar_rx_mfs_detect_and_reconstitute_mf (sar_t *ctx, mfs_rx_t *mfs) { sar_job_mfs_t *job = NULL; sar_mf_t sar_mf; uint pb_missing = 0; dbg_assert (mfs); /* Try to reassembly Frames. */ while (mfs->head && lesseq_mod2p16 (mfs->head->header.ssn, mfs->ssn_min)) { if (mfs->head->header.mfbf && sar_rx_mfs_detect_and_reconstitute_mf__possible ( ctx, mfs, mfs->head, &sar_mf)) { pb_t *pb_mf_ends; uint qte = sar_mf.qte - 1; /* Verify if all the PBs necessary to reassembly the Mac Frame are * present. */ for (pb_mf_ends = mfs->head; qte && pb_mf_ends->next && (u16) (pb_mf_ends->header.ssn + 1) == pb_mf_ends->next->header.ssn; pb_mf_ends = pb_mf_ends->next, qte--); /* If no PBs are missing then launch the job creator */ if (qte == 0) { /* Enough memory ? */ if (blk_slack ()) { job = sar_rx_mfs_detect_and_reconstitute_mf__create_job ( ctx, mfs, &sar_mf, pb_mf_ends); break; } else { /* Not enough memory available, drop the frame. */ SAR_TRACE (EXHAUSTED_MEMORY, mfs->head->header.ssn, pb_mf_ends->header.ssn); /* The whole mac frame is present in this PB, in the mean * time, no blocks will be released, so it can be fully * released. */ if (sar_mf.qte == 1) mfs->head = sar_pb_release (mfs->head); else { pb_t *pb = mfs->head; blk_addref_desc (&pb_mf_ends->blk); mfs->head = pb_mf_ends; blk_release_desc_range (&pb->blk, &pb_mf_ends->blk); } } } /* The segments are not contiguous and the missing PBs are outside * the window. */ else if (less_mod2p16 (pb_mf_ends->header.ssn + 1, mfs->ssn_min)) { /* Next PB is outside the window. */ if (mfs->head == pb_mf_ends) /* Special case when the MF in the head is on several PBs * and the next PB is missing and ouside the window. * This will compute the gap between the PB's SSN and the * MFS SSN min. */ pb_missing += sar_stats_rx_gap_between_pb_ssn_min ( mfs, pb_mf_ends); SAR_TRACE (MF_PB_OUTSIDE_WIN, mfs->ssn_min, mfs->window_size, mfs->head->header.ssn, mfs->head->header.mfbo); mfs->head = sar_pb_release (mfs->head); } /* The MAC Frame starts in a PB before the SSN min (still present * in the MFS) and ends with a missing PB which SSN is in the * Window. */ else /* Stop the loop. */ break; } /* PB have got a MF boundary and the data read in the PB is * incomplete and the next PB is after the SSN min i.e. it can be * receive. */ else if (mfs->head->header.mfbf && !sar_mf.header_complete && lesseq_mod2p16 (mfs->ssn_min, mfs->head->header.ssn + 1)) { job = NULL; break; } /* Impossible to reassembly the Mac Frame. */ else { SAR_TRACE (PB_EMPTY, mfs->head->header.ssn, mfs->head->header.mfbo); pb_missing += sar_stats_rx_gap_between_pb_ssn_min ( mfs, mfs->head); mfs->head = sar_pb_release (mfs->head); } } sar_mfs_stats_missing_pbs (mfs, pb_missing); return job; } /** * Queue jobs in the pending list. * \param ctx the module context. * \param jobs the jobs to queue. */ static inline void sar_rx_mfs_process__queue_jobs (sar_t *ctx, sar_job_mfs_t *jobs) { if (jobs->next) slist_push_back_range (ctx->reassembly.jobs_pending_list., jobs, jobs->next, bare); else slist_push_back (ctx->reassembly.jobs_pending_list., jobs, bare); } /** * Process the MFS and try to reassembly all frames until ssn min * \param ctx the module context. * \param mfs the mfs to process. */ PRIVATE void ARCH_ILRAM2_PRIO(0) sar_rx_mfs_process (sar_t *ctx, mfs_rx_t * mfs) { sar_job_mfs_t *job; dbg_assert (mfs); /* No pending jobs. */ if (slist_empty (ctx->reassembly.jobs_pending_list., bare)) { while ((job = sar_rx_mfs_detect_and_reconstitute_mf (ctx, mfs))) { u8 *buffer = bufmgr_get (ctx->bufmgr); if (buffer) { job->job.data_addr = buffer; /* Workaround for maria bug #905 */ if (job->job.next) { job->job.next->data_addr = buffer + job->job.data_len; phy_bridgedma_start ( ctx->bridgedma_ctx, &job->job, job->job.next); } else phy_bridgedma_start ( ctx->bridgedma_ctx, &job->job, &job->job); } else { /* No more buffers available. */ sar_rx_mfs_process__queue_jobs (ctx, job); break; } } } /* No more buffer available. */ while ((job = sar_rx_mfs_detect_and_reconstitute_mf (ctx, mfs))) sar_rx_mfs_process__queue_jobs (ctx, job); } /** * Provide the next reassembly job to the bridge DMA. * \param ctx the SAR context. * * It only provided the job to the bridge DMA if a reassembly buffer is * available. It will loop to provide a maximum of jobs to the bridge DMA * until it has available buffers and jobs. */ static void sar_rx_upper_layer_transmit_data (sar_t *ctx) { sar_job_mfs_t *job_head = NULL; sar_job_mfs_t *job_tail = NULL; sar_job_mfs_t *job_current; u8 *buffer; dbg_assert (ctx); /* Get the head of the list. */ job_current = ctx->reassembly.jobs_pending_list.head; while (job_current && (buffer = bufmgr_get (ctx->bufmgr))) { if (job_head == NULL) job_head = job_current; job_current->job.data_addr = buffer; /* Work around maria:#905 */ if (job_current->job.crc_store == false) { sar_job_mfs_t *j = job_current->next; dbg_assert (j); j->job.data_addr = buffer + job_current->job.data_len; job_current = j; } job_tail = job_current; job_current = job_current->next; } /* No more buffers available or no more job to unchain. */ if (job_head) { /* Unchain the jobs from the list. */ slist_slice (ctx->reassembly.jobs_pending_list., job_tail, bare); job_tail->next = NULL; /* Provide the jobs to the bridgedma list. */ phy_bridgedma_start (ctx->bridgedma_ctx, &job_head->job, &job_tail->job); } } /** * Try to extract MF from the PBs present in the MFS. * \param ctx the SAR context. * \param mfs the MFS to use. */ static void sar_mac_frame_reconstitute (sar_t *ctx, mfs_rx_t *mfs) { sar_rx_mfs_ssn_min_update (ctx, mfs); sar_rx_mfs_process (ctx, mfs); blk_release (mfs); } /** * Process the MPDU by trying to make a reassembly * \param ctx the SAR context. * \param rx the MPDU received. */ PRIVATE void ARCH_ILRAM2_PRIO(0) sar_reassembly_run (sar_t *ctx, sar_mpdu_t *rx) { dbg_assert (ctx); dbg_assert (rx); sar_pb_pool_refill (ctx, SAR_MPDU_RX_REFILL + rx->rx.pb_nb + rx->rx.chandata_nb); if (MAC_TEI_IS_STA(rx->rx.params.tei)) mac_store_sta_add (ctx->mac_store, rx->rx.params.tei); /** If the message only contains chandata. * send the noise measurements to the CE. */ if (rx->rx.chandata_first && !rx->rx.pb_first) { dbg_assert (!rx->rx.mfs); dbg_assert (!rx->rx.mfs_mme); /* Channel data ownership is handed over to callee. */ ctx->sar_measurement (ctx->measure_ctx, &rx->rx.params, 0, rx->rx.chandata_first, rx->rx.chandata_nb, 0, 0); } else { if (MAC_TEI_IS_STA(rx->rx.params.tei)) sar_rx_mfs_cmd_apply (ctx, &rx->rx.params); sar_rx_mpdu_process (ctx, rx); if (rx->rx.mfs) { sar_expiration_mfs_update_ntb (ctx, rx->rx.mfs, rx->arrival_ntb); sar_mac_frame_reconstitute (ctx, &rx->rx.mfs->rx); } if (rx->rx.mfs_mme) { sar_expiration_mfs_update_ntb (ctx, rx->rx.mfs_mme, rx->arrival_ntb); sar_mac_frame_reconstitute (ctx, &rx->rx.mfs_mme->rx); } } } /** * Release the PBs of the MPDU. * \param mpdu the MPDU object. */ static void sar_mpdu_release_content (sar_t *ctx, sar_mpdu_t *mpdu) { if (CONFIG_TRACE) { arch_load_cache ((u32*) &mpdu->rx.pb_first->header, 1); arch_load_cache ((u32*) &mpdu->rx.pb_last->header, 1); SAR_TRACE (MPDU_EXPIRE, phy_date (), mpdu->rx.pb_first->header.ssn, mpdu->rx.pb_last->header.ssn); } if (mpdu->rx.mfs) blk_release (mpdu->rx.mfs); if (mpdu->rx.mfs_mme) blk_release (mpdu->rx.mfs_mme); if (mpdu->rx.pb_first) blk_release_desc_range (&mpdu->rx.pb_first->blk, &mpdu->rx.pb_last->blk); if (mpdu->rx.chandata_first) blk_release_desc_range_nb (&mpdu->rx.chandata_first->blk, mpdu->rx.chandata_nb); } void ARCH_ILRAM2_PRIO(0) sar_mpdu_process (void *user, pbproc_rx_desc_t *rx_desc) { sar_t *ctx; sar_mpdu_t *mpdu; dbg_assert (user); dbg_assert (rx_desc); dbg_assert (rx_desc->rx); ctx = (sar_t *) user; /* Do not lock DSR, this function is called only in DSR context. */ mpdu = PARENT_OF (sar_mpdu_t, rx, rx_desc->rx); dbg_assert (!mpdu->rx.mfs || mpdu->rx.mfs->common.mme == false); dbg_assert (!mpdu->rx.mfs_mme || mpdu->rx.mfs_mme->common.mme == true); /* It the SAR is not activated the packets are directly dropped. */ if (ctx->activate) { /* See section 4.4.2 Format of Long MPDU Payload of AV specification. */ if (mpdu->rx.pb_nb == 1 && mpdu->rx.params.pb_size == PHY_PB_SIZE_136) *((u32*) &mpdu->rx.pb_first->data[128]) = 0; mpdu->arrival_ntb = mpdu->rx.params.preamble_ntb; if (mpdu->rx.pb_nb) mpdu->rx.pb_last->next = NULL; /** Tracing system. */ SAR_TRACE (RX, phy_date (), mpdu->rx.params.tei, mpdu->rx.params.lid, mpdu->rx.pb_nb); sar_reassembly_run (ctx, mpdu); phy_bridgedma_stopped (ctx->bridgedma_ctx); sar_bridge_dma_free_head (ctx); } else { sar_mpdu_release_content (ctx, mpdu); /* Allocate new PBs to the PBProc PB pool. */ sar_pb_pool_refill (ctx, SAR_MPDU_RX_REFILL + mpdu->rx.pb_nb + mpdu->rx.chandata_nb); } blk_release_desc (&rx_desc->blk); } void sar_buffer_available (sar_t *ctx) { if (ctx->activate) { /* If there is RX pending JOB provide the head to the bridge DMA. */ sar_rx_upper_layer_transmit_data (ctx); phy_bridgedma_stopped (ctx->bridgedma_ctx); sar_bridge_dma_free_head (ctx); } if (ctx->pbs_missing_for_pbproc) { /* Refill the PB pool if missing block were registered. */ sar_pb_pool_refill (ctx, 0); } }