- Main Page
- Modules
- Data Structures
- Files

- File List
- Globals

# /space/home/slher/rel/Beken/v6_4_5/macsw/ip/lmac/src/tx/txl/txl_agg.c

Go to the documentation of this file.

    00001
    00020 /*
    00021  * INCLUDE FILES
    00022  ****************************************************************************************
    00023  */
    00024 #include "co_int.h"
    00025 #include "co_bool.h"
    00026 #include <string.h>
    00027
    00028 #include "dbg_assert.h"
    00029 #include "mac.h"
    00030 #include "mac_frame.h"
    00031 #include "mm.h"
    00032 #include "macif.h"
    00033 #include "rxl_hwdesc.h"
    00034 #include "txl_buffer.h"
    00035 #if NX_TX_FRAME
    00036 #include "txl_frame.h"
    00037 #endif
    00038 #include "tx_swdesc.h"
    00039 #include "txl_cfm.h"
    00040 #include "txl_cntrl.h"
    00041 #include "rxl_cntrl.h"
    00042 #include "reg_mac_pl.h"
    00043 #include "reg_mac_core.h"
    00044 #include "dbg.h"
    00045 #include "ps.h"
    00046 #if (NX_P2P)
    00047 #include "p2p.h"
    00048 #endif //(NX_P2P)
    00049 #if (NX_TD)
    00050 #include "td.h"
    00051 #endif //(NX_TD)
    00052 #if NX_UMAC_PRESENT
    00053 #include "txu_cntrl.h"
    00054 #include "apm.h"
    00055 #endif
    00056 #if NX_MFP
    00057 #include "mfp.h"
    00058 #endif
    00059 #if (RW_BFMER_EN)
    00060 #include "bfr.h"
    00061 #endif //(RW_BFMER_EN)
    00062 #if NX_MAC_HE
    00063 #include "txl_he.h"
    00064 #endif
    00065 #include "txl_agg.h"
    00066
    00067 #if NX_AMPDU_TX
    00068
    00069 /*
    00070  * DEFINES
    00071  ****************************************************************************************
    00072  */
    00073 #if RW_MUMIMO_TX_EN
    00075 #define TX_SEC_IRQ_BITS_MERGED  ( NXMAC_SEC_U_3AC_3_TX_TRIGGER_BIT     |                 \
    00076                                   NXMAC_SEC_U_3AC_2_TX_TRIGGER_BIT     |                 \
    00077                                   NXMAC_SEC_U_3AC_1_TX_TRIGGER_BIT     |                 \
    00078                                   NXMAC_SEC_U_3AC_0_TX_TRIGGER_BIT     |                 \
    00079                                   NXMAC_SEC_U_2AC_3_TX_TRIGGER_BIT     |                 \
    00080                                   NXMAC_SEC_U_2AC_2_TX_TRIGGER_BIT     |                 \
    00081                                   NXMAC_SEC_U_2AC_1_TX_TRIGGER_BIT     |                 \
    00082                                   NXMAC_SEC_U_2AC_0_TX_TRIGGER_BIT     |                 \
    00083                                   NXMAC_SEC_U_1AC_3_TX_TRIGGER_BIT     |                 \
    00084                                   NXMAC_SEC_U_1AC_2_TX_TRIGGER_BIT     |                 \
    00085                                   NXMAC_SEC_U_1AC_1_TX_TRIGGER_BIT     |                 \
    00086                                   NXMAC_SEC_U_1AC_0_TX_TRIGGER_BIT )
    00087
    00089 #define MU_AMPDU_CLOSE()                                                                 \
    00090 {                                                                                        \
    00091     txl_mumimo_ampdu_finish(ac, user_pos);                                               \
    00092     /* Check if the other user positions are still open */                               \
    00093     if (txlist->mumimo.open & txlist->mumimo.users)                                      \
    00094     {                                                                                    \
    00095         /* We could still receive packets on other user positions, */                    \
    00096         /* so save the current packet */                                                 \
    00097         ASSERT_ERR(txlist->mumimo.txdesc[user_pos] == NULL);                             \
    00098         txlist->mumimo.txdesc[user_pos] = txdesc;                                        \
    00099         status = MU_PAUSED;                                                              \
    00100         break;                                                                           \
    00101     }                                                                                    \
    00102     else                                                                                 \
    00103     {                                                                                    \
    00104         /* Close the MU-MIMO PPDU under construction */                                  \
    00105         txl_agg_mumimo_close(ac);                                                        \
    00106                                                                                          \
    00107         /* Start a new MU-MIMO PPDU */                                                   \
    00108         continue;                                                                        \
    00109     }                                                                                    \
    00110 }
    00111 #endif
    00112
    00117 const uint8_t TX_VHT_RATE_TO_MIN_SEP[10*4*2] =
    00118 {
    00119     //BW20,GI800  BW20,GI400    BW40,GI800    BW40,GI400   MCS Index
    00120     [  0] =  1,   [  1] =  1,   [  2] =  2,   [  3] =  2, //MCS 0
    00121     [  8] =  2,   [  9] =  2,   [ 10] =  4,   [ 11] =  4, //MCS 1
    00122     [ 16] =  3,   [ 17] =  3,   [ 18] =  6,   [ 19] =  6, //MCS 2
    00123     [ 24] =  4,   [ 25] =  4,   [ 26] =  7,   [ 27] =  8, //MCS 3
    00124     [ 32] =  5,   [ 33] =  6,   [ 34] = 11,   [ 35] = 12, //MCS 4
    00125     [ 40] =  7,   [ 41] =  8,   [ 42] = 14,   [ 43] = 15, //MCS 5
    00126     [ 48] =  8,   [ 49] =  9,   [ 50] = 16,   [ 51] = 17, //MCS 6
    00127     [ 56] =  9,   [ 57] = 10,   [ 58] = 17,   [ 59] = 19, //MCS 7
    00128     [ 64] = 10,   [ 65] = 11,   [ 66] = 21,   [ 67] = 23, //MCS 8
    00129     [ 72] = 11,   [ 73] = 13,   [ 74] = 23,   [ 75] = 25, //MCS 9
    00130     //BW80,GI800  BW80,GI400    BW160,GI800   BW160,GI400
    00131     [  4] =  4,   [  5] =  5,   [  6] =  8,   [  7] =  9, //MCS 0
    00132     [ 12] =  8,   [ 13] =  9,   [ 14] = 15,   [ 15] = 17, //MCS 1
    00133     [ 20] = 11,   [ 21] = 13,   [ 22] = 22,   [ 23] = 25, //MCS 2
    00134     [ 28] = 15,   [ 29] = 17,   [ 30] = 30,   [ 31] = 33, //MCS 3
    00135     [ 36] = 22,   [ 37] = 25,   [ 38] = 44,   [ 39] = 49, //MCS 4
    00136     [ 44] = 30,   [ 45] = 33,   [ 46] = 59,   [ 47] = 65, //MCS 5
    00137     [ 52] = 33,   [ 53] = 37,   [ 54] = 66,   [ 55] = 74, //MCS 6
    00138     [ 60] = 37,   [ 61] = 41,   [ 62] = 74,   [ 63] = 82, //MCS 7
    00139     [ 68] = 44,   [ 69] = 49,   [ 70] = 88,   [ 71] = 98, //MCS 8
    00140     [ 76] = 49,   [ 77] = 55,   [ 78] = 98,   [ 79] = 109, //MCS 9
    00141 };
    00142
    00143
    00148 const uint16_t TX_VHT_RATE_TO_32US_LEN[10*4*2] =
    00149 {
    00150     //BW20,GI800    BW20,GI400      BW40,GI800      BW40,GI400   MCS Index
    00151     [  0] =   26,   [  1] =   28,   [  2] =   54,   [  3] =   60, //MCS 0
    00152     [  8] =   52,   [  9] =   57,   [ 10] =  108,   [ 11] =  120, //MCS 1
    00153     [ 16] =   78,   [ 17] =   86,   [ 18] =  162,   [ 19] =  180, //MCS 2
    00154     [ 24] =  104,   [ 25] =  115,   [ 26] =  216,   [ 27] =  240, //MCS 3
    00155     [ 32] =  156,   [ 33] =  173,   [ 34] =  324,   [ 35] =  360, //MCS 4
    00156     [ 40] =  208,   [ 41] =  231,   [ 42] =  432,   [ 43] =  480, //MCS 5
    00157     [ 48] =  234,   [ 49] =  260,   [ 50] =  486,   [ 51] =  540, //MCS 6
    00158     [ 56] =  260,   [ 57] =  288,   [ 58] =  540,   [ 59] =  600, //MCS 7
    00159     [ 64] =  312,   [ 65] =  346,   [ 66] =  648,   [ 67] =  720, //MCS 8
    00160     [ 72] =  346,   [ 73] =  385,   [ 74] =  720,   [ 75] =  800, //MCS 9
    00161     //BW80,GI800    BW80,GI400      BW160,GI800     BW160,GI400
    00162     [  4] =  117,   [  5] =  130,   [  6] =  234,   [  7] =  260, //MCS 0
    00163     [ 12] =  234,   [ 13] =  260,   [ 14] =  468,   [ 15] =  520, //MCS 1
    00164     [ 20] =  351,   [ 21] =  390,   [ 22] =  702,   [ 23] =  780, //MCS 2
    00165     [ 28] =  468,   [ 29] =  520,   [ 30] =  936,   [ 31] = 1040, //MCS 3
    00166     [ 36] =  702,   [ 37] =  780,   [ 38] = 1404,   [ 39] = 1560, //MCS 4
    00167     [ 44] =  936,   [ 45] = 1040,   [ 46] = 1872,   [ 47] = 2080, //MCS 5
    00168     [ 52] = 1053,   [ 53] = 1170,   [ 54] = 2106,   [ 55] = 2340, //MCS 6
    00169     [ 60] = 1170,   [ 61] = 1300,   [ 62] = 2340,   [ 63] = 2600, //MCS 7
    00170     [ 68] = 1404,   [ 69] = 1560,   [ 70] = 2808,   [ 71] = 3120, //MCS 8
    00171     [ 76] = 1560,   [ 77] = 1733,   [ 78] = 3120,   [ 79] = 3466, //MCS 9
    00172 };
    00173
    00174 #if RW_MUMIMO_TX_EN
    00176 #define VHT_BW   4
    00178 #define VHT_MCS  10
    00179
    00183 const uint16_t VHT_NDBPS[VHT_BW][VHT_MCS] = {
    00184     // MCS Index:  0     1      2       3      4      5      6      7      8      9
    00185    [BW_20MHZ]  = {26,   52,    78,     104,   156,   208,   234,   260,   312,   1040},
    00186    [BW_40MHZ]  = {54,   108,   162,    216,   324,   432,   486,   540,   648,   720 },
    00187    [BW_80MHZ]  = {117,  234,   351,    468,   702,   936,   1053,  1170,  1404,  1560},
    00188    [BW_160MHZ] = {234,  468,   702,    936,   1404,  1872,  2106,  2340,  2808,  3120},
    00189 };
    00190 #endif
    00191
    00193 #define TX_MIN_AMPDU_NB_PER_AC  (3 * RW_USER_MAX)
    00195 #define TX_AGG_DIVIDER          (8 / RW_USER_MAX)
    00197 #if (NX_TXDESC_CNT0 / TX_AGG_DIVIDER) >= TX_MIN_AMPDU_NB_PER_AC
    00198 #define TX_MAX_AMPDU_NB_PER_AC0  (NX_TXDESC_CNT0 / TX_AGG_DIVIDER)
    00199 #else
    00200 #define TX_MAX_AMPDU_NB_PER_AC0  TX_MIN_AMPDU_NB_PER_AC
    00201 #endif
    00203 #if (NX_TXDESC_CNT1 / TX_AGG_DIVIDER) >= TX_MIN_AMPDU_NB_PER_AC
    00204 #define TX_MAX_AMPDU_NB_PER_AC1  (NX_TXDESC_CNT1 / TX_AGG_DIVIDER)
    00205 #else
    00206 #define TX_MAX_AMPDU_NB_PER_AC1  TX_MIN_AMPDU_NB_PER_AC
    00207 #endif
    00209 #if (NX_TXDESC_CNT2 / TX_AGG_DIVIDER) >= TX_MIN_AMPDU_NB_PER_AC
    00210 #define TX_MAX_AMPDU_NB_PER_AC2  (NX_TXDESC_CNT2 / TX_AGG_DIVIDER)
    00211 #else
    00212 #define TX_MAX_AMPDU_NB_PER_AC2  TX_MIN_AMPDU_NB_PER_AC
    00213 #endif
    00215 #if (NX_TXDESC_CNT3 / TX_AGG_DIVIDER) >= TX_MIN_AMPDU_NB_PER_AC
    00216 #define TX_MAX_AMPDU_NB_PER_AC3  (NX_TXDESC_CNT3 / TX_AGG_DIVIDER)
    00217 #else
    00218 #define TX_MAX_AMPDU_NB_PER_AC3  TX_MIN_AMPDU_NB_PER_AC
    00219 #endif
    00220 #if NX_BEACONING
    00222 #if (NX_TXDESC_CNT4 / TX_AGG_DIVIDER) > TX_MIN_AMPDU_NB_PER_AC
    00223 #define TX_MAX_AMPDU_NB_PER_AC4  (NX_TXDESC_CNT4 / TX_AGG_DIVIDER)
    00224 #else
    00225 #define TX_MAX_AMPDU_NB_PER_AC4  TX_MIN_AMPDU_NB_PER_AC
    00226 #endif
    00227 #endif
    00228
    00230 #define TX_AMPDU_DESC_QUEUE_CNT (NX_TXQ_CNT + NX_MAC_HE)
    00231
    00232 #if NX_MAC_HE
    00234 #define TX_MAX_AMPDU_NB_FOR_HE_TB 4
    00236 #define TX_HE_TB_AMPDU_QUEUE_IDX (TX_AMPDU_DESC_QUEUE_CNT - 1)
    00238 #define TX_AGG_FINISH_DUR 5
    00239 #endif
    00240
    00241 /*
    00242  * GLOBAL VARIABLE DEFINITIONS
    00243  ****************************************************************************************
    00244  */
    00246 static struct tx_agg_desc tx_agg_desc_array0[TX_MAX_AMPDU_NB_PER_AC0] __SHAREDRAM;
    00248 static struct tx_agg_desc tx_agg_desc_array1[TX_MAX_AMPDU_NB_PER_AC1] __SHAREDRAM;
    00250 static struct tx_agg_desc tx_agg_desc_array2[TX_MAX_AMPDU_NB_PER_AC2] __SHAREDRAM;
    00252 static struct tx_agg_desc tx_agg_desc_array3[TX_MAX_AMPDU_NB_PER_AC3] __SHAREDRAM;
    00253 #if NX_BEACONING
    00255 static struct tx_agg_desc tx_agg_desc_array4[TX_MAX_AMPDU_NB_PER_AC4] __SHAREDRAM;
    00256 #endif
    00257 #if NX_MAC_HE
    00259 static struct tx_agg_desc tx_agg_desc_array5[TX_MAX_AMPDU_NB_FOR_HE_TB] __SHAREDRAM;
    00260 #endif
    00261
    00263 static const struct tx_agg_desc * const tx_agg_desc_array[TX_AMPDU_DESC_QUEUE_CNT] =
    00264 {
    00265     tx_agg_desc_array0,
    00266     tx_agg_desc_array1,
    00267     tx_agg_desc_array2,
    00268     tx_agg_desc_array3,
    00269     #if NX_BEACONING
    00270     tx_agg_desc_array4,
    00271     #endif
    00272     #if NX_MAC_HE
    00273     tx_agg_desc_array5,
    00274     #endif
    00275 };
    00276
    00278 static const int tx_agg_desc_cnt[TX_AMPDU_DESC_QUEUE_CNT] =
    00279 {
    00280     TX_MAX_AMPDU_NB_PER_AC0,
    00281     TX_MAX_AMPDU_NB_PER_AC1,
    00282     TX_MAX_AMPDU_NB_PER_AC2,
    00283     TX_MAX_AMPDU_NB_PER_AC3,
    00284     #if NX_BEACONING
    00285     TX_MAX_AMPDU_NB_PER_AC4,
    00286     #endif
    00287     #if NX_MAC_HE
    00288     TX_MAX_AMPDU_NB_FOR_HE_TB,
    00289     #endif
    00290 };
    00291
    00293 struct co_list tx_agg_desc_pool[TX_AMPDU_DESC_QUEUE_CNT];
    00294
    00295 /*
    00296  * FUNCTION BODIES
    00297  ****************************************************************************************
    00298  */
    00299
    00309 static bool txl_desc_is_agg(struct txdesc *txdesc, struct tx_agg_desc * agg_desc)
    00310 {
    00311     if (is_mpdu_agg(txdesc) && !is_mpdu_first(txdesc))
    00312     {
    00313         //check if its (staid, tid) matches that of the started AMPDU
    00314         if ( (agg_desc->sta_idx == txdesc->host.staid)  &&
    00315              (agg_desc->tid     == txdesc->host.tid) )
    00316         {
    00317             return true;
    00318         }
    00319     }
    00320     return false;
    00321 }
    00322
    00323 #if NX_UMAC_PRESENT
    00324
    00334 static bool txl_ampdu_has_ht_vht_rate(struct txdesc *txdesc)
    00335 {
    00336     return (((txdesc->umac.phy_flags & FORMAT_MOD_TX_RCX_MASK)
    00337                                    >> FORMAT_MOD_TX_RCX_OFT) > FORMATMOD_NON_HT_DUP_OFDM);
    00338 }
    00339 #endif
    00340
    00351 static uint32_t txl_vht_idx_to_32us_len_get(int base_idx, uint8_t bw)
    00352 {
    00353     int idx = base_idx | (bw << 1);
    00354     return ((uint32_t)TX_VHT_RATE_TO_32US_LEN[idx]);
    00355 }
    00356
    00367 static uint16_t txl_vht_idx_to_1us_len_get(int base_idx, uint8_t bw)
    00368 {
    00369     int idx = base_idx | (bw << 1);
    00370     return ((uint32_t)TX_VHT_RATE_TO_MIN_SEP[idx]);
    00371 }
    00372
    00387 static int txl_ht_vht_ampdu_param_get(struct txdesc *txdesc,
    00388                                       uint8_t format_mod,
    00389                                       uint32_t *max_len_sta,
    00390                                       uint32_t *nss)
    00391 {
    00392     uint32_t phy_flags = txdesc->umac.phy_flags;
    00393     uint32_t mcs_idx = (phy_flags & MCS_INDEX_TX_RCX_MASK) >> MCS_INDEX_TX_RCX_OFT;
    00394     uint32_t sgi = (phy_flags & SHORT_GI_TX_RCX_MASK) >> SHORT_GI_TX_RCX_OFT;
    00395     struct sta_info_tag *sta = &sta_info_tab[txdesc->host.staid];
    00396
    00397     // Check if modulation is HT or VHT
    00398     if (format_mod == FORMATMOD_VHT)
    00399     {
    00400         // Extract NSS
    00401         *nss = ((mcs_idx & VHT_NSS_MASK) >> VHT_NSS_OFT) + 1;
    00402
    00403         // Extract MCS index
    00404         mcs_idx = (mcs_idx & VHT_MCS_MASK) >> VHT_MCS_OFT;
    00405
    00406         // Sanity checks: Maximum 4 SS supported, and MCS shall be between 0 and 9
    00407         ASSERT_ERR((*nss > 0) && (*nss <= 4));
    00408         ASSERT_ERR(mcs_idx <= 9);
    00409
    00410         // Get the maximum VHT A-MPDU size supported by the receiver
    00411         *max_len_sta = sta->ampdu_size_max_vht;
    00412     }
    00413     else
    00414     {
    00415         // Sanity check: The current implementation only supports MCS0-31
    00416         ASSERT_ERR(mcs_idx <= 31);
    00417
    00418         // Extract NSS
    00419         *nss = ((mcs_idx & HT_NSS_MASK) >> HT_NSS_OFT) + 1;
    00420
    00421         // Extract MCS index in 0-7 range
    00422         mcs_idx = (mcs_idx & HT_MCS_MASK) >> HT_MCS_OFT;
    00423
    00424         // Get the maximum HT A-MPDU size supported by the receiver
    00425         *max_len_sta = sta->ampdu_size_max_ht;
    00426     }
    00427
    00428     // To compute the maximum length we take the minimal bandwidth, to ensure that the
    00429     // packet won't cross the L-SIG duration limit in case the MAC HW would have to
    00430     // reduce the transmission bandwidth
    00431     return ((mcs_idx << 3) | sgi);
    00432 }
    00433
    00443 static void txl_ampdu_constraints_get(struct txdesc *txdesc,
    00444                                       uint8_t ac,
    00445                                       struct txl_agg_build_tag *agg)
    00446 {
    00447     int rate_idx;
    00448     uint32_t max_len_sta;
    00449     uint32_t max_len_phy;
    00450     uint32_t nss;
    00451     uint32_t bw = (txdesc->umac.phy_flags & BW_TX_RCX_MASK) >> BW_TX_RCX_OFT;
    00452     struct sta_info_tag *sta = &sta_info_tab[txdesc->host.staid];
    00453     #if NX_BW_LEN_ADAPT
    00454     int i;
    00455     #endif
    00456     uint8_t format_mod;
    00457     uint32_t (*idx_to_32us_len)(int, uint8_t);
    00458     uint16_t (*idx_to_1us_len)(int, uint8_t);
    00459
    00460     // Get modulation format
    00461     format_mod = (txdesc->umac.phy_flags & FORMAT_MOD_TX_RCX_MASK) >> FORMAT_MOD_TX_RCX_OFT;
    00462
    00463     // Sanity check: A-MPDUs cannot be sent at legacy rates
    00464     ASSERT_ERR(format_mod > FORMATMOD_NON_HT_DUP_OFDM);
    00465
    00466     #if NX_MAC_HE
    00467     if (format_mod == FORMATMOD_HE_SU)
    00468     {
    00469         // Get A-MPDU parameters for HE A-MPDU
    00470         rate_idx = txl_he_ampdu_param_get(txdesc, format_mod, &max_len_sta, &nss);
    00471         idx_to_32us_len = txl_he_idx_to_32us_len_get;
    00472         idx_to_1us_len = txl_he_idx_to_1us_len_get;
    00473     }
    00474     else
    00475     #endif
    00476     {
    00477         // Get A-MPDU parameters for HT or VHT A-MPDU
    00478         rate_idx = txl_ht_vht_ampdu_param_get(txdesc, format_mod, &max_len_sta, &nss);
    00479         idx_to_32us_len = txl_vht_idx_to_32us_len_get;
    00480         idx_to_1us_len = txl_vht_idx_to_1us_len_get;
    00481     }
    00482
    00483     // Compute the rate indexes
    00484     agg->bw = bw;
    00485
    00486     #if NX_BW_LEN_ADAPT
    00487     agg->bw_idx = 0;
    00488
    00489     // Compute the maximum length for each BW
    00490     for (i = 0; i <= agg->bw; i++)
    00491     {
    00492         // Compute the maximum length of the A-MPDU depending on the PHY rate
    00493         max_len_phy = idx_to_32us_len(rate_idx, i) * nss * mm_env.ampdu_max_dur[ac];
    00494
    00495         // Get the constraints of the A-MPDU based on the PHY rate that will be used
    00496         agg->max_len[i] = co_min(max_len_phy, max_len_sta);
    00497     }
    00498
    00499     #else
    00500     // Compute the maximum length of the A-MPDU depending on the PHY rate
    00501     max_len_phy = idx_to_32us_len(rate_idx, 0) * nss * mm_env.ampdu_max_dur[ac];
    00502
    00503     // Get the constraints of the A-MPDU based on the PHY rate that will be used
    00504     agg->max_len[0] = co_min(max_len_phy, max_len_sta);
    00505     #endif
    00506
    00507     // To compute the minimal A-MPDU subframe length we take the maximal bandwidth
    00508     agg->mmss_bytes = idx_to_1us_len(rate_idx, bw) * nss *
    00509                                                    (uint16_t)sta->ampdu_spacing_min;
    00510
    00511     // Get the maximum number of MPDUs inside the A-MPDU from BA agreement
    00512     agg->max_cnt = sta_mgmt_get_tx_buff_size(txdesc->host.staid, txdesc->host.tid);
    00513 }
    00514
    00525 static void txl_fill_bar(struct tx_agg_desc *agg_desc, uint16_t sn,
    00526                          uint8_t staid, uint8_t tid, uint8_t bw)
    00527 {
    00528     struct tx_hd *bar_thd = &agg_desc->bar_thd;
    00529     struct tx_policy_tbl *bar_pol = &agg_desc->bar_pol_tbl;
    00530     struct bar_frame *bar_payl = &agg_desc->bar_payl;
    00531     struct sta_info_tag *sta = &sta_info_tab[staid];
    00532     struct vif_info_tag *vif = &vif_info_tab[sta->inst_nbr];
    00533
    00534     //------------------------------------------------------------------------------------
    00535     //FILL PAYLOAD
    00536     //------------------------------------------------------------------------------------
    00537     //RA
    00538     bar_payl->h.addr1 = sta->mac_addr;
    00539     //TA - our own
    00540     bar_payl->h.addr2 = vif->mac_addr;
    00541
    00542     //bar_cntl - Normal ACK, Non-Multi tID, compressed BAR
    00543     bar_payl->bar_cntrl = BAR_CNTL_COMPRESSED_BIT|(tid << BAR_CNTL_TID_OFT);
    00544     //BAR Info - SSC
    00545     //smallest in A-MPDU - that of 1st MPDU in AMPDU while there are no retries
    00546     bar_payl->bar_information = sn << 4;
    00547
    00548     //------------------------------------------------------------------------------------
    00549     //Format THD
    00550     //------------------------------------------------------------------------------------
    00551     // Reset chaining between THD
    00552     bar_thd->nextfrmexseq_ptr = 0;
    00553
    00554     // Set status as 0
    00555     bar_thd->statinfo = 0;
    00556
    00557     //------------------------------------------------------------------------------------
    00558     //Format policy table
    00559     //------------------------------------------------------------------------------------
    00560     // Set TX power
    00561     bar_pol->powercntrlinfo[0] = TX_PWR_LEVEL_SET(nxmac_ofdm_max_pwr_level_getf());
    00562     // Keep same bandwidth for BAR
    00563     bar_pol->ratecntrlinfo[0] = (bw << BW_TX_RCX_OFT) |
    00564                                 (FORMATMOD_NON_HT_DUP_OFDM << FORMAT_MOD_TX_RCX_OFT) |
    00565                                 (HW_RATE_24MBPS << MCS_INDEX_TX_RCX_OFT);
    00566 }
    00567
    00573 static void txl_agg_hwdesc_init(void)
    00574 {
    00575     // Reset the TX Aggregation descriptor array elements
    00576     memset(tx_agg_desc_pool, 0, sizeof(tx_agg_desc_pool));
    00577
    00578     // Initialize the descriptor pools
    00579     for (int i = 0; i < TX_AMPDU_DESC_QUEUE_CNT; i++)
    00580     {
    00581         struct co_list *list = &tx_agg_desc_pool[i];
    00582
    00583         memset((void *)tx_agg_desc_array[i], 0, tx_agg_desc_cnt[i] * sizeof(struct tx_agg_desc));
    00584         for (int j = 0; j < tx_agg_desc_cnt[i]; j++)
    00585         {
    00586             struct tx_agg_desc *agg_desc = (struct tx_agg_desc *)&tx_agg_desc_array[i][j];
    00587             struct tx_hd *a_thd = &agg_desc->a_thd;
    00588             struct tx_hd *bar_thd = &agg_desc->bar_thd;
    00589             struct bar_frame *bar_payl = &agg_desc->bar_payl;
    00590             struct tx_policy_tbl *bar_pol = &agg_desc->bar_pol_tbl;
    00591
    00592             // Remember the free list in which the A-MPDU descriptor is pushed
    00593             agg_desc->free_list = list;
    00594
    00595             // Fill payload
    00596             bar_payl->h.fctl = (MAC_FCTRL_CTRL_T | MAC_FCTRL_BAR_ST);
    00597             bar_payl->h.durid = 0x0500;
    00598
    00599             // Initialize default A-MPDU descriptor
    00600             a_thd->upatterntx = TX_HEADER_DESC_PATTERN;
    00601             a_thd->frmlifetime = 0;
    00602             a_thd->macctrlinfo2 = WHICHDESC_AMPDU_EXTRA | INTERRUPT_EN_TX;
    00603             a_thd->optlen[0] = 0;
    00604             a_thd->optlen[1] = 0;
    00605             a_thd->optlen[2] = 0;
    00606
    00607             // Initialize BAR descriptor
    00608             bar_thd->upatterntx = TX_HEADER_DESC_PATTERN;
    00609             bar_thd->frmlifetime = 0;
    00610             bar_thd->nextmpdudesc_ptr = 0;
    00611             bar_thd->policyentryaddr = CPU2HW(&agg_desc->bar_pol_tbl);
    00612             bar_thd->datastartptr = CPU2HW(bar_payl);
    00613             bar_thd->dataendptr = (uint32_t)bar_thd->datastartptr + BAR_FRM_LEN_WITH_FCS - 1;
    00614             bar_thd->first_pbd_ptr = 0;
    00615             bar_thd->optlen[0] = 0;
    00616             bar_thd->optlen[1] = 0;
    00617             bar_thd->optlen[2] = 0;
    00618
    00619             // Set frame length
    00620             bar_thd->frmlen = BAR_FRM_LEN_WITH_FCS;
    00621             // Set PHY control information
    00622             bar_thd->phyctrlinfo = 0;
    00623             // Set MAC control information 1
    00624             bar_thd->macctrlinfo1 = EXPECTED_ACK_COMPRESSED_BLOCK_ACK;
    00625             // Set WhichDescriptor field
    00626             bar_thd->macctrlinfo2 = INTERRUPT_EN_TX | WHICHDESC_UNFRAGMENTED_MSDU |
    00627                                             FRM_TYPE_CNTL | FRM_CNTL_SUBTYPE_BAR| TS_VALID_BIT;
    00628
    00629             // Initialize BAR policy table
    00630             bar_pol->upatterntx = POLICY_TABLE_PATTERN;
    00631             bar_pol->phycntrlinfo1 = phy_get_ntx() << NX_TX_PT_OFT;
    00632             bar_pol->phycntrlinfo2 = TX_NTX_2_ANTENNA_SET(phy_get_ntx());
    00633             bar_pol->maccntrlinfo1 = 0;
    00634             bar_pol->maccntrlinfo2 = 0xFFFF0704;
    00635
    00636             // Push the descriptor into the list
    00637             co_list_push_back(list, (struct co_list_hdr *) agg_desc);
    00638         }
    00639     }
    00640 }
    00641
    00647 static void txl_agg_hwdesc_reset(void)
    00648 {
    00649     // Initialize the descriptor pools
    00650     for (int i = 0; i < TX_AMPDU_DESC_QUEUE_CNT; i++)
    00651     {
    00652         struct co_list *list = &tx_agg_desc_pool[i];
    00653
    00654         // Initialize the list
    00655         co_list_init(list);
    00656
    00657         for (int j = 0; j < tx_agg_desc_cnt[i]; j++)
    00658         {
    00659             struct tx_agg_desc *agg_desc = (struct tx_agg_desc *)&tx_agg_desc_array[i][j];
    00660
    00661             // Push the descriptor into the list
    00662             co_list_push_back(list, (struct co_list_hdr *) agg_desc);
    00663         }
    00664     }
    00665 }
    00666
    00667 #if NX_MAC_HE
    00668
    00679 static bool txl_agg_is_int_or_mgt(struct txdesc *txdesc)
    00680 {
    00681     if (is_int_frame(txdesc))
    00682         return true;
    00683
    00684     #if NX_UMAC_PRESENT
    00685     if (txdesc->host.flags & TXU_CNTRL_MGMT)
    00686         return true;
    00687     #endif
    00688
    00689     return false;
    00690 }
    00691
    00703 static void txl_agg_set_uph(struct txdesc *txdesc)
    00704 {
    00705     // Go through the MPDUs until we cannot put more MPDUs due to the HE TB PPDU
    00706     // length, the timeout of the HE TB programming, or until we reach the end of the
    00707     // A-MPDU
    00708     while(1)
    00709     {
    00710         // Update the +HTC field with the UPH - if +HTC is not updated, it means that the
    00711         // buffer is not allocated yet, and we can exit the loop
    00712         if (!txl_buffer_update_htc(&txdesc->lmac.hw_desc->thd, txl_he_tb_uph_get()))
    00713             break;
    00714
    00715         // Maybe this MPDU is the last of the A-MPDU
    00716         if (is_mpdu_last(txdesc))
    00717             break;
    00718
    00719         // Get next MPDU
    00720         txdesc = tx_desc_next(txdesc);
    00721
    00722         // Sanity check - The latest MPDU we handled was not the last of the A-MPDU, so
    00723         // we should not reach the end of the list
    00724         ASSERT_ERR(txdesc != NULL);
    00725     }
    00726 }
    00727
    00750 static struct tx_hd *txl_agg_split(struct txdesc *txdesc, struct txdesc **txdesc_next,
    00751                                    uint32_t max_len, uint16_t min_mpdu_len, uint8_t ac)
    00752 {
    00753     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    00754     struct tx_agg_desc *agg_desc_old = txdesc->lmac.agg_desc;
    00755     struct tx_agg_desc *agg_desc_new;
    00756     struct tx_hd *thd_tb;
    00757     struct tx_hd *a_thd_new, *a_thd_old;
    00758     struct txdesc *txdesc_prev = NULL;
    00759     #if NX_UMAC_PRESENT
    00760     uint8_t current_cnt;
    00761     #endif
    00762     bool split = true;
    00763     bool timeout = false;
    00764
    00765     agg_desc_new = txl_agg_desc_alloc(TX_HE_TB_AMPDU_QUEUE_IDX);
    00766     ASSERT_ERR(agg_desc_new != NULL);
    00767
    00768     // Initialize some fields of the new A-MPDU descriptor from values of old one
    00769     agg_desc_new->status = agg_desc_old->status;
    00770     agg_desc_new->sta_idx = agg_desc_old->sta_idx;
    00771     agg_desc_new->tid = agg_desc_old->tid;
    00772     agg_desc_new->user_cnt = 1;
    00773
    00774     // Initialize some fields of the new A-MPDU THD from values of old one
    00775     a_thd_old = &agg_desc_old->a_thd;
    00776     a_thd_new = &agg_desc_new->a_thd;
    00777     a_thd_new->phyctrlinfo = a_thd_old->phyctrlinfo;
    00778     a_thd_new->macctrlinfo1 = a_thd_old->macctrlinfo1;
    00779     a_thd_new->macctrlinfo2 = a_thd_old->macctrlinfo2;
    00780     a_thd_new->nextmpdudesc_ptr = a_thd_old->nextmpdudesc_ptr;
    00781     a_thd_new->policyentryaddr = a_thd_old->policyentryaddr;
    00782     a_thd_new->frmlen = 0;
    00783     thd_tb = a_thd_new;
    00784
    00785     // Prepare looping across old A-MPDU
    00786     #if NX_UMAC_PRESENT
    00787     current_cnt = 0;
    00788     #endif
    00789
    00790     // Go through the A-MPDU MPDUs until we cannot put more MPDUs due to the HE TB PPDU
    00791     // length, the timeout of the HE TB programming, or until we reach the end of the
    00792     // A-MPDU
    00793     while(1)
    00794     {
    00795         struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    00796         uint16_t nb_delims_new, nb_delims_old;
    00797         uint16_t subfrm_len_new, subfrm_len_old;
    00798
    00799         // Check if the new frame is smaller than the minimum MPDU length
    00800         nb_delims_old = (txdesc->umac.flags & NB_BLANK_DELIM_MSK) >> NB_BLANK_DELIM_OFT;
    00801         subfrm_len_old = txl_mpdu_subframe_len(thd) + nb_delims_old * DELIMITER_LEN;
    00802         if (subfrm_len_old < min_mpdu_len)
    00803         {
    00804             // Compute the new number of delimiters and sub-frame length
    00805             nb_delims_new = txl_agg_mpdu_nb_delims(thd, min_mpdu_len);
    00806             subfrm_len_new = txl_mpdu_subframe_len(thd) + nb_delims_new * DELIMITER_LEN;
    00807         }
    00808         else
    00809         {
    00810             // Sub-frame is already bigger than required, let's keep it like that
    00811             subfrm_len_new = subfrm_len_old;
    00812         }
    00813
    00814         // Check if the MPDU fits into the HE TB PPDU length and ensure we will have
    00815         // enough time to chain the PPDU
    00816         if (((a_thd_new->frmlen + subfrm_len_new) > max_len) || timeout)
    00817         {
    00818             // Check if we can put at least one MPDU in the HE TB
    00819             if (txdesc_prev == NULL)
    00820             {
    00821                 // Request to disable the MU EDCA because the HE TB queue is blocked
    00822                 txl_he_mu_edca_blocked(a_thd_old, ac);
    00823
    00824                 // Free the allocated A-MPDU descriptor
    00825                 txl_agg_desc_free(agg_desc_new);
    00826
    00827                 return NULL;
    00828             }
    00829             // Check if the transmitted frame will be an A-MPDU or a singleton
    00830             else if (is_mpdu_first(txdesc_prev))
    00831             {
    00832                 // Transform the frame in singleton
    00833                 txl_agg_change_to_singleton(txdesc_prev, true);
    00834
    00835                 // For singleton sent as HE TB we need a A-MPDU descriptor attached so
    00836                 // put it back
    00837                 txdesc_prev->lmac.agg_desc = agg_desc_new;
    00838
    00839                 // The THD that needs to be chained in the HE TB is now the one attached
    00840                 // to the TX descriptor
    00841                 thd_tb = &txdesc_prev->lmac.hw_desc->thd;
    00842                 break;
    00843             }
    00844             else
    00845             {
    00846                 struct tx_hd *thd_last = &txdesc_prev->lmac.hw_desc->thd;
    00847                 #if NX_UMAC_PRESENT
    00848                 struct tx_cfm_tag *cfm = txl_cfm_tag_get(txdesc_prev);
    00849                 cfm->ampdu_size = current_cnt;
    00850                 #endif
    00851
    00852                 // Remember the last TX descriptor in the A-MPDU descriptor
    00853                 agg_desc_new->txdesc_last = txdesc_prev;
    00854
    00855                 // Mark as last MPDU
    00856                 set_mpdu_pos(txdesc_prev, WHICHDESC_AMPDU_LAST);
    00857
    00858                 // And update the MAC Control Information 2 field to the correct value
    00859                 thd_last->macctrlinfo2 = txdesc_prev->umac.flags | INTERRUPT_EN_TX;
    00860                 thd_last->nextmpdudesc_ptr = 0;
    00861                 break;
    00862             }
    00863         }
    00864
    00865         PROF_AGG_ADD_MPDU_SET();
    00866
    00867         // Update the +HTC with the UPH
    00868         txl_buffer_update_htc(thd, txl_he_tb_uph_get());
    00869
    00870         // Attach the new A-MPDU descriptor to the TX descriptor
    00871         txdesc->lmac.agg_desc = agg_desc_new;
    00872
    00873         if (subfrm_len_old < min_mpdu_len)
    00874         {
    00875             // Update number of blank delimiters
    00876             txdesc->umac.flags = thd->macctrlinfo2;
    00877             txdesc->umac.flags &= ~NB_BLANK_DELIM_MSK;
    00878             txdesc->umac.flags |= (nb_delims_new << NB_BLANK_DELIM_OFT);
    00879             thd->macctrlinfo2 = txdesc->umac.flags | INTERRUPT_EN_TX;
    00880         }
    00881         else
    00882         {
    00883             subfrm_len_old = subfrm_len_new;
    00884         }
    00885
    00886         // Add MPDU to A-MPDU
    00887         a_thd_new->frmlen += subfrm_len_new;
    00888         // Now that we have added the MPDU to the new A-MPDU, let's "remove" it from the
    00889         // old one
    00890         a_thd_old->frmlen -= subfrm_len_old;
    00891
    00892         PROF_AGG_ADD_MPDU_CLR();
    00893
    00894         // Maybe this MPDU is the last of the previously programmed A-MPDU
    00895         if (is_mpdu_last(txdesc))
    00896         {
    00897             agg_desc_new->txdesc_last = txdesc;
    00898             split = false;
    00899             break;
    00900         }
    00901
    00902         txdesc_prev = txdesc;
    00903         #if NX_UMAC_PRESENT
    00904         current_cnt++;
    00905         #endif
    00906
    00907         // If we reached the time limit, then request to exit the loop
    00908         if (nxmac_tx_hetb_rem_dur_getf() < TX_HE_TB_PROG_TIME)
    00909             timeout = true;
    00910
    00911         txdesc = tx_desc_next(txdesc);
    00912
    00913         // Sanity check - We should exit either because we reached the end of the A-MPDU
    00914         // or we filled-up completely the HE TB PPDU, but we shall never reach the end of
    00915         // the descriptor list
    00916         ASSERT_ERR(txdesc != NULL);
    00917     }
    00918
    00919     // Sanity check. The pending A-MPDU descriptor shall be the first in the list of
    00920     // descriptors. If it is not the case it means that we have probably been too late
    00921     // to handle the previous A-MPDU BAR-THD/A-THD check interrupt.
    00922     ASSERT_ERR(co_list_pick(&txlist->aggregates) == &agg_desc_old->list_hdr);
    00923
    00924     // The A-MPDU might not be split, in case its original length was longer than the
    00925     // allowed one, but new blank delimiter values have reduced it below this threshold.
    00926     // Even if this case seems unlikely, let's handle it, it has a very limited cost.
    00927     if (split)
    00928     {
    00929         // A-MPDU has been split, we'll have one more PPDU in the TX path
    00930         txlist->ppdu_cnt++;
    00931
    00932         // Maybe the first MPDU after the newly created A-MPDU was the last in the
    00933         // split A-MPDU. In such case we need to pop its descriptor from the aggregates
    00934         // list before pushing the new descriptor. The releasing of the old descriptor
    00935         // will be done after the new A-MPDU is chained.
    00936         if (is_mpdu_last(txdesc))
    00937         {
    00938             co_list_pop_front(&txlist->aggregates);
    00939         }
    00940         *txdesc_next = txdesc;
    00941     }
    00942     else
    00943     {
    00944         // A-MPDU was not split, so free the descriptor used previously
    00945         co_list_pop_front(&txlist->aggregates);
    00946         txl_agg_desc_free(agg_desc_old);
    00947         *txdesc_next = tx_desc_next(txdesc);
    00948
    00949         // Remove BAR THD from last MPDU
    00950         txdesc->lmac.hw_desc->thd.nextmpdudesc_ptr = 0;
    00951     }
    00952
    00953     // Push the new A-MPDU descriptor in the list
    00954     co_list_push_front(&txlist->aggregates, &agg_desc_new->list_hdr);
    00955
    00956     return (thd_tb);
    00957 }
    00958
    00983 static bool txl_agg_he_tb_prep_for_cat(struct txdesc *txdesc1, struct txdesc *txdesc2,
    00984                                        uint8_t ac)
    00985 {
    00986     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    00987     struct txl_agg_build_tag *agg;
    00988     struct txdesc *txdesc_first, *txdesc_last;
    00989     struct tx_hd *thd;
    00990     struct tx_pbd *tbd;
    00991     struct txl_buffer_tag *buffer;
    00992     #if NX_UMAC_PRESENT
    00993     struct tx_cfm_tag *cfm;
    00994     #endif
    00995     struct tx_agg_desc *agg_desc = txdesc2->lmac.agg_desc;
    00996
    00997     // Check if both PPDUs are compatible
    00998     if ((txdesc1->host.staid != txdesc2->host.staid) ||
    00999         (txdesc1->host.tid != txdesc2->host.tid))
    01000         return false;
    01001
    01002     // There is a gray zone when the first MPDU of a A-MPDU has been allocated, or enough
    01003     // buffer allocation has been done for the A-MPDU to be appended, but we are still
    01004     // waiting for the DMA interrupt following one of the previous events. The handling of
    01005     // the awaited DMA interrupt could cause an erroneous chaining of the A-MPDU so don't
    01006     // do the concatenation in that case
    01007     if ((txl_buffer_get(txdesc2) && !(agg_desc->status & AGG_FIRST_DOWNLOADED)) ||
    01008         ((agg_desc->status & (AGG_ALLOC | AGG_DOWNLOADED)) == AGG_ALLOC))
    01009         return false;
    01010
    01011     if (agg_desc->status & AGG_FORMATTED)
    01012         return true;
    01013
    01014     agg = &txlist->agg[0];
    01015
    01016     // No concatenation if finished A-MPDU would result into a singleton MPDU
    01017     if (agg->curr_cnt == 1)
    01018         return false;
    01019
    01020     txdesc_last = agg->txdesc_last;
    01021     thd = &txdesc_last->lmac.hw_desc->thd;
    01022     buffer = txl_buffer_get(txdesc_last);
    01023     tbd = HW2CPU(thd->first_pbd_ptr);
    01024
    01025     // If buffer is already allocated for this descriptor, check if it is
    01026     // already downloaded or not
    01027     if ((buffer != NULL) && (tbd->upatterntx != TX_PAYLOAD_DESC_PATTERN))
    01028         return false;
    01029
    01030     // Check if we still have enough time to do the finishing procedure
    01031     if (nxmac_tx_hetb_rem_dur_getf() <= (TX_HE_TB_PROG_TIME + TX_AGG_FINISH_DUR))
    01032         return false;
    01033
    01034     // For profiling
    01035     PROF_AGG_FINISH_AMPDU_SET();
    01036
    01037     txdesc_first = txdesc2;
    01038     #if NX_UMAC_PRESENT
    01039     cfm = txl_cfm_tag_get(txdesc_last);
    01040     #endif
    01041
    01042     // Re-mark last MPDU as LAST not MIDDLE
    01043     set_mpdu_pos(txdesc_last, WHICHDESC_AMPDU_LAST);
    01044
    01045     agg_desc->txdesc_last = txdesc_last;
    01046     txl_he_txop_dur_rtscts_thres_ampdu_check(agg->txdesc_first);
    01047
    01048     #if NX_UMAC_PRESENT
    01049     cfm->ampdu_size = agg->curr_cnt;
    01050     #endif
    01051
    01052     //BAR DESCRIPTOR
    01053     txl_fill_bar(agg_desc, txdesc_last->umac.sn_win, agg_desc->sta_idx, agg_desc->tid, agg->bw);
    01054
    01055     // Chain the BAR descriptor to the last MPDU
    01056     thd->nextmpdudesc_ptr = CPU2HW(&agg_desc->bar_thd);
    01057
    01058     // And update the MAC Control Information 2 field to the correct value
    01059     thd->macctrlinfo2 = txdesc_last->umac.flags | INTERRUPT_EN_TX;
    01060
    01061     // Current aggregate is now completed
    01062     agg_desc->status |= AGG_FORMATTED;
    01063
    01064     // If buffer is already allocated for this descriptor, check if it is
    01065     // already downloaded or not
    01066     if (buffer != NULL)
    01067     {
    01068         // Check if enough data is already allocated
    01069         if (!(agg_desc->status & AGG_ALLOC))
    01070         {
    01071             // If this is not the case, check if the first MPDU is already downloaded
    01072             if (!(agg_desc->status & AGG_FIRST_DOWNLOADED))
    01073             {
    01074                 // The first MPDU is not downloaded, so we will wait for its DMA
    01075                 // interrupt and do the A-MPDU chaining at that moment
    01076                 struct txl_buffer_tag *buffer_first = txl_buffer_get(txdesc_first);
    01077                 buffer_first->flags |= BUF_ALLOC_OK;
    01078             }
    01079             else
    01080                 // The first MPDU is already downloaded, so we consider that we can
    01081                 // chain the A-MPDU immediately
    01082                 agg_desc->status |= AGG_DOWNLOADED;
    01083         }
    01084     }
    01085
    01086     // Check if aggregate descriptor is ready to be programmed
    01087     if (agg_desc->status & AGG_DOWNLOADED)
    01088     {
    01089         // Re-set the PHY flags in the policy table as they might have been changed
    01090         // during the TXOP duration RTS/CTS threshold check
    01091         struct tx_policy_tbl *pt = HW2CPU(agg_desc->a_thd.policyentryaddr);
    01092         pt->ratecntrlinfo[0] = agg->txdesc_first->umac.phy_flags;
    01093     }
    01094
    01095     // Put aggregate in used list
    01096     co_list_push_back(&txl_cntrl_env.txlist[ac].aggregates, &agg_desc->list_hdr);
    01097
    01098     // We will now have some data programmed for transmission, so we can activate the A-MPDU
    01099     // optimization
    01100     txl_cntrl_env.txlist[ac].ppdu_cnt++;
    01101
    01102     // Reinitialize the build state machine
    01103     agg->desc = NULL;
    01104
    01105     // For profiling
    01106     PROF_AGG_FINISH_AMPDU_CLR();
    01107
    01108     return true;
    01109 }
    01110
    01132 static bool txl_agg_he_tb_cat_ampdu_mpdu(struct txdesc *txdesc_ampdu, struct txdesc **txdesc_next,
    01133                                          uint32_t max_len, uint16_t min_mpdu_len, uint8_t ac)
    01134 {
    01135     struct tx_agg_desc *agg_desc = txdesc_ampdu->lmac.agg_desc;
    01136     struct txdesc *txdesc_mpdu = *txdesc_next;
    01137     struct txdesc *txdesc_last = agg_desc->txdesc_last;
    01138     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01139     struct tx_hd *thd = &txdesc_mpdu->lmac.hw_desc->thd;
    01140     struct tx_hd *thd_last = &txdesc_last->lmac.hw_desc->thd;
    01141     uint16_t nb_delims_new, subfrm_len_new;
    01142     struct tx_hd *a_thd;
    01143
    01144     // Check if MPDU is compatible with the A-MPDU
    01145     if ((agg_desc->sta_idx != txdesc_mpdu->host.staid) ||
    01146         (agg_desc->tid != txdesc_mpdu->host.tid))
    01147         return false;
    01148
    01149     a_thd = &agg_desc->a_thd;
    01150     thd = &txdesc_mpdu->lmac.hw_desc->thd;
    01151
    01152     // Re-add the +HTC length and order bit in the MPDU buffer if necessary
    01153     txl_buffer_add_htc(thd);
    01154
    01155     // Compute the new number of delimiters and the sub-frame length
    01156     nb_delims_new = txl_agg_mpdu_nb_delims(thd, min_mpdu_len);
    01157     subfrm_len_new = txl_mpdu_subframe_len(thd) + nb_delims_new * DELIMITER_LEN;
    01158
    01159     if ((a_thd->frmlen + subfrm_len_new) > max_len)
    01160         return false;
    01161
    01162     // PPDUs will be concatenated, we'll have one less in the TX path
    01163     txlist->ppdu_cnt--;
    01164
    01165     set_mpdu_pos(txdesc_last, WHICHDESC_AMPDU_INT);
    01166     thd_last->macctrlinfo2 = txdesc_last->umac.flags | INTERRUPT_EN_TX;
    01167     thd_last->nextmpdudesc_ptr = CPU2HW(thd);
    01168
    01169     set_mpdu_pos(txdesc_mpdu, WHICHDESC_AMPDU_LAST);
    01170     txdesc_mpdu->umac.flags |= (nb_delims_new << NB_BLANK_DELIM_OFT) | UNDER_BA_SETUP_BIT;
    01171     thd->macctrlinfo2 = txdesc_mpdu->umac.flags | INTERRUPT_EN_TX;
    01172     thd->nextfrmexseq_ptr = 0;
    01173     thd->policyentryaddr = 0;
    01174
    01175     a_thd->frmlen += subfrm_len_new;
    01176
    01177     txdesc_mpdu->lmac.agg_desc = agg_desc;
    01178     agg_desc->txdesc_last = txdesc_mpdu;
    01179
    01180     *txdesc_next = tx_desc_next(agg_desc->txdesc_last);
    01181
    01182     // Update the +HTC with the UPH
    01183     txl_buffer_update_htc(thd, txl_he_tb_uph_get());
    01184
    01185     #if NX_UMAC_PRESENT
    01186     txl_cfm_tag_get(txdesc_mpdu)->ampdu_size = txl_cfm_tag_get(txdesc_last)->ampdu_size + 1;
    01187     #endif
    01188
    01189     return true;
    01190 }
    01191
    01213 static bool txl_agg_he_tb_cat_mpdu_ampdu(struct txdesc *txdesc_mpdu, struct txdesc **txdesc_next,
    01214                                          uint32_t max_len, uint16_t min_mpdu_len, uint8_t ac)
    01215 {
    01216     struct txdesc *txdesc_ampdu = *txdesc_next;
    01217     struct tx_agg_desc *agg_desc = txdesc_ampdu->lmac.agg_desc;
    01218     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01219     struct tx_hd *thd = &txdesc_mpdu->lmac.hw_desc->thd;
    01220     uint16_t nb_delims_new, subfrm_len_new;
    01221     struct tx_hd *a_thd;
    01222
    01223     // Ensure the A-MPDU can be concatenated to the first PPDU
    01224     if (!txl_agg_he_tb_prep_for_cat(txdesc_mpdu, txdesc_ampdu, ac))
    01225         return false;
    01226
    01227     a_thd = &agg_desc->a_thd;
    01228     thd = &txdesc_mpdu->lmac.hw_desc->thd;
    01229
    01230     // Compute the new number of delimiters and the sub-frame length
    01231     nb_delims_new = txl_agg_mpdu_nb_delims(thd, min_mpdu_len);
    01232     subfrm_len_new = txl_mpdu_subframe_len(thd) + nb_delims_new * DELIMITER_LEN;
    01233
    01234     // PPDUs will be concatenated, we'll have one less in the TX path
    01235     txlist->ppdu_cnt--;
    01236
    01237     thd->nextmpdudesc_ptr = a_thd->nextmpdudesc_ptr;
    01238     a_thd->frmlen += subfrm_len_new;
    01239     a_thd->nextmpdudesc_ptr = CPU2HW(thd);
    01240
    01241     txdesc_mpdu->lmac.agg_desc = agg_desc;
    01242     set_mpdu_pos(txdesc_mpdu, WHICHDESC_AMPDU_FIRST);
    01243     txdesc_mpdu->umac.flags |= (nb_delims_new << NB_BLANK_DELIM_OFT) | UNDER_BA_SETUP_BIT;
    01244     thd->macctrlinfo2 = txdesc_mpdu->umac.flags | INTERRUPT_EN_TX;
    01245     thd->policyentryaddr = 0;
    01246
    01247     set_mpdu_pos(txdesc_ampdu, WHICHDESC_AMPDU_INT);
    01248     txdesc_ampdu->lmac.hw_desc->thd.macctrlinfo2 = txdesc_ampdu->umac.flags | INTERRUPT_EN_TX;
    01249
    01250     #if NX_UMAC_PRESENT
    01251     txl_cfm_tag_get(agg_desc->txdesc_last)->ampdu_size++;
    01252     #endif
    01253
    01254     if (a_thd->frmlen > max_len)
    01255     {
    01256         txl_agg_split(txdesc_mpdu, txdesc_next, max_len, min_mpdu_len, ac);
    01257         return false;
    01258     }
    01259
    01260     // Update the UPH in all A-MPDU constituent MPDUs
    01261     txl_agg_set_uph(txdesc_ampdu);
    01262
    01263     *txdesc_next = tx_desc_next(agg_desc->txdesc_last);
    01264
    01265     return true;
    01266 }
    01267
    01291 static bool txl_agg_he_tb_cat_ampdu(struct txdesc *txdesc, struct txdesc **txdesc_next,
    01292                                     uint32_t max_len, uint16_t min_mpdu_len, uint8_t ac)
    01293 {
    01294     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01295     struct txdesc *txdesc_n = *txdesc_next;
    01296     struct tx_agg_desc *agg_desc_n = txdesc_n->lmac.agg_desc;
    01297     struct tx_agg_desc *agg_desc = txdesc->lmac.agg_desc;
    01298     struct tx_hd *a_thd, *a_thd_n;
    01299     struct txdesc *txdesc_last = agg_desc->txdesc_last;
    01300     struct tx_hd *thd_n = &txdesc_n->lmac.hw_desc->thd;
    01301     bool split = true;
    01302     struct txdesc *txdesc_prev = NULL;
    01303     #if NX_UMAC_PRESENT
    01304     uint8_t current_cnt;
    01305     #endif
    01306
    01307     // Pop the first aggregate descriptor as we will have to pop also the next one
    01308     co_list_pop_front(&txlist->aggregates);
    01309
    01310     // Ensure the A-MPDU can be concatenated to the first PPDU
    01311     if (!txl_agg_he_tb_prep_for_cat(txdesc, txdesc_n, ac))
    01312     {
    01313         co_list_push_front(&txlist->aggregates, &agg_desc->list_hdr);
    01314         return false;
    01315     }
    01316
    01317     a_thd_n = &agg_desc_n->a_thd;
    01318     a_thd = &agg_desc->a_thd;
    01319
    01320     #if NX_UMAC_PRESENT
    01321     current_cnt = txl_cfm_tag_get(txdesc_last)->ampdu_size;
    01322     #endif
    01323
    01324     // Chain the MPDU
    01325     txdesc_last->lmac.hw_desc->thd.nextmpdudesc_ptr = CPU2HW(thd_n);
    01326     set_mpdu_pos(txdesc_last, WHICHDESC_AMPDU_INT);
    01327     txdesc_last->lmac.hw_desc->thd.macctrlinfo2 = txdesc_last->umac.flags | INTERRUPT_EN_TX;
    01328     set_mpdu_pos(txdesc_n, WHICHDESC_AMPDU_INT);
    01329     thd_n->macctrlinfo2 = (*txdesc_next)->umac.flags | INTERRUPT_EN_TX;
    01330
    01331     // Go through the second A-MPDU MPDUs and update them to be part of the first A-MPDU,
    01332     // until we cannot put more MPDUs due to the HE TB PPDU length, the timeout of the HE
    01333     // TB programming, or until we reach the end of the second A-MPDU
    01334     while(1)
    01335     {
    01336         struct tx_hd *thd = &txdesc_n->lmac.hw_desc->thd;
    01337         uint16_t nb_delims_new, nb_delims_old;
    01338         uint16_t subfrm_len_new, subfrm_len_old;
    01339
    01340         // Check if the new frame is smaller than the minimum MPDU length
    01341         nb_delims_old = (txdesc_n->umac.flags & NB_BLANK_DELIM_MSK) >> NB_BLANK_DELIM_OFT;
    01342         subfrm_len_old = txl_mpdu_subframe_len(thd) + nb_delims_old * DELIMITER_LEN;
    01343         if (subfrm_len_old < min_mpdu_len)
    01344         {
    01345             // Compute the new number of delimiters and the sub-frame length
    01346             nb_delims_new = txl_agg_mpdu_nb_delims(thd, min_mpdu_len);
    01347             subfrm_len_new = txl_mpdu_subframe_len(thd) + nb_delims_new * DELIMITER_LEN;
    01348         }
    01349         else
    01350         {
    01351             // Sub-frame is already bigger than required, let's keep it like that
    01352             subfrm_len_new = subfrm_len_old;
    01353         }
    01354
    01355         // Check if the MPDU fits into the HE TB PPDU length and ensure we will have
    01356         // enough time to chain the PPDU
    01357         if (((a_thd->frmlen + subfrm_len_new) > max_len) ||
    01358             (nxmac_tx_hetb_rem_dur_getf() < TX_HE_TB_PROG_TIME))
    01359         {
    01360             // Check if we can put at least one MPDU in the HE TB
    01361             if (txdesc_prev == NULL)
    01362             {
    01363                 // No MPDU of the second A-MPDU could be concatenated - Revert the
    01364                 // chaining previously done
    01365                 txdesc_last->lmac.hw_desc->thd.nextmpdudesc_ptr = 0;
    01366                 set_mpdu_pos(txdesc_last, WHICHDESC_AMPDU_LAST);
    01367                 txdesc_last->lmac.hw_desc->thd.macctrlinfo2 = txdesc_last->umac.flags | INTERRUPT_EN_TX;
    01368                 set_mpdu_pos(txdesc_n, WHICHDESC_AMPDU_FIRST);
    01369                 thd->macctrlinfo2 = txdesc_n->umac.flags | INTERRUPT_EN_TX;
    01370                 // Push back the A-MPDU descriptor in the list
    01371                 co_list_push_front(&txlist->aggregates, &agg_desc->list_hdr);
    01372                 *txdesc_next = txdesc_n;
    01373                 return false;
    01374             }
    01375             else
    01376             {
    01377                 struct tx_hd *thd_last = &txdesc_prev->lmac.hw_desc->thd;
    01378                 #if NX_UMAC_PRESENT
    01379                 struct tx_cfm_tag *cfm = txl_cfm_tag_get(txdesc_prev);
    01380                 cfm->ampdu_size = current_cnt;
    01381                 #endif
    01382
    01383                 // Remember the last TX descriptor in the A-MPDU descriptor
    01384                 agg_desc->txdesc_last = txdesc_prev;
    01385
    01386                 // Mark as last MPDU
    01387                 set_mpdu_pos(txdesc_prev, WHICHDESC_AMPDU_LAST);
    01388
    01389                 // And update the MAC Control Information 2 field to the correct value
    01390                 thd_last->macctrlinfo2 = txdesc_prev->umac.flags | INTERRUPT_EN_TX;
    01391                 thd_last->nextmpdudesc_ptr = 0;
    01392                 break;
    01393             }
    01394         }
    01395
    01396         PROF_AGG_ADD_MPDU_SET();
    01397
    01398         // Update the +HTC with the UPH
    01399         txl_buffer_update_htc(thd, txl_he_tb_uph_get());
    01400
    01401         // Attach the new A-MPDU descriptor to the TX descriptor
    01402         txdesc_n->lmac.agg_desc = agg_desc;
    01403
    01404         if (subfrm_len_old < min_mpdu_len)
    01405         {
    01406             // Update number of blank delimiters
    01407             txdesc_n->umac.flags = thd->macctrlinfo2;
    01408             txdesc_n->umac.flags &= ~NB_BLANK_DELIM_MSK;
    01409             txdesc_n->umac.flags |= (nb_delims_new << NB_BLANK_DELIM_OFT);
    01410             thd->macctrlinfo2 = txdesc_n->umac.flags | INTERRUPT_EN_TX;
    01411         }
    01412         else
    01413         {
    01414             subfrm_len_old = subfrm_len_new;
    01415         }
    01416
    01417         // Add MPDU to A-MPDU
    01418         a_thd->frmlen += subfrm_len_new;
    01419         // Now that we have added the MPDU to the new A-MPDU, let's "remove" it from the
    01420         // old one
    01421         a_thd_n->frmlen -= subfrm_len_old;
    01422
    01423         PROF_AGG_ADD_MPDU_CLR();
    01424
    01425         // Maybe this MPDU is the last of the previously programmed A-MPDU
    01426         if (is_mpdu_last(txdesc_n))
    01427         {
    01428             agg_desc->txdesc_last = txdesc_n;
    01429             split = false;
    01430             break;
    01431         }
    01432
    01433         txdesc_prev = txdesc_n;
    01434
    01435         #if NX_UMAC_PRESENT
    01436         current_cnt++;
    01437         #endif
    01438
    01439         txdesc_n = tx_desc_next(txdesc_n);
    01440
    01441         // Sanity check - We should exit either because we reached the end of the A-MPDU
    01442         // or we filled-up completely the HE TB PPDU, but we shall never reach the end of
    01443         // the descriptor list
    01444         ASSERT_ERR(txdesc_n != NULL);
    01445     }
    01446
    01447     // The A-MPDU might not be split, in case its original length was longer than the
    01448     // allowed one, but new blank delimiter values have reduced it below this threshold.
    01449     // Even if this case seems unlikely, let's handle it, it has a very limited cost.
    01450     if (split)
    01451     {
    01452         // Maybe the first MPDU after the newly created A-MPDU was the last in the
    01453         // split A-MPDU. In such case we need to pop its descriptor from the aggregates
    01454         // list before pushing the new descriptor. The releasing of the old descriptor
    01455         // will be done after the new A-MPDU is chained.
    01456         if (is_mpdu_last(txdesc_n))
    01457         {
    01458             co_list_pop_front(&txlist->aggregates);
    01459         }
    01460         *txdesc_next = txdesc_n;
    01461     }
    01462     else
    01463     {
    01464         // A-MPDU has been fully concatenated, we'll have one less PPDU in the TX path
    01465         txlist->ppdu_cnt--;
    01466
    01467         // A-MPDU was not split, so free the descriptor used previously
    01468         co_list_pop_front(&txlist->aggregates);
    01469         txl_agg_desc_free(agg_desc_n);
    01470         *txdesc_next = tx_desc_next(txdesc_n);
    01471
    01472         // Remove BAR THD from last MPDU
    01473         txdesc_n->lmac.hw_desc->thd.nextmpdudesc_ptr = 0;
    01474     }
    01475
    01476     // Push the new A-MPDU descriptor in the list
    01477     co_list_push_front(&txlist->aggregates, &agg_desc->list_hdr);
    01478
    01479     return !split;
    01480 }
    01481 #endif // NX_MAC_HE
    01482
    01483 #if RW_MUMIMO_TX_EN
    01484
    01492 static void txl_mumimo_convert(uint8_t ac, uint8_t user_pos)
    01493 {
    01494     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01495     struct txl_mumimo_build_info_tag *mumimo = &txlist->mumimo;
    01496     struct txdesc *txdesc = (struct txdesc *)co_list_pick(&mumimo->tx[user_pos]);
    01497     struct tx_agg_desc *agg_desc = txdesc->lmac.agg_desc;
    01498
    01499     // We will now have some data programmed for transmission, so we can activate the A-MPDU
    01500     // optimization
    01501     txlist->ppdu_cnt++;
    01502
    01503     // Chain the data THD to the primary A-MPDU THD
    01504     if (is_mpdu_agg(txdesc))
    01505     {
    01506         struct txdesc *last = (struct txdesc *)co_list_pick_last(&mumimo->tx[user_pos]);
    01507         struct tx_hd *thd = &last->lmac.hw_desc->thd;
    01508
    01509         // Chain the BAR descriptor to the last MPDU
    01510         thd->nextmpdudesc_ptr = CPU2HW(&agg_desc->bar_thd);
    01511
    01512         // Put secondary aggregate in used list
    01513         co_list_push_back(&txlist->aggregates, &agg_desc->list_hdr);
    01514     }
    01515     else
    01516     {
    01517         // Put back the aggregate in the free list
    01518         txl_agg_desc_free(agg_desc);
    01519
    01520         // Unchain the aggregate descriptor from the TX descriptor
    01521         txdesc->lmac.agg_desc = NULL;
    01522     }
    01523
    01524     // Check if we can allocate TX buffers for the MPDUs of this user
    01525     while (txlist->first_to_download[0] == NULL)
    01526     {
    01527         if (!txl_payload_alloc(txdesc, ac, 0))
    01528             break;
    01529
    01530         txdesc = tx_desc_next(txdesc);
    01531         if (txdesc == NULL)
    01532             break;
    01533     }
    01534     // Concatenate the MU-MIMO TX descriptor list with the transmitting list
    01535     co_list_concat(&txlist->transmitting[0], &mumimo->tx[user_pos]);
    01536 }
    01537
    01551 static uint32_t txl_mumimo_duration_ns(uint32_t length, uint8_t mcs, bool sgi,
    01552                                        uint8_t bw, uint8_t nss)
    01553 {
    01554     // Total TXTIME
    01555     uint32_t duration;
    01556
    01557     // IEEE coresponding variables
    01558     uint32_t ndbps;
    01559     uint32_t nes;
    01560     uint32_t bit_cnt;
    01561
    01562     // Number of DATA BIT PER SYMBOL
    01563     ndbps = ((uint16_t)nss) * VHT_NDBPS[bw][mcs];
    01564
    01565     // nES = 6*NES
    01566     // VHT IEEE P802.11ac D4.0
    01567     // 20MHz and  40MHz Bandwidth
    01568     if (bw < BW_80MHZ)
    01569     {
    01570         nes = (ndbps <= 2160) ? 6 : (ndbps <= 4320) ? 12 : 18;
    01571     }
    01572     // 80MHz Bandwidth
    01573     else if (bw == BW_80MHZ)
    01574     {
    01575         if ((nss == 7) && (ndbps == 8190))
    01576         {
    01577             nes = 36;
    01578         }
    01579         else if((nss == 7) && (ndbps == 2457))
    01580         {
    01581             nes = 18;
    01582         }
    01583         else
    01584         {
    01585             nes = (ndbps <= 2106) ? 6  :
    01586                   (ndbps <= 4212) ? 12 :
    01587                   (ndbps <= 6318) ? 18 :
    01588                   (ndbps <= 8424) ? 24 :
    01589                                     36;
    01590         }
    01591     }
    01592     // 160MHz Bandwidth
    01593     else
    01594     {
    01595         if (((nss == 4) && (ndbps == 9360)) || ((nss == 7) && (ndbps == 9828)))
    01596         {
    01597             nes = 36;
    01598         }
    01599         else if ((ndbps == 14040) && ((nss == 5) || (nss == 6)))
    01600         {
    01601             nes = 48;
    01602         }
    01603         else if ((nss == 7) && (ndbps == 16380))
    01604         {
    01605             nes = 54;
    01606         }
    01607         else
    01608         {
    01609             nes = (ndbps <= 2106)  ? 6  :
    01610                   (ndbps <= 4212)  ? 12 :
    01611                   (ndbps <= 6318)  ? 18 :
    01612                   (ndbps <= 8424)  ? 24 :
    01613                   (ndbps <= 10530) ? 30 :
    01614                   (ndbps <= 12636) ? 36 :
    01615                   (ndbps <= 14742) ? 42 :
    01616                   (ndbps <= 16848) ? 48 :
    01617                   (ndbps <= 18720) ? 54 :
    01618                                      72 ;
    01619         }
    01620     }
    01621
    01622     // Data Time on Air for a MUMIMO A-MPDU
    01623     // 1) The full data length in bit is length_i*8 + 16 bits of service field + 6*nES tail bits (IEEE Std 802.11 - 2012 Chapter 18.3.5.x)
    01624     // 2) Convert into Nsymbols according to the MCS (round up the value)
    01625     // 3) Convert Nsymbols into time duration according to Normal GI or Short GI (round up the value)
    01626
    01627     // Total of Symbols = (length_i*8 + 16 + 6*NES) / NDBPS
    01628     bit_cnt = (length << 3) + 16 + nes;
    01629     duration =  bit_cnt / ndbps;
    01630     duration += (duration * ndbps < bit_cnt) ? 1 : 0;
    01631
    01632     // Compute actual duration
    01633     duration *= sgi ? 3600 : 4000;
    01634
    01635     // Return the value
    01636     return (duration);
    01637 }
    01638
    01647 static void txl_mumimo_ampdu_finish(uint8_t ac, uint8_t user_pos)
    01648 {
    01649     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01650     struct txl_agg_build_tag *agg = &txlist->agg[user_pos];
    01651     struct tx_agg_desc *agg_desc = agg->desc;
    01652     struct txdesc *txdesc = agg->txdesc_last;
    01653     uint32_t length;
    01654
    01655     // For profiling
    01656     PROF_AGG_FINISH_AMPDU_SET();
    01657     PROF_MU_USER_POS_SET(user_pos);
    01658
    01659     // Check if the A-MPDU has only one MPDU
    01660     if (agg->curr_cnt == 1)
    01661     {
    01662         struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    01663
    01664         // In such case, transform it to a singleton MPDU
    01665         txdesc->umac.flags = WHICHDESC_UNFRAGMENTED_MSDU;
    01666
    01667         // Clear the next_mpdu field (that was pointing to the BAR THD)
    01668         thd->nextmpdudesc_ptr = 0;
    01669         // Update the MAC Control Information 2 field to the correct value
    01670         thd->macctrlinfo2 = WHICHDESC_UNFRAGMENTED_MSDU | INTERRUPT_EN_TX;
    01671
    01672         // Get the length of the packet
    01673         length = thd->frmlen;
    01674     }
    01675     else
    01676     {
    01677         struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    01678         struct tx_hd *a_thd = &agg_desc->a_thd;
    01679
    01680         // Re-mark last MPDU as LAST not MIDDLE
    01681         set_mpdu_pos(txdesc, WHICHDESC_AMPDU_LAST);
    01682         #if NX_UMAC_PRESENT
    01683         txdesc->lmac.hw_desc->cfm.ampdu_size = agg->curr_cnt;
    01684         #endif
    01685
    01686         // And update the MAC Control Information 2 field to the correct value
    01687         thd->macctrlinfo2 = txdesc->umac.flags | INTERRUPT_EN_TX;
    01688
    01689         // Current aggregate is now completed
    01690         agg_desc->status |= AGG_FORMATTED;
    01691
    01692         // Get the length of the A-MPDU
    01693         length = a_thd->frmlen;
    01694     }
    01695
    01696     // BAR DESCRIPTOR
    01697     txl_fill_bar(agg_desc, txdesc->umac.sn_win, agg_desc->sta_idx, agg_desc->tid, agg->bw);
    01698
    01699     // Indicate that this user position is now closed for this MU-MIMO PPDU
    01700     txlist->mumimo.open &= ~CO_BIT(user_pos);
    01701
    01702     // Save the A-MPDU length (used later for duration computation)
    01703     txlist->mumimo.length[user_pos] = length;
    01704
    01705     // Reinitialize the build state machine
    01706     agg->desc = NULL;
    01707
    01708     // For profiling
    01709     PROF_AGG_FINISH_AMPDU_CLR();
    01710 }
    01711
    01728 static int txl_mumimo_check(struct txdesc * txdesc, uint8_t ac, uint8_t *user_pos)
    01729 {
    01730     int status = SU_PACKET;
    01731     uint8_t group_id = get_group_id(txdesc);
    01732     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01733
    01734     // Check if the frame can be MU-MIMOed
    01735     if (is_mpdu_agg(txdesc) && is_mumimo_group_id(group_id) &&
    01736         bfr_is_calibrated(txdesc->host.staid))
    01737     {
    01738         // Frame can be MU-MIMOed
    01739         status = MU_PACKET;
    01740         *user_pos = get_user_pos(txdesc);
    01741
    01742         // Check if some users are already part of an ongoing construction
    01743         if (txlist->mumimo.users)
    01744         {
    01745             // Check if the pushed frame is part of the same group
    01746             if (group_id != txlist->mumimo.group_id)
    01747             {
    01748                 // Close this user position
    01749                 if (txlist->mumimo.users & CO_BIT(*user_pos))
    01750                     txl_mumimo_ampdu_finish(ac, *user_pos);
    01751                 else
    01752                     txlist->mumimo.open &= ~CO_BIT(*user_pos);
    01753
    01754                 // Check if the other user positions are still open
    01755                 if (txlist->mumimo.open & txlist->mumimo.users)
    01756                 {
    01757                     // We could still receive packets on other user positions,
    01758                     // so save the current packet
    01759                     ASSERT_ERR(txlist->mumimo.txdesc[*user_pos] == NULL);
    01760                     txlist->mumimo.txdesc[*user_pos] = txdesc;
    01761                     status = MU_PAUSED;
    01762                 }
    01763                 else
    01764                 {
    01765                     // Close the MU-MIMO PPDU under construction
    01766                     txl_agg_mumimo_close(ac);
    01767                     return (MU_RESTART_CHECK);
    01768                 }
    01769             }
    01770             else
    01771             {
    01772                 txdesc->umac.phy_flags &= VHT_MCS_MASK;
    01773                 txdesc->umac.phy_flags |= txlist->mumimo.phy_flags;
    01774             }
    01775         }
    01776         else
    01777         {
    01778             // Start a new MU-MIMO PPDU
    01779             PROF_MU_PPDU_START_SET();
    01780             txlist->mumimo.group_id = group_id;
    01781             // Remember the BW and GI of the actual transmission
    01782             txlist->mumimo.phy_flags = txdesc->umac.phy_flags & ~MCS_INDEX_TX_RCX_MASK;
    01783             txdesc->umac.phy_flags &= ~VHT_NSS_MASK;
    01784             PROF_MU_PPDU_START_CLR();
    01785         }
    01786
    01787         PROF_MU_USER_POS_SET(*user_pos);
    01788     }
    01789     else
    01790     {
    01791         // Check if a MU-MIMO PPDU is under construction
    01792         if (txlist->mumimo.users)
    01793         {
    01794             *user_pos = get_user_pos(txdesc);
    01795             // Close this user position
    01796             if (txlist->mumimo.users & CO_BIT(*user_pos))
    01797                 txl_mumimo_ampdu_finish(ac, *user_pos);
    01798             else
    01799                 txlist->mumimo.open &= ~CO_BIT(*user_pos);
    01800
    01801             // Check if the other user positions are still open
    01802             if (txlist->mumimo.open & txlist->mumimo.users)
    01803             {
    01804                 // We could still receive packets on other user positions,
    01805                 // so save the current packet
    01806                 ASSERT_ERR(txlist->mumimo.txdesc[*user_pos] == NULL);
    01807                 txlist->mumimo.txdesc[*user_pos] = txdesc;
    01808                 status = MU_PAUSED;
    01809             }
    01810             else
    01811             {
    01812                 // Close the MU-MIMO PPDU under construction
    01813                 txl_agg_mumimo_close(ac);
    01814                 return (MU_RESTART_CHECK);
    01815             }
    01816         }
    01817         else
    01818         {
    01819             *user_pos = 0;
    01820         }
    01821     }
    01822
    01823     return (status);
    01824 }
    01825
    01838 static void txl_mumimo_secondary_done(uint8_t access_category, uint8_t user_idx)
    01839 {
    01840     struct txl_list *txlist = &txl_cntrl_env.txlist[access_category];
    01841
    01842     // Sanity check - This handler is only for secondary users
    01843     ASSERT_ERR(user_idx != 0);
    01844
    01845     // Walk through the list of completed SW descriptors
    01846     while (1)
    01847     {
    01848         struct txdesc *txdesc;
    01849         struct tx_hd *txhd;
    01850         uint32_t txstatus;
    01851         struct tx_agg_desc *agg_desc;
    01852
    01853         //pick first txdesc in transmitting list to check its doneTx status
    01854         txdesc = (struct txdesc *)co_list_pick(&(txlist->transmitting[user_idx]));
    01855
    01856         // a descriptor is waiting to be confirmed
    01857         if (txdesc == NULL)
    01858             break;
    01859
    01860         txhd = &txdesc->lmac.hw_desc->thd;
    01861         txstatus = txhd->statinfo;
    01862         agg_desc = txdesc->lmac.agg_desc;
    01863
    01864         //---------------
    01865         //STATUS ANALYSIS
    01866         //---------------
    01867         // Check the doneTx status bit
    01868         if (txstatus & DESC_DONE_TX_BIT)
    01869         {
    01870             // For profiling
    01871             PROF_AGG_SMPDU_DONETX_SET();
    01872
    01873             PROF_MU_USER_POS_IRQ_SET(get_user_pos(txdesc));
    01874
    01875             #if !NX_FULLY_HOSTED
    01876             // Free the MPDU buffers and allocate buffer(s) for the subsequent ones
    01877             txl_free_done_mpdu(txdesc, access_category, user_idx);
    01878             #endif
    01879
    01880             // Put a first status to the descriptor here, indicating it has been
    01881             // already handled by the HW
    01882             txdesc->lmac.hw_desc->cfm.status = txstatus;
    01883
    01884             // For profiling
    01885             PROF_AGG_SMPDU_DONETX_CLR();
    01886         }
    01887         // THD not yet transmitted, stop here and come back later
    01888         else
    01889         {
    01890             #if !NX_FULLY_HOSTED && NX_AMSDU_TX
    01891             // Maybe some A-MSDU sub-frames (i.e. TBD) are done, check that
    01892             txl_check_done_amsdu_subframe(txdesc, access_category, user_idx);
    01893             #endif
    01894             break;
    01895         }
    01896
    01897         // Pop picked txdesc definitively from list
    01898         co_list_pop_front(&(txlist->transmitting[user_idx]));
    01899
    01900         // Save the status and put the descriptor in the temporary confirmation queue
    01901         txdesc->lmac.hw_desc->cfm.status = txstatus;
    01902         co_list_push_back(&agg_desc->cfm, (struct co_list_hdr *)txdesc);
    01903     }
    01904 }
    01905
    01906 void txl_agg_mumimo_close(uint8_t ac)
    01907 {
    01908     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    01909     struct txl_mumimo_build_info_tag *mumimo = &txlist->mumimo;
    01910     int i;
    01911     uint32_t dur_max = 0;
    01912     uint8_t prim_user_pos = 0;
    01913     uint8_t nb_users = 0;
    01914     bool mumimook = true;
    01915
    01916     PROF_MU_PPDU_CLOSE_SET();
    01917
    01918     // Go through the A-MPDU which are ready for this MU-MIMO PPDU
    01919     for (i = 0; i < RW_USER_MAX; i++)
    01920     {
    01921         if (mumimo->users & CO_BIT(i))
    01922         {
    01923             struct txl_agg_build_tag *agg = &txlist->agg[i];
    01924             struct txdesc *txdesc = (struct txdesc *)co_list_pick(&mumimo->tx[i]);
    01925             uint32_t rate_info = mumimo->rateinfo[i];
    01926             uint32_t duration;
    01927
    01928             // Check if we have to close the A-MPDU under formation
    01929             if (agg->desc != NULL)
    01930             {
    01931                 txl_mumimo_ampdu_finish(ac, i);
    01932             }
    01933
    01934             // Now we need to ensure that the primary user is an A-MPDU
    01935             if (!is_mpdu_agg(txdesc))
    01936                 mumimook = false;
    01937
    01938             // Compute the duration of the A-MPDU
    01939             duration = txl_mumimo_duration_ns(mumimo->length[i],
    01940                                               (rate_info & VHT_MCS_MASK) >> VHT_MCS_OFT,
    01941                                               (rate_info & SHORT_GI_TX_RCX_MASK) != 0,
    01942                                               (rate_info & BW_TX_RCX_MASK) >> BW_TX_RCX_OFT,
    01943                                               ((rate_info & VHT_NSS_MASK) >> VHT_NSS_OFT) + 1);
    01944
    01945             // Check if the duration of the present A-MPDU is the greatest one
    01946             if (duration > dur_max)
    01947             {
    01948                 // The present A-MPDU duration is the greatest one, so save its duration
    01949                 // as well as the user position
    01950                 dur_max = duration;
    01951                 prim_user_pos = i;
    01952             }
    01953
    01954             // One more user in this MU-MIMO PPDU
    01955             nb_users++;
    01956         }
    01957     }
    01958
    01959     // Check if the MU-MIMO PPDU has several users
    01960     if ((nb_users > 1) && (mumimook))
    01961     {
    01962         struct txdesc *txdesc = (struct txdesc *)co_list_pick(&mumimo->tx[prim_user_pos]);
    01963
    01964         // Now we need to ensure that the primary user is an A-MPDU
    01965         if (is_mpdu_agg(txdesc))
    01966         {
    01967             struct tx_agg_desc *prim_agg_desc = txdesc->lmac.agg_desc;
    01968             struct tx_hd *a_thd = &prim_agg_desc->a_thd;
    01969             struct tx_hd *bar_thd = &prim_agg_desc->bar_thd;
    01970             struct txdesc *last = (struct txdesc *)co_list_pick_last(&mumimo->tx[prim_user_pos]);
    01971             struct tx_hd *thd = &last->lmac.hw_desc->thd;
    01972             int sec_idx = 1;
    01973             uint32_t *sec_user_ptr = &a_thd->sec_user1_ptr;
    01974             struct tx_agg_desc *agg_desc = prim_agg_desc;
    01975
    01976             PROF_MU_USER_POS_SET(prim_user_pos);
    01977
    01978             // Chain the BAR descriptor to the last MPDU
    01979             thd->nextmpdudesc_ptr = CPU2HW(&prim_agg_desc->bar_thd);
    01980
    01981             // This descriptor will be an intermediate one
    01982             prim_agg_desc->status |= AGG_INT;
    01983             prim_agg_desc->download = 0;
    01984             prim_agg_desc->prim_agg_desc = prim_agg_desc;
    01985
    01986             // Initialize the THD fields
    01987             a_thd->phyctrlinfo = (mumimo->group_id << GID_TX_OFT) |
    01988                                  (prim_user_pos << USER_POS_OFT) |
    01989                                  USE_MUMIMO_TX_BIT;
    01990
    01991             // Put primary aggregate in used list
    01992             co_list_push_back(&txlist->aggregates, &prim_agg_desc->list_hdr);
    01993
    01994             // We will now have some data programmed for transmission, so we can activate the A-MPDU
    01995             // optimization
    01996             txlist->ppdu_cnt++;
    01997
    01998             // Perform the chaining to the primary A-MPDU and BAR THDs
    01999             for (i = 0; i < RW_USER_MAX; i++)
    02000             {
    02001                 if (mumimo->users & CO_BIT(i))
    02002                 {
    02003                     int user_q_idx;
    02004                     txdesc = (struct txdesc *)co_list_pick(&mumimo->tx[i]);
    02005
    02006                     PROF_MU_USER_POS_SET(i);
    02007
    02008                     if (i != prim_user_pos)
    02009                     {
    02010                         agg_desc = txdesc->lmac.agg_desc;
    02011
    02012                         // Chain the data THD to the primary A-MPDU THD
    02013                         if (is_mpdu_agg(txdesc))
    02014                         {
    02015                             thd = &agg_desc->a_thd;
    02016
    02017                             // If the secondary PSDU is an A-MPDU chain the A-THD
    02018                             *sec_user_ptr++ = CPU2HW(&agg_desc->a_thd);
    02019                         }
    02020                         else
    02021                         {
    02022                             thd = &txdesc->lmac.hw_desc->thd;
    02023
    02024                             // Otherwise chain the Singleton THD
    02025                             *sec_user_ptr++ = CPU2HW(&txdesc->lmac.hw_desc->thd);
    02026                         }
    02027
    02028                         // Chain the BAR THD to the previous BAR THD
    02029                         bar_thd->nextfrmexseq_ptr = CPU2HW(&agg_desc->bar_thd);
    02030                         bar_thd = &agg_desc->bar_thd;
    02031
    02032                         // Initialize the THD fields
    02033                         thd->phyctrlinfo = (i << USER_POS_OFT) | USE_MUMIMO_TX_BIT;
    02034
    02035                         // The user queue index will map to the secondary index
    02036                         user_q_idx = sec_idx++;
    02037
    02038                         // Point the secondary AGG desc to the primary one
    02039                         agg_desc->prim_agg_desc = prim_agg_desc;
    02040                         agg_desc->status |= AGG_INT;
    02041
    02042                         // Put secondary aggregate in used list
    02043                         co_list_push_back(&txlist->aggregates, &agg_desc->list_hdr);
    02044                     }
    02045                     else
    02046                     {
    02047                         // The primary A-MPDU has the queue index 0
    02048                         user_q_idx = 0;
    02049                     }
    02050
    02051                     // Indicate that some payload download are expected on this user index
    02052                     prim_agg_desc->download |= CO_BIT(user_q_idx);
    02053
    02054                     // Check if we can allocate TX buffers for the MPDUs of this user
    02055                     while (txlist->first_to_download[user_q_idx] == NULL)
    02056                     {
    02057                         if (!txl_payload_alloc(txdesc, ac, user_q_idx))
    02058                             break;
    02059
    02060                         txdesc = tx_desc_next(txdesc);
    02061                         if (txdesc == NULL)
    02062                             break;
    02063                     }
    02064                     // Concatenate the MU-MIMO TX descriptor list with the transmitting list
    02065                     co_list_concat(&txlist->transmitting[user_q_idx], &mumimo->tx[i]);
    02066                 }
    02067             }
    02068             // Reset the intermediate status of the last AGG descriptor
    02069             agg_desc->status &= ~AGG_INT;
    02070             // Save the last user BAR THD
    02071             prim_agg_desc->last_bar_thd = bar_thd;
    02072         }
    02073         else
    02074         {
    02075             // The primary user is not an A-MPDU, so chain all the packets to the default
    02076             // user queue (i.e 0)
    02077             for (i = 0; i < RW_USER_MAX; i++)
    02078             {
    02079                 if (mumimo->users & CO_BIT(i))
    02080                 {
    02081                     // Convert the MU-MIMO A-MPDU or MPDU into a SU one
    02082                     txl_mumimo_convert(ac, i);
    02083                 }
    02084             }
    02085         }
    02086     }
    02087     else
    02088     {
    02089         // The primary user is not an A-MPDU, so chain all the packets to the default
    02090         // user queue (i.e 0)
    02091         for (i = 0; i < RW_USER_MAX; i++)
    02092         {
    02093             if (mumimo->users & CO_BIT(i))
    02094             {
    02095                 // Convert the MU-MIMO A-MPDU or MPDU into a SU one
    02096                 txl_mumimo_convert(ac, i);
    02097             }
    02098         }
    02099     }
    02100
    02101     // Clear the MU-MIMO preparation structure
    02102     mumimo->nb_users = 0;
    02103     mumimo->users = 0;
    02104     mumimo->first_user_pos = (mumimo->first_user_pos + 1) % RW_USER_MAX;
    02105     mumimo->open = MU_USER_MASK;
    02106
    02107     // If IPC queues were paused and packets saved, handle them now
    02108     for (i = 0; i < RW_USER_MAX; i++)
    02109     {
    02110         int pos = (mumimo->first_user_pos + i) % RW_USER_MAX;
    02111         struct txdesc *txdesc = mumimo->txdesc[pos];
    02112
    02113         if (txdesc != NULL)
    02114         {
    02115             int status;
    02116
    02117             mumimo->txdesc[pos] = NULL;
    02118
    02119             status = txl_agg_push_mpdu(txdesc, ac);
    02120
    02121             if (status == SU_PACKET)
    02122             {
    02123                 // Check if we can try to allocate a Tx buffer
    02124                 if (txlist->first_to_download[0] == NULL)
    02125                 {
    02126                     txl_payload_alloc(txdesc, ac, 0);
    02127                 }
    02128                 co_list_push_back(&txlist->transmitting[0], &txdesc->list_hdr);
    02129             }
    02130         }
    02131     }
    02132
    02133     // Reenable APP queues
    02134     macif_tx_enable_users(ac, mumimo->open);
    02135
    02136     PROF_MU_PPDU_CLOSE_CLR();
    02137 }
    02138 #endif // RW_MUMIMO_TX_EN
    02139
    02151 uint16_t txl_agg_mpdu_nb_delims(struct tx_hd *thd, uint16_t mmss_bytes)
    02152 {
    02153     uint16_t nb_delims = 0;
    02154     uint16_t subfrm_len = txl_mpdu_subframe_len(thd);
    02155
    02156     //calculate nb delimiters
    02157     if (subfrm_len < mmss_bytes)
    02158     {
    02159         //keep only byte length which must be covered by blank delimiters
    02160         mmss_bytes -= subfrm_len;
    02161
    02162         //not a multiple of 4 bytes(blank delim length) -> add 1 after division to ensure space is reached
    02163         nb_delims = (mmss_bytes + DELIMITER_LEN - 1) / DELIMITER_LEN;
    02164     }
    02165     return nb_delims;
    02166 }
    02167
    02168 void txl_agg_init(void)
    02169 {
    02170     // Initialize the HW descriptors linked to aggregation
    02171     txl_agg_hwdesc_init();
    02172 }
    02173
    02174 void txl_agg_reset(void)
    02175 {
    02176     // Reset the HW descriptors linked to aggregation
    02177     txl_agg_hwdesc_reset();
    02178 }
    02179
    02180 void txl_agg_finish(uint8_t ac)
    02181 {
    02182     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    02183     struct txl_agg_build_tag *agg = &txlist->agg[0];
    02184     struct tx_agg_desc *agg_desc = agg->desc;
    02185     struct txdesc *txdesc = agg->txdesc_last;
    02186
    02187     // For profiling
    02188     PROF_AGG_FINISH_AMPDU_SET();
    02189
    02190     // Check if the A-MPDU has only one MPDU
    02191     if (agg->curr_cnt == 1)
    02192     {
    02193         struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    02194         struct txl_buffer_tag *buffer = txl_buffer_get(txdesc);
    02195
    02196         // In such case, transform it to a singleton MPDU
    02197         txdesc->umac.flags = WHICHDESC_UNFRAGMENTED_MSDU;
    02198
    02199         // Clear the next_mpdu field (that was pointing to the BAR THD)
    02200         thd->nextmpdudesc_ptr = 0;
    02201         // Update the MAC Control Information 2 field to the correct value
    02202         thd->macctrlinfo2 = WHICHDESC_UNFRAGMENTED_MSDU | INTERRUPT_EN_TX;
    02203
    02204         // If buffer is already allocated for this descriptor, update the MAC_CONTROL_INFO2 field
    02205         if (buffer != NULL)
    02206         {
    02207             struct txl_buffer_control *bufctrl = &buffer->buffer_control;
    02208             struct tx_policy_tbl *pol = &bufctrl->policy_tbl;
    02209
    02210             // Check if the MPDU is already downloaded
    02211             if (agg_desc->status & AGG_FIRST_DOWNLOADED)
    02212             {
    02213                 // Set phy control info
    02214                 thd->phyctrlinfo = bufctrl->phy_control_info;
    02215                 // Set policy table address
    02216                 thd->policyentryaddr = CPU2HW(pol);
    02217                 // Set MAC control info 1 field
    02218                 thd->macctrlinfo1 = bufctrl->mac_control_info;
    02219
    02220                 #if RW_BFMER_EN
    02221                 if (bfr_is_enabled() && !bfr_is_bfmed_sglt_allowed(txdesc))
    02222                 {
    02223                     // The policy table attached to this packet may not be all compliant
    02224                     // with beamforming (because of the additional rate steps), so we
    02225                     // disable the beamforming for this frame
    02226                     pol->phycntrlinfo2 &= ~(BMFED_BIT | SMM_INDEX_PT_MASK);
    02227
    02228                     /*
    02229                      * As frame is not beamformed, the SMM Index field indicates the
    02230                      * spatial mapping in the modem:
    02231                      *      0 = Stream 0 on antenna 0 and stream 1 on antenna 1
    02232                      *      2 = Stream mixed on antenna 0 and antenna 1
    02233                      */
    02234                     pol->phycntrlinfo2 |= 2 << SMM_INDEX_PT_OFT;
    02235                 }
    02236                 #endif
    02237                 #if NX_MAC_HE
    02238                 // Remove the +HTC field if required
    02239                 if (!is_htc_sglt_allowed(txdesc))
    02240                     txl_buffer_remove_htc(thd);
    02241                 #endif
    02242
    02243                 #if NX_AMSDU_TX
    02244                 // Check if this packet is split across multiple buffers
    02245                 if (txl_buffer_is_amsdu_multi_buf(txdesc))
    02246                 {
    02247                     // Split packet - Disable HW retransmissions
    02248                     pol->maccntrlinfo2 &= ~(LONG_RETRY_LIMIT_MASK | SHORT_RETRY_LIMIT_MASK);
    02249                 }
    02250                 #endif
    02251
    02252                 #if TRACE_COMPO(LMAC)
    02253                 {
    02254                     struct mac_hdr *mac_hdr = (struct mac_hdr *)HW2CPU(txl_buffer_machdr_get(txdesc));
    02255                     TRACE_LMAC(TX, "{VIF-%d} to STA-%d %fc SN=%d (A-MDPU to singleton)",
    02256                                txdesc->host.vif_idx, txdesc->host.staid, mac_hdr->fctl,
    02257                                (mac_hdr->seq >> MAC_SEQCTRL_NUM_OFT));
    02258                 }
    02259                 #endif // TRACE_COMPO(LMAC)
    02260
    02261                 #if NX_MAC_HE
    02262                 // Check if the singleton shall be protected with a RTS/CTS
    02263                 txl_he_txop_dur_rtscts_thres_mpdu_check(txdesc);
    02264                 #endif
    02265
    02266                 // Chain the THD to the MAC HW
    02267                 txl_frame_exchange_chain(thd, thd, ac);
    02268             }
    02269         }
    02270         // Put back the aggregate in the free list
    02271         txl_agg_desc_free(agg_desc);
    02272
    02273         // Unchain the aggregate descriptor from the TX descriptor
    02274         txdesc->lmac.agg_desc = NULL;
    02275     }
    02276     else
    02277     {
    02278         struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    02279         struct txl_buffer_tag *buffer = txl_buffer_get(txdesc);
    02280         #if NX_UMAC_PRESENT
    02281         struct tx_cfm_tag *cfm = txl_cfm_tag_get(txdesc);
    02282         #endif
    02283
    02284         // Re-mark last MPDU as LAST not MIDDLE
    02285         set_mpdu_pos(txdesc, WHICHDESC_AMPDU_LAST);
    02286
    02287         #if NX_MAC_HE
    02288         agg_desc->txdesc_last = txdesc;
    02289         txl_he_txop_dur_rtscts_thres_ampdu_check(agg->txdesc_first);
    02290         #endif
    02291
    02292         #if NX_UMAC_PRESENT
    02293         cfm->ampdu_size = agg->curr_cnt;
    02294         #endif
    02295
    02296         //BAR DESCRIPTOR
    02297         txl_fill_bar(agg_desc, txdesc->umac.sn_win, agg_desc->sta_idx, agg_desc->tid, agg->bw);
    02298
    02299         // Chain the BAR descriptor to the last MPDU
    02300         thd->nextmpdudesc_ptr = CPU2HW(&agg_desc->bar_thd);
    02301
    02302         // And update the MAC Control Information 2 field to the correct value
    02303         thd->macctrlinfo2 = txdesc->umac.flags | INTERRUPT_EN_TX;
    02304
    02305         // Current aggregate is now completed
    02306         agg_desc->status |= AGG_FORMATTED;
    02307
    02308         // If buffer is already allocated for this descriptor, check if it is
    02309         // already downloaded or not
    02310         if (buffer != NULL)
    02311         {
    02312             // Check if enough data is already allocated
    02313             if (!(agg_desc->status & AGG_ALLOC))
    02314             {
    02315                 // If this is not the case, check if the first MPDU is already downloaded
    02316                 if (!(agg_desc->status & AGG_FIRST_DOWNLOADED))
    02317                 {
    02318                     // The first MPDU is not downloaded, so we will wait for its DMA
    02319                     // interrupt and do the A-MPDU chaining at that moment
    02320                     struct txl_buffer_tag *buffer_first = txl_buffer_get(agg->txdesc_first);
    02321                     buffer_first->flags |= BUF_ALLOC_OK;
    02322                 }
    02323                 else
    02324                     // The first MPDU is already downloaded, so we consider that we can
    02325                     // chain the A-MPDU immediately
    02326                     agg_desc->status |= AGG_DOWNLOADED;
    02327             }
    02328         }
    02329
    02330         // Check if aggregate descriptor is ready to be programmed
    02331         if (agg_desc->status & AGG_DOWNLOADED)
    02332         {
    02333             struct tx_hd *next_prev_hd = &agg_desc->bar_thd;
    02334             #if NX_MAC_HE
    02335             // Re-set the PHY flags in the policy table as they might have been changed
    02336             // during the TXOP duration RTS/CTS threshold check
    02337             struct tx_policy_tbl *pt = HW2CPU(agg_desc->a_thd.policyentryaddr);
    02338             pt->ratecntrlinfo[0] = agg->txdesc_first->umac.phy_flags;
    02339             #endif
    02340
    02341             #if TRACE_COMPO(LMAC)
    02342             struct tx_hd *first_thb = (struct tx_hd *)HW2CPU(agg_desc->a_thd.nextmpdudesc_ptr);
    02343             struct tx_pbd *first_pbd = (struct tx_pbd *)HW2CPU(first_thb->first_pbd_ptr);
    02344             struct mac_hdr *mac_hdr = (struct mac_hdr *)HW2CPU(first_pbd->datastartptr);
    02345             TRACE_LMAC(TX, "[AC %d] {VIF-%d} to STA-%d AMPDU (#%d, %lu bytes) SN=%d",
    02346                        ac, txdesc->host.vif_idx, txdesc->host.staid, agg->curr_cnt,
    02347                        TR_32(agg_desc->a_thd.frmlen), (mac_hdr->seq >> MAC_SEQCTRL_NUM_OFT));
    02348             #endif // TRACE_COMPO(LMAC)
    02349
    02350             // Chain the THD to the MAC HW
    02351             txl_frame_exchange_chain(&agg_desc->a_thd, next_prev_hd, ac);
    02352         }
    02353
    02354         // Put aggregate in used list
    02355         co_list_push_back(&txl_cntrl_env.txlist[ac].aggregates, &agg_desc->list_hdr);
    02356     }
    02357
    02358     // We will now have some data programmed for transmission, so we can activate the A-MPDU
    02359     // optimization
    02360     txl_cntrl_env.txlist[ac].ppdu_cnt++;
    02361
    02362     // Reinitialize the build state machine
    02363     agg->desc = NULL;
    02364
    02365     // For profiling
    02366     PROF_AGG_FINISH_AMPDU_CLR();
    02367 }
    02368
    02369 int txl_agg_push_mpdu(struct txdesc * txdesc, uint8_t ac)
    02370 {
    02371     struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    02372     uint8_t user_pos = 0;
    02373     int status = SU_PACKET;
    02374
    02375     //make local copy of staid and tid of MPDU given by UMAC
    02376     uint8_t  tid   = txdesc->host.tid;
    02377     uint8_t  staid = txdesc->host.staid;
    02378
    02379     do
    02380     {
    02381         struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    02382         struct txl_agg_build_tag *agg;
    02383
    02384         #if RW_MUMIMO_TX_EN
    02385         status = txl_mumimo_check(txdesc, ac, &user_pos);
    02386
    02387         // Check if the operation on this queue shall be paused
    02388         if (status == MU_PAUSED)
    02389             break;
    02390
    02391         // Check if the MU checking has to be restarted
    02392         if (status == MU_RESTART_CHECK)
    02393             continue;
    02394         #endif
    02395
    02396         // Retrieve the A-MPDU construction structure for this user position
    02397         agg = &txlist->agg[user_pos];
    02398
    02399         // Check if we are currently building an aggregate
    02400         if (agg->desc == NULL)
    02401         {
    02402             // No build ongoing, try to start a new one
    02403             // Make sure the MPDU can be part of an aggregate
    02404             if (!is_mpdu_agg(txdesc)
    02405                 #if NX_UMAC_PRESENT
    02406                 || !txl_ampdu_has_ht_vht_rate(txdesc)
    02407                 #endif
    02408                 )
    02409             {
    02410                 // We will now have some data programmed for transmission, so we can
    02411                 // activate the A-MPDU optimization
    02412                 txl_cntrl_env.txlist[ac].ppdu_cnt++;
    02413                 break;
    02414             }
    02415
    02416             // Get a free aggregate descriptor
    02417             agg->desc = txl_agg_desc_alloc(ac);
    02418
    02419             // No more free aggregate descriptors, handle the MPDU as a normal one
    02420             if (agg->desc == NULL)
    02421             {
    02422                 #if RW_MUMIMO_TX_EN
    02423                 // Check if a MU-MIMO PPDU is under construction. If yes we need
    02424                 // to close it
    02425                 if (txlist->mumimo.users)
    02426                 {
    02427                     txl_agg_mumimo_close(ac);
    02428                 }
    02429                 // This packet cannot be part of a MU-MIMO PPDU as no AGG descriptor
    02430                 // is available
    02431                 status = SU_PACKET;
    02432                 #endif
    02433                 //mark it as non aggregatable for the rest of the procedure
    02434                 txdesc->umac.flags = WHICHDESC_UNFRAGMENTED_MSDU;
    02435                 // We will now have some data programmed for transmission, so we can
    02436                 // activate the A-MPDU optimization
    02437                 txl_cntrl_env.txlist[ac].ppdu_cnt++;
    02438                 break;
    02439             }
    02440
    02441             // For profiling
    02442             PROF_AGG_START_AMPDU_SET();
    02443
    02444             // Initialize aggregate descriptor
    02445             agg->desc->status = 0;
    02446             agg->desc->available_len = 0;
    02447             agg->desc->available_cnt = 0;
    02448             agg->desc->sta_idx = staid;
    02449             agg->desc->tid = tid;
    02450             agg->desc->user_cnt = 1;
    02451             #if RW_MUMIMO_TX_EN
    02452             agg->desc->download = 0;
    02453             agg->desc->prim_agg_desc = NULL;
    02454             agg->desc->last_bar_thd = NULL;
    02455             co_list_init(&agg->desc->cfm);
    02456             #endif
    02457
    02458             // Get the constraints of the A-MPDU based on the PHY rate that will be used
    02459             // and the receiving STA
    02460             txl_ampdu_constraints_get(txdesc, ac, agg);
    02461
    02462             // For profiling
    02463             PROF_AGG_START_AMPDU_CLR();
    02464
    02465             // For profiling
    02466             PROF_AGG_ADD_MPDU_SET();
    02467
    02468             // Initialize the current A-MPDU length
    02469             agg->desc->a_thd.frmlen = txl_mpdu_subframe_len(thd);
    02470             //reset A-THD status
    02471             agg->desc->a_thd.statinfo = 0;
    02472             agg->desc->a_thd.nextmpdudesc_ptr = CPU2HW(thd);
    02473             agg->desc->a_thd.first_pbd_ptr = 0;
    02474             agg->desc->a_thd.optlen[0] = 0;
    02475             agg->desc->a_thd.optlen[1] = 0;
    02476             agg->desc->a_thd.optlen[2] = 0;
    02477
    02478             // Initialize the current A-MPDU count
    02479             agg->curr_cnt = 1;
    02480
    02481             // Initialize the number of delimiters to be put in the next HW descriptor
    02482             agg->nb_delims = txl_agg_mpdu_nb_delims(thd, agg->mmss_bytes);
    02483             #if NX_MAC_HE
    02484             //set number of delimiters
    02485             txdesc->umac.flags |= (agg->nb_delims << NB_BLANK_DELIM_OFT);
    02486             agg->desc->a_thd.frmlen += agg->nb_delims * DELIMITER_LEN;
    02487             #endif
    02488
    02489             // Save the pointer to the TX descriptor
    02490             agg->txdesc_last = txdesc;
    02491             agg->txdesc_first = txdesc;
    02492
    02493             //mark the txdesc as first of AMPDU
    02494             set_mpdu_pos(txdesc, WHICHDESC_AMPDU_FIRST);
    02495
    02496             //link txdesc to agg_cntrl
    02497             txdesc->lmac.agg_desc = agg->desc;
    02498             txdesc->umac.flags |= UNDER_BA_SETUP_BIT;
    02499
    02500             //set WhichDesc field in the THD
    02501             thd->macctrlinfo2 = txdesc->umac.flags;
    02502
    02503             #if RW_MUMIMO_TX_EN
    02504             if (status == MU_PACKET)
    02505             {
    02506                 PROF_MU_MPDU_ADD_SET();
    02507                 co_list_push_back(&txlist->mumimo.tx[user_pos], &txdesc->list_hdr);
    02508                 // Remember that this user position has an A-MPDU under creation on it
    02509                 txlist->mumimo.users |= CO_BIT(user_pos);
    02510                 txlist->mumimo.rateinfo[user_pos] = txdesc->umac.phy_flags;
    02511                 agg->desc->status |= AGG_MU;
    02512                 PROF_MU_MPDU_ADD_CLR();
    02513             }
    02514             #endif
    02515
    02516             // For profiling
    02517             PROF_AGG_ADD_MPDU_CLR();
    02518             break;
    02519         }
    02520         else
    02521         {
    02522             struct tx_agg_desc *agg_desc = agg->desc;
    02523             struct tx_hd *a_thd = &agg_desc->a_thd;
    02524             struct tx_hd *prev_thd = &agg->txdesc_last->lmac.hw_desc->thd;
    02525             uint32_t ampdu_subfrmlen;
    02526             #if NX_BW_LEN_ADAPT
    02527             uint8_t bw_idx = agg->bw_idx;
    02528             #else
    02529             uint8_t bw_idx = 0;
    02530             #endif
    02531
    02532             // txdesc must be compatible with current AMPDU
    02533             if (!txl_desc_is_agg(txdesc, agg_desc))
    02534             {
    02535                 #if RW_MUMIMO_TX_EN
    02536                 // Close this user position
    02537                 if (agg_desc->status & AGG_MU)
    02538                 {
    02539                     MU_AMPDU_CLOSE();
    02540                 }
    02541                 else
    02542                 {
    02543                 #endif
    02544                     // Close the A-MPDU under construction
    02545                     txl_agg_finish(ac);
    02546
    02547                     // And try to start a new one
    02548                     continue;
    02549                 #if RW_MUMIMO_TX_EN
    02550                 }
    02551                 #endif
    02552             }
    02553
    02554             #if NX_MAC_HE
    02555             agg->nb_delims = txl_agg_mpdu_nb_delims(thd, agg->mmss_bytes);
    02556             #endif
    02557
    02558             // Compute the MPDU subframe length (including delimiters)
    02559             ampdu_subfrmlen = txl_mpdu_subframe_len(thd) + agg->nb_delims * DELIMITER_LEN;
    02560
    02561             //check for AMPDU max len
    02562             if ((ampdu_subfrmlen + a_thd->frmlen) > agg->max_len[bw_idx])
    02563             {
    02564                 #if NX_BW_LEN_ADAPT
    02565                 if (bw_idx < agg->bw)
    02566                 {
    02567                     // Check if we have at least 2 MPDUs inside the A-MPDU to ensure that
    02568                     // in case of BW drop at lowest BW the transmitted packet will still
    02569                     // be an A-MPDU. We also ensure that we can go to the next BW index,
    02570                     // i.e that the new MPDU will still fit in the A-MPDU if transmitted
    02571                     // at the next BW
    02572                     if ((agg->curr_cnt == 1)
    02573                      || ((ampdu_subfrmlen + a_thd->frmlen) > agg->max_len[bw_idx+1]))
    02574                     {
    02575                         #if RW_MUMIMO_TX_EN
    02576                         // Close this user position
    02577                         if (agg_desc->status & AGG_MU)
    02578                         {
    02579                             MU_AMPDU_CLOSE();
    02580                         }
    02581                         else
    02582                         {
    02583                         #endif
    02584                             // Close the A-MPDU under construction
    02585                             txl_agg_finish(ac);
    02586                             // And try to start a new one
    02587                             continue;
    02588                         #if RW_MUMIMO_TX_EN
    02589                         }
    02590                         #endif
    02591                     }
    02592
    02593                     // For profiling
    02594                     PROF_BW_DROP_STEP_SET();
    02595
    02596                     // Save the last TX descriptor of this BW step
    02597                     agg_desc->txdesc[bw_idx] = agg->txdesc_last;
    02598
    02599                     // Update the THD with the optional length of this BW step
    02600                     a_thd->optlen[bw_idx] = a_thd->frmlen;
    02601
    02602                     // Increase the current BW index
    02603                     agg->bw_idx++;
    02604
    02605                     // For profiling
    02606                     PROF_BW_DROP_STEP_CLR();
    02607                 }
    02608                 else
    02609                 #endif
    02610                 {
    02611                     // Maximum length reached, finish current A-MPDU
    02612                     #if RW_MUMIMO_TX_EN
    02613                     // Close this user position
    02614                     if (agg_desc->status & AGG_MU)
    02615                     {
    02616                         MU_AMPDU_CLOSE();
    02617                     }
    02618                     else
    02619                     {
    02620                     #endif
    02621                         // Close the A-MPDU under construction
    02622                         txl_agg_finish(ac);
    02623                         // And try to start a new one
    02624                         continue;
    02625                     #if RW_MUMIMO_TX_EN
    02626                     }
    02627                     #endif
    02628                 }
    02629             }
    02630
    02631             // For profiling
    02632             PROF_AGG_ADD_MPDU_SET();
    02633
    02634             #if RW_MUMIMO_TX_EN
    02635             // Check if the packet will be effectively part of a MU-PPDU or not
    02636             if (!(agg_desc->status & AGG_MU))
    02637             {
    02638                 status = SU_PACKET;
    02639             }
    02640             #endif
    02641
    02642             //link txdesc to agg_cntrl
    02643             txdesc->lmac.agg_desc = agg_desc;
    02644
    02645             //set number of delims for previous txdesc into this one
    02646             txdesc->umac.flags |= (agg->nb_delims << NB_BLANK_DELIM_OFT) | UNDER_BA_SETUP_BIT;
    02647
    02648             //mark the txdesc as part of AMPDU
    02649             set_mpdu_pos(txdesc, WHICHDESC_AMPDU_INT);
    02650
    02651             //set WhichDesc and delimiter fields in the THD
    02652             thd->macctrlinfo2 = txdesc->umac.flags;
    02653
    02654             //chain the new THD to the previous one
    02655             prev_thd->nextmpdudesc_ptr = CPU2HW(thd);
    02656
    02657             //increase ampdu_desc length value
    02658             a_thd->frmlen += ampdu_subfrmlen;
    02659
    02660             #if !NX_MAC_HE
    02661             // Compute the number of delimiters to be put in the next HW descriptor
    02662             agg->nb_delims = txl_agg_mpdu_nb_delims(thd, agg->mmss_bytes);
    02663             #endif
    02664
    02665             // Save the pointer to the last handled TX descriptor
    02666             agg->txdesc_last = txdesc;
    02667
    02668             #if RW_MUMIMO_TX_EN
    02669             if (status == MU_PACKET)
    02670             {
    02671                 PROF_MU_MPDU_ADD_SET();
    02672                 co_list_push_back(&txlist->mumimo.tx[user_pos], &txdesc->list_hdr);
    02673                 PROF_MU_MPDU_ADD_CLR();
    02674             }
    02675             #endif
    02676
    02677             // For profiling
    02678             PROF_AGG_ADD_MPDU_CLR();
    02679
    02680             //increase nb MPDUs in AMPDU counter
    02681             agg->curr_cnt++;
    02682
    02683             // Check if we have reached the maximum number of packets
    02684             if (agg->curr_cnt >= agg->max_cnt)
    02685             {
    02686                 // We have reached the maximum MPDU count, finish current A-MPDU
    02687                 #if RW_MUMIMO_TX_EN
    02688                 if (status == MU_PACKET)
    02689                 {
    02690                     txl_mumimo_ampdu_finish(ac, user_pos);
    02691                     // Check if other user positions are still open
    02692                     if (txlist->mumimo.open & txlist->mumimo.users)
    02693                     {
    02694                         // We could still receive packets on other user positions,
    02695                         // so pause this user queue
    02696                         status = MU_PAUSED;
    02697                     }
    02698                     else
    02699                     {
    02700                         // Close the MU-MIMO PPDU under construction
    02701                         txl_agg_mumimo_close(ac);
    02702                     }
    02703                 }
    02704                 else
    02705                 #endif
    02706                     txl_agg_finish(ac);
    02707             }
    02708
    02709             // Exit the loop
    02710             break;
    02711         }
    02712     } while (1);
    02713
    02714     return (status);
    02715 }
    02716
    02717
    02718 void txl_agg_release(struct tx_agg_desc *agg_desc)
    02719 {
    02720     // Decrease the user count
    02721     agg_desc->user_cnt--;
    02722
    02723     // Free the descriptor
    02724     if (agg_desc->user_cnt == 0)
    02725     {
    02726         txl_agg_desc_free(agg_desc);
    02727     }
    02728 }
    02729 struct tx_hd *txl_agg_change_to_singleton(struct txdesc *txdesc, bool he_tb)
    02730 {
    02731     struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    02732     struct txl_buffer_tag *buffer = txl_buffer_get(txdesc);
    02733
    02734     // In such case, transform it to a singleton MPDU
    02735     txdesc->umac.flags = WHICHDESC_UNFRAGMENTED_MSDU;
    02736
    02737     // Unchain the aggregate descriptor from the TX descriptor
    02738     txdesc->lmac.agg_desc = NULL;
    02739
    02740     // Clear the next_mpdu field
    02741     thd->nextmpdudesc_ptr = 0;
    02742
    02743     // Update the MAC Control Information 2 field to the correct value
    02744     thd->macctrlinfo2 = WHICHDESC_UNFRAGMENTED_MSDU | INTERRUPT_EN_TX;
    02745
    02746     if (buffer)
    02747     {
    02748         struct txl_buffer_control *bufctrl = &buffer->buffer_control;
    02749
    02750         #if NX_UMAC_PRESENT
    02751         // Copy the buffer control
    02752         txl_buffer_control_copy(txdesc, buffer);
    02753         #if NX_MAC_HE
    02754         // Remove the +HTC field if required
    02755         if (!he_tb && !is_htc_sglt_allowed(txdesc))
    02756             txl_buffer_remove_htc(thd);
    02757         #endif
    02758         #endif
    02759
    02760         #if NX_MAC_HE
    02761         // Mark the buffer as ready to be sent as HE TB singleton if required
    02762         buffer->flags |= BUF_SINGLETON_READY;
    02763         // Check if the singleton shall be protected with a RTS/CTS
    02764         txl_he_txop_dur_rtscts_thres_mpdu_check(txdesc);
    02765         #endif
    02766
    02767         // Set MAC control info 1 field
    02768         thd->macctrlinfo1 = bufctrl->mac_control_info;
    02769         // Set phy control info
    02770         thd->phyctrlinfo = bufctrl->phy_control_info;
    02771         // Set policy table address
    02772         thd->policyentryaddr = CPU2HW(&bufctrl->policy_tbl);
    02773         #if NX_AMSDU_TX
    02774         // Check if this packet is split across several buffers
    02775         if (txl_buffer_is_amsdu_multi_buf(txdesc))
    02776         {
    02777             struct tx_policy_tbl *pol = &bufctrl->policy_tbl;
    02778
    02779             // Split packet - Disable HW retransmissions
    02780             pol->maccntrlinfo2 &= ~(LONG_RETRY_LIMIT_MASK | SHORT_RETRY_LIMIT_MASK);
    02781         }
    02782         #endif
    02783
    02784         return (thd);
    02785     }
    02786
    02787     return NULL;
    02788 }
    02789
    02790 #if (NX_BW_LEN_ADAPT)
    02791 void txl_agg_bw_drop_handle(uint8_t access_category)
    02792 {
    02793     struct txl_list *txlist = &txl_cntrl_env.txlist[access_category];
    02794     struct txdesc * txdesc = (struct txdesc *)co_list_pick(&(txlist->transmitting[0]));
    02795     struct tx_agg_desc *agg_desc;
    02796     struct tx_hd *thd, *prev_thd, *next_thd;
    02797     uint8_t bw;
    02798
    02799     // Sanity check - we shall have a descriptor in the list
    02800     ASSERT_ERR(txdesc != NULL);
    02801
    02802     // Check if the HW triggered the mechanism for a singleton - maybe not correct
    02803     while (!is_mpdu_agg(txdesc))
    02804     {
    02805         struct tx_hd *txhd = &txdesc->lmac.hw_desc->thd;
    02806         uint32_t txstatus = txhd->statinfo;
    02807         if (!(txstatus & DESC_DONE_TX_BIT))
    02808             return;
    02809         txdesc = tx_desc_next(txdesc);
    02810
    02811         if (txdesc == NULL)
    02812             return;
    02813     }
    02814
    02815     // Sanity check - the mechanism works only for A-MPDUs
    02816     ASSERT_ERR(is_mpdu_first(txdesc));
    02817
    02818     // Get the A-MPDU descriptor pointer
    02819     agg_desc = txdesc->lmac.agg_desc;
    02820
    02821     // Get the actual BW of transmission and the last TX desc that will be transmitted
    02822     bw = nxmac_tx_bw_after_drop_getf();
    02823     txdesc = agg_desc->txdesc[bw];
    02824     thd = &txdesc->lmac.hw_desc->thd;
    02825
    02826     ASSERT_ERR(!is_mpdu_last(txdesc));
    02827
    02828     // Split the A-MPDU
    02829     // Re-mark last MPDU as LAST not MIDDLE
    02830     set_mpdu_pos(txdesc, WHICHDESC_AMPDU_LAST);
    02831
    02832     // Chain the BAR descriptor to the last MPDU
    02833     thd->nextmpdudesc_ptr = CPU2HW(&agg_desc->bar_thd);
    02834
    02835     // And update the MAC Control Information 2 field to the correct value
    02836     thd->macctrlinfo2 = txdesc->umac.flags | INTERRUPT_EN_TX;
    02837
    02838     prev_thd = &agg_desc->bar_thd;
    02839     next_thd = HW2CPU(agg_desc->bar_thd.nextfrmexseq_ptr);
    02840
    02841     // Now reform an A-MPDU with the following descriptors
    02842     txdesc = tx_desc_next(txdesc);
    02843     ASSERT_ERR(txdesc != NULL);
    02844     while (1)
    02845     {
    02846         bool last = false;
    02847
    02848         if (is_mpdu_last(txdesc))
    02849             last = true;
    02850
    02851         // Increase the number of PPDU we have pushed into the data path
    02852         txlist->ppdu_cnt++;
    02853
    02854         // Change the A-MPDU member into a singleton frame
    02855         thd = txl_agg_change_to_singleton(txdesc, false);
    02856
    02857         if (thd)
    02858         {
    02859             prev_thd->nextfrmexseq_ptr = CPU2HW(thd);
    02860
    02861             if (txl_cntrl_env.txlist[access_category].last_frame_exch == prev_thd)
    02862                 txl_cntrl_env.txlist[access_category].last_frame_exch = thd;
    02863
    02864             prev_thd = thd;
    02865         }
    02866
    02867         if (last)
    02868         {
    02869             thd->nextfrmexseq_ptr = CPU2HW(next_thd);
    02870             break;
    02871         }
    02872
    02873         txdesc = tx_desc_next(txdesc);
    02874         ASSERT_ERR(txdesc != NULL);
    02875     }
    02876 }
    02877 #endif
    02878
    02879 void txl_agg_check(uint8_t access_category)
    02880 {
    02881     struct txl_list *txlist = &txl_cntrl_env.txlist[access_category];
    02882     GLOBAL_INT_DISABLE();
    02883     if (txlist->ppdu_cnt == 0)
    02884     {
    02885         #if RW_MUMIMO_TX_EN
    02886         // Check if a MU-MIMO PPDU is under preparation
    02887         if (txlist->mumimo.users)
    02888         {
    02889             txl_agg_mumimo_close(access_category);
    02890         }
    02891         else
    02892         #endif
    02893         if (txlist->agg[0].desc != NULL)
    02894         {
    02895             // Close the pending aggregate
    02896             txl_agg_finish(access_category);
    02897         }
    02898     }
    02899     GLOBAL_INT_RESTORE();
    02900 }
    02901
    02902
    02903 #if RW_MUMIMO_TX_EN
    02904 void txl_agg_sec_transmit_trigger(void)
    02905 {
    02906     // For profiling
    02907     PROF_MU_SEC_USER_IRQ_SET();
    02908
    02909     while (nxmac_sec_users_tx_int_event_get() & TX_SEC_IRQ_BITS)
    02910     {
    02911         uint8_t access_category;
    02912         uint8_t user_idx;
    02913         uint8_t highest;
    02914         uint32_t status = nxmac_sec_users_tx_int_event_get() & TX_SEC_IRQ_BITS;
    02915
    02916         // sanity check: we cannot be handling a TX interrupt if there isn't one pending
    02917         ASSERT_REC(status != 0);
    02918
    02919         // don't care about tx header or buffer trigger, only access category and user index,
    02920         // so merge trigger and buffer status
    02921         status |= status >> (NXMAC_SEC_U_1AC_0_TX_BUF_TRIGGER_POS - NXMAC_SEC_U_1AC_0_TX_TRIGGER_POS);
    02922
    02923         // Now proceed only the merged bits
    02924         status &= TX_SEC_IRQ_BITS_MERGED;
    02925
    02926         // Get the highest bit set
    02927         highest =  31 - co_clz(status);
    02928
    02929         // Compute the corresponding access category and secondary user index
    02930         access_category = highest % 4;
    02931         user_idx = highest / 8;
    02932
    02933         // sanity checks
    02934         ASSERT_ERR(access_category < NX_TXQ_CNT);
    02935         ASSERT_ERR(user_idx < RW_USER_MAX);
    02936
    02937         // clear the handled interrupts (buffer and TX)
    02938         nxmac_sec_users_tx_int_event_clear(CO_BIT(user_idx*8 + access_category + NXMAC_SEC_U_1AC_0_TX_BUF_TRIGGER_POS) |
    02939                                            CO_BIT(user_idx*8 + access_category + NXMAC_SEC_U_1AC_0_TX_TRIGGER_POS));
    02940
    02941         // For profiling
    02942         PROF_TX_AC_IRQ_SET(access_category);
    02943
    02944         // Go through the list of already transmitted packets to release them
    02945         txl_mumimo_secondary_done(access_category, user_idx + 1);
    02946     }
    02947
    02948     // For profiling
    02949     PROF_MU_SEC_USER_IRQ_CLR();
    02950 }
    02951 #endif
    02952
    02953 void txl_agg_set_ampdu_protection(uint32_t *rc)
    02954 {
    02955     uint8_t bw, rate, mcs;
    02956     uint32_t rc_loc = *rc;
    02957
    02958     // Check if the PPDU is already protected (e.g ERP protection) - In such case we rely
    02959     // on this protection
    02960     if ((rc_loc & PROT_FRM_EX_RCX_MASK) != PROT_NO_PROT)
    02961         return;
    02962
    02963     // Retrieve BW and MCS of the PPDU in order to compute the protection parameters
    02964     bw = (rc_loc & BW_TX_RCX_MASK) >> BW_TX_RCX_OFT;
    02965     mcs = ((rc_loc & FORMAT_MOD_TX_RCX_MASK) >> FORMAT_MOD_TX_RCX_OFT) >= FORMATMOD_VHT ?
    02966                                              rc_loc & VHT_MCS_MASK : rc_loc & HT_MCS_MASK;
    02967     rate = mcs > 2 ? HW_RATE_24MBPS : HW_RATE_6MBPS;
    02968     rc_loc &= ~(PROT_FRM_EX_RCX_MASK | MCS_INDEX_PROT_TX_RCX_MASK | BW_PROT_TX_RCX_MASK |
    02969                                                            FORMAT_MOD_PROT_TX_RCX_MASK);
    02970     rc_loc |= (bw << BW_PROT_TX_RCX_OFT) | (rate << MCS_INDEX_PROT_TX_RCX_OFT) | PROT_SELF_CTS;
    02971     rc_loc |= FORMATMOD_NON_HT_DUP_OFDM << FORMAT_MOD_PROT_TX_RCX_OFT;
    02972
    02973     *rc = rc_loc;
    02974 }
    02975
    02976 void txl_agg_check_rtscts_retry_limit(struct tx_hd *a_thd, uint8_t access_category)
    02977 {
    02978     uint32_t a_thd_status = a_thd->statinfo;
    02979
    02980     // Check if A-MPDU descriptor is done
    02981     if (a_thd_status & DESC_DONE_TX_BIT)
    02982     {
    02983         struct tx_policy_tbl *pol = HW2CPU(a_thd->policyentryaddr);
    02984
    02985         // Sanity check - the only valid case coming here corresponds
    02986         // to an RTS/CTS frame exchanged that failed
    02987         ASSERT_REC(a_thd_status & RETRY_LIMIT_REACHED_BIT);
    02988
    02989         // Reset the status of the A-MPDU descriptor
    02990         a_thd->statinfo = 0;
    02991
    02992         // Change the protection to CTS-to-self to ensure that the A-MPDU
    02993         // will be transmitted on next attempt
    02994         pol->ratecntrlinfo[0] &= ~PROT_FRM_EX_RCX_MASK;
    02995         pol->ratecntrlinfo[0] |= PROT_SELF_CTS;
    02996
    02997         // Reprogram the A-MPDU in TX (by setting the newHead bit)
    02998         txl_cntrl_newhead(CPU2HW(a_thd), access_category);
    02999     }
    03000 }
    03001
    03002 void txl_agg_check_saved_agg_desc(uint8_t access_category)
    03003 {
    03004     struct txl_list *txlist = &txl_cntrl_env.txlist[access_category];
    03005     struct tx_agg_desc *agg_desc_prev = txlist->agg_desc_prev;
    03006
    03007     // Check if we have to free the previous A-MPDU descriptor
    03008     if (agg_desc_prev != NULL)
    03009     {
    03010         // Decrease the user count
    03011         agg_desc_prev->user_cnt--;
    03012
    03013         // Free the descriptor
    03014         if (agg_desc_prev->user_cnt == 0)
    03015         {
    03016             txl_agg_desc_free(agg_desc_prev);
    03017         }
    03018
    03019         // No more previous A-MPDU descriptor to be considered for now
    03020         txlist->agg_desc_prev = NULL;
    03021     }
    03022 }
    03023
    03024 #if NX_MAC_HE
    03025 struct tx_hd *txl_agg_set_new_ampdu_head(struct txdesc *txdesc, struct tx_hd **bar_thd)
    03026 {
    03027     struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    03028     struct tx_agg_desc *agg_desc = txdesc->lmac.agg_desc;
    03029     struct txl_buffer_tag *buffer = txl_buffer_get(txdesc);
    03030
    03031     thd = &agg_desc->a_thd;
    03032
    03033     // A-MPDU was split
    03034     thd->nextmpdudesc_ptr = CPU2HW(&txdesc->lmac.hw_desc->thd);
    03035     thd->policyentryaddr = CPU2HW(&agg_desc->pol_tbl);
    03036     set_mpdu_pos(txdesc, WHICHDESC_AMPDU_FIRST);
    03037     txdesc->lmac.hw_desc->thd.macctrlinfo2 &= ~WHICHDESC_MSK;
    03038     txdesc->lmac.hw_desc->thd.macctrlinfo2 |= WHICHDESC_AMPDU_FIRST;
    03039
    03040     if (buffer == NULL)
    03041     {
    03042         agg_desc->status = AGG_FORMATTED;
    03043         agg_desc->available_len = 0;
    03044         return NULL;
    03045     }
    03046
    03047     *bar_thd = &agg_desc->bar_thd;
    03048     return thd;
    03049 }
    03050
    03051 struct tx_hd *txl_agg_he_tb_prep(struct txdesc *txdesc, struct txdesc **txdesc_next,
    03052                                  uint32_t max_len, uint16_t min_mpdu_len, uint8_t ac)
    03053 {
    03054     struct tx_agg_desc *agg_desc;
    03055     struct tx_hd *thd = &txdesc->lmac.hw_desc->thd;
    03056     struct txl_list *txlist = &txl_cntrl_env.txlist[ac];
    03057
    03058     // First check if the MPDU to transmit is already part of a A-MPDU
    03059     if (is_mpdu_agg(txdesc))
    03060     {
    03061         // A-MPDU case
    03062         // Ensure the A-MPDU is ready for TX
    03063         agg_desc = txdesc->lmac.agg_desc;
    03064         if (!is_mpdu_first(txdesc) ||
    03065             ((agg_desc->status & (AGG_FORMATTED | AGG_DOWNLOADED)) !=
    03066                          (AGG_FORMATTED | AGG_DOWNLOADED)))
    03067             return NULL;
    03068
    03069         // Check if it can fit entirely in the HE TB PPDU or not
    03070         if (agg_desc->a_thd.frmlen > max_len)
    03071             // Cannot fit - split it
    03072             return txl_agg_split(txdesc, txdesc_next, max_len, min_mpdu_len, ac);
    03073
    03074         // Set the UPH on all the MPDUs that have already a buffer
    03075         txl_agg_set_uph(txdesc);
    03076
    03077         *txdesc_next = tx_desc_next(agg_desc->txdesc_last);
    03078     }
    03079     else
    03080     {
    03081         // MPDU case
    03082         // Ensure the MPDU is ready for TX
    03083         struct txl_buffer_tag *buffer = txl_buffer_get(txdesc);
    03084         if (!buffer || !(buffer->flags & BUF_SINGLETON_READY))
    03085             return NULL;
    03086
    03087         // If not already done set the +HTC length and the order bit in the MPDU buffer
    03088         if (!txl_agg_is_int_or_mgt(txdesc))
    03089             txl_buffer_add_htc(thd);
    03090
    03091         // Check if it can fit entirely in the HE TB PPDU or not
    03092         if (thd->frmlen > max_len)
    03093         {
    03094             // Request to disable the MU EDCA because the HE TB queue is blocked
    03095             txl_he_mu_edca_blocked(thd, ac);
    03096             return NULL;
    03097         }
    03098
    03099         // Update the HTC field with the UPH
    03100         if (!txl_agg_is_int_or_mgt(txdesc))
    03101             txl_buffer_update_htc(thd, txl_he_tb_uph_get());
    03102
    03103         *txdesc_next = tx_desc_next(txdesc);
    03104     }
    03105
    03106     // We will be able to push at least one MPDU in the HE TB, let's try to concatenate
    03107     // it with next MPDUs if any
    03108     while (*txdesc_next)
    03109     {
    03110         struct txdesc *txdesc_n = *txdesc_next;
    03111
    03112         // Don't aggregate frames sent internally, and check if we still have time to
    03113         // concatenate new MPDUs
    03114         if (txl_agg_is_int_or_mgt(txdesc) || txl_agg_is_int_or_mgt(txdesc_n) ||
    03115                                      (nxmac_tx_hetb_rem_dur_getf() < TX_HE_TB_PROG_TIME))
    03116             break;
    03117
    03118         if (is_mpdu_agg(txdesc))
    03119         {
    03120             if (!is_mpdu_agg(txdesc_n))
    03121             {
    03122                 if (!txl_agg_he_tb_cat_ampdu_mpdu(txdesc, txdesc_next, max_len, min_mpdu_len, ac))
    03123                     break;
    03124             }
    03125             else if (!txl_agg_he_tb_cat_ampdu(txdesc, txdesc_next, max_len, min_mpdu_len, ac))
    03126                 break;
    03127         }
    03128         else if (!is_mpdu_agg(txdesc_n) ||
    03129                  !txl_agg_he_tb_cat_mpdu_ampdu(txdesc, txdesc_next, max_len, min_mpdu_len, ac))
    03130             break;
    03131     };
    03132
    03133     // Check if HE TB will be a A-MPDU
    03134     if (is_mpdu_agg(txdesc))
    03135     {
    03136         thd = &txdesc->lmac.agg_desc->a_thd;
    03137         // Remove BAR THD from last MPDU
    03138         txdesc->lmac.agg_desc->txdesc_last->lmac.hw_desc->thd.nextmpdudesc_ptr = 0;
    03139         return thd;
    03140     }
    03141
    03142     // The HE TB PPDU will contain only one MPDU
    03143     // Check if there is already a AGG descriptor allocated for it - In such case no need
    03144     // to allocate a new one.
    03145     if (txdesc->lmac.agg_desc)
    03146         return thd;
    03147
    03148     // Allocate an aggregate descriptor for the HE TB singleton
    03149     agg_desc = txl_agg_desc_alloc(TX_HE_TB_AMPDU_QUEUE_IDX);
    03150     ASSERT_ERR(agg_desc != NULL);
    03151
    03152     // Initialize it
    03153     agg_desc->status = 0;
    03154     agg_desc->user_cnt = 1;
    03155
    03156     // Assign it to the TX descriptor
    03157     txdesc->lmac.agg_desc = agg_desc;
    03158
    03159     // Push it at the front of the aggregate list
    03160     co_list_push_front(&txlist->aggregates, &agg_desc->list_hdr);
    03161
    03162     return thd;
    03163 }
    03164 #endif // NX_MAC_HE
    03165
    03166
    03167 #endif
    03168
    03169