/* *************************************************************************** * Ralink Tech Inc. * 4F, No. 2 Technology 5th Rd. * Science-based Industrial Park * Hsin-chu, Taiwan, R.O.C. * * (c) Copyright 2002-2009, Ralink Technology, Inc. * * All rights reserved. Ralink's source code is an unpublished work and the * use of a copyright notice does not imply otherwise. This source code * contains confidential trade secret material of Ralink Tech. Any attemp * or participation in deciphering, decoding, reverse engineering or in any * way altering the source code is stricitly prohibited, unless the prior * written consent of Ralink Technology, Inc. is obtained. *************************************************************************** Module Name: cmm_txbf.c Abstract: Tx Beamforming related functions Revision History: Who When What -------- ---------- ---------------------------------------------- Shiang 2009/11/04 */ #include "rt_config.h" #ifdef TXBF_SUPPORT #define ETXBF_PROBE_TIME (RA_INTERVAL-100) /* Wait for Sounding Response will time out 100msec before end of RA interval */ #ifdef MFB_SUPPORT UCHAR mcsToLowerMcs[] = { /* originalMfb, newMfb1s, newMfb2s, newMfb3s*/ 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 0, 8, 8, 9, 1, 9, 9, 10, 2, 10, 10, 11, 3, 11, 11, 12, 4, 12, 12, 13, 5, 13, 13, 14, 6, 14, 14, 15, 7, 15, 15, 16, 0, 8, 16, 17, 1, 9, 17, 18, 2, 10, 18, 19, 3, 11, 19, 20, 4, 12, 20, 21, 5, 13, 21, 22, 6, 14, 22, 23, 7, 15, 23, 24, 0, 8, 16, 25, 1, 9, 17, 26, 2, 10, 18, 27, 3, 11, 19, 28, 4, 12, 20, 29, 5, 13, 21, 30, 6, 14, 22, 31, 7, 15, 23, 32, 0, 0, 0, 33, 3, 3, 3, 34, 3, 3, 3, 35, 3, 11, 11, 36, 4, 4, 4, 37, 6, 6, 6, 38, 6, 12, 12, 39, 3, 3, 17, 40, 3, 11, 11, 41, 3, 3, 17, 42, 3, 11, 11, 43, 3, 11, 19, 44, 3, 11, 11, 45, 3, 11, 19, 46, 4, 4, 18, 47, 4, 12, 12, 48, 6, 6, 6, 49, 6, 12, 12, 50, 6, 12, 20, 51, 6, 14, 14, 52, 6, 14, 14, 53, 3, 3, 17, 54, 3, 11, 11, 55, 3, 11, 19, 56, 3, 3, 17, 57, 3, 11, 11, 58, 3, 11, 19, 59, 3, 11, 19, 60, 3, 11, 11, 61, 3, 11, 19, 62, 3, 11, 19, 63, 3, 11, 19, 64, 3, 11, 19, 65, 4, 4, 18, 66, 4, 12, 12, 67, 4, 12, 20, 68, 6, 6, 6, 69, 6, 12, 12, 70, 6, 12, 20, 71, 6, 12, 20, 72, 6, 14, 14, 73, 6, 14, 14, 74, 6, 14, 14, 75, 6, 14, 22, 76, 6, 14, 22 }; #endif /* MFB_SUPPORT */ #ifdef ETXBF_EN_COND3_SUPPORT UCHAR groupShift[] = {4, 4, 4}; UCHAR groupMethod[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1}; SHORT groupThrd[] = {-8, 4, 20, 32, 52, 68, 80, 88, -16, 8, 12, 64, 40, 60, 80, 88, -24, 12, 12, 96, 40, 60, 80, 88}; UINT dataRate[] = {65, 130, 195, 260, 390, 520, 585, 650, 130, 260, 390, 520, 780, 1040, 1170, 1300, 190, 390, 585, 780, 1170, 1560, 1755, 1950}; #endif /* ETXBF_EN_COND3_SUPPORT */ #if defined(RT2883) || defined(RT3883) || defined(RT3593) static inline VOID rtmp_asic_etxbf_write_change( IN RTMP_ADAPTER *pAd, IN BOOLEAN bWriteEnable) { UINT8 byteValue; RTMP_BBP_IO_READ8_BY_REG_ID(pAd, BBP_REG_BF, &byteValue); if (bWriteEnable) byteValue |= 0x80; else byteValue &= (~0x80); RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_REG_BF, byteValue); } #endif VOID rtmp_asic_set_bf( IN RTMP_ADAPTER *pAd) { UINT8 byteValue = 0; UINT Value32; #if defined(RT2883) || defined(RT3883) || defined(RT3593) RTMP_BBP_IO_READ8_BY_REG_ID(pAd, BBP_REG_BF, &byteValue); /* Leave bit3 unchanged. It must be enabled to report BF SNR. */ if (pAd->CommonCfg.RegTransmitSetting.field.ITxBfEn) byteValue |= 0x20; else byteValue &= (~0x20); if (pAd->CommonCfg.ETxBfEnCond) byteValue |= 0x90; else byteValue &= ~0x90; RTMP_BBP_IO_WRITE8_BY_REG_ID(pAd, BBP_REG_BF, byteValue); #endif #ifdef MT76x2 RTMP_IO_READ32(pAd, PFMU_R1, &Value32); Value32 &= ~0x330; if (pAd->CommonCfg.RegTransmitSetting.field.ITxBfEn) Value32 |= 0x120; else Value32 &= (~0x120); if (pAd->CommonCfg.ETxBfEnCond > 0) Value32 |= 0x210; else Value32 &= ~0x210; RTMP_IO_WRITE32(pAd, PFMU_R1, Value32); RTMP_IO_READ32(pAd, PFMU_R0, &Value32); Value32 &= ~((0x1 << 6) | 0x3); if (pAd->CommonCfg.RegTransmitSetting.field.ITxBfEn) Value32 |= ((0x1 << 6) | 0x1); else Value32 &= ~((0x1 << 6) | 0x1); if (pAd->CommonCfg.ETxBfEnCond > 0) { Value32 |= (0x1 << 6) | 0x2; RTMP_IO_WRITE32(pAd, TX_TXBF_CFG_2, 0xFFFFFFFF); } else Value32 &= ~((0x1 << 6) | 0x2); RTMP_IO_WRITE32(pAd, PFMU_R0, Value32); #endif } /* TxBFInit - Intialize TxBF fields in pEntry supportsETxBF - TRUE if client supports ETxBF */ VOID TxBFInit( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry, IN BOOLEAN supportsETxBF) { pEntry->bfState = READY_FOR_SNDG0; pEntry->sndgMcs = 0; pEntry->sndg0Snr0 = 0; pEntry->sndg0Snr1 = 0; pEntry->sndg0Snr2 = 0; pEntry->sndg0Mcs = 0; #ifdef ETXBF_EN_COND3_SUPPORT pEntry->sndgRateIdx = 0; pEntry->sndg0RateIdx = 0; pEntry->sndg1Mcs = 0; pEntry->sndg1RateIdx = 0; pEntry->sndg1Snr0 = 0; pEntry->sndg1Snr1 = 0; pEntry->sndg1Snr2 = 0; pEntry->bf0Mcs = 0; pEntry->bf0RateIdx = 0; pEntry->bf1Mcs = 0; pEntry->bf1RateIdx = 0; #endif /* EXTBF_EN_COND3_SUPPORT */ pEntry->noSndgCnt = 0; pEntry->eTxBfEnCond = supportsETxBF? pAd->CommonCfg.ETxBfEnCond: 0; pEntry->noSndgCntThrd = NO_SNDG_CNT_THRD; pEntry->ndpSndgStreams = pAd->Antenna.field.TxPath; /* If client supports ETxBf and ITxBF then give ETxBF priority over ITxBF */ pEntry->iTxBfEn = pEntry->eTxBfEnCond> 0 ? 0 : pAd->CommonCfg.RegTransmitSetting.field.ITxBfEn; } BOOLEAN rtmp_chk_itxbf_calibration( IN RTMP_ADAPTER *pAd) { INT calIdx, calCnt; USHORT offset, eeVal, *calptr; #ifndef MT76x2 USHORT g_caladdr[] = {0x1a0, 0x1a2, 0x1b0, 0x1b2, 0x1b6, 0x1b8}; USHORT a_caladdr[] = {0x1a4, 0x1a6, 0x1a8, 0x1aa, 0x1ac, 0x1ae, 0x1b4, 0x1ba, 0x1bc, 0x1be, 0x1c0, 0x1c2, 0x1c4, 0x1c6, 0x1c8}; #else USHORT g_caladdr[] = {0xc0, 0xc2, 0xd4, 0xd6, 0xd8}; USHORT a_caladdr[] = {0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, 0xd0, 0xd2, 0xda, 0xdc, 0xde, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec, 0xee, 0xf0}; #endif UINT32 ee_sum; BOOLEAN bCalibrated = TRUE; if (pAd->CommonCfg.Channel <= 14) { calCnt = sizeof(g_caladdr) / sizeof(USHORT); calptr = &g_caladdr[0] ; } else { calCnt = sizeof(a_caladdr) / sizeof(USHORT); calptr = &a_caladdr[0]; } ee_sum = 0; for (calIdx = 0; calIdx < calCnt; calIdx++) { offset = *(calptr + calIdx); RT28xx_EEPROM_READ16(pAd, offset, eeVal); ee_sum += eeVal; DBGPRINT(RT_DEBUG_INFO, ("Check EEPROM(offset=0x%x, eeVal=0x%x, ee_sum=0x%x)!\n", offset, eeVal, ee_sum)); if (eeVal!=0xffff && eeVal!=0) return TRUE; } if ((ee_sum == (0xffff * calCnt)) || (ee_sum == 0x0)) { bCalibrated = FALSE; DBGPRINT(RT_DEBUG_TRACE, ("EEPROM all 0xffff(cnt =%d, sum=0x%x), not valid calibration value!\n", calCnt, ee_sum)); } return bCalibrated; } VOID Trigger_Sounding_Packet( IN PRTMP_ADAPTER pAd, IN UCHAR SndgType, IN UCHAR SndgBW, IN UCHAR SndgMcs, IN MAC_TABLE_ENTRY *pEntry) { /* SngType 0: disable 1 : sounding 2: NDP sounding */ NdisAcquireSpinLock(&pEntry->TxSndgLock); pEntry->TxSndgType = SndgType; NdisReleaseSpinLock(&pEntry->TxSndgLock); RTMPSetTimer(&pEntry->eTxBfProbeTimer, ETXBF_PROBE_TIME); /*DBGPRINT(RT_DEBUG_TRACE, ("ETxBF in Trigger_Sounding_Packet(): sndgType=%d, bw=%d, mcs=%d\n", SndgType, SndgBW, SndgMcs)); */ } /* eTxBFProbing - called by Rate Adaptation routine each interval. Initiates a sounding packet if enabled. */ VOID eTxBFProbing( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry) { if (pEntry->eTxBfEnCond == 0) { pEntry->bfState = READY_FOR_SNDG0; } else if (pEntry->bfState==READY_FOR_SNDG0 && pEntry->noSndgCnt>=pEntry->noSndgCntThrd) { /* Select NDP sounding, maximum streams */ pEntry->sndgMcs = (pEntry->ndpSndgStreams==3) ? 16 : 8; Trigger_Sounding_Packet(pAd, SNDG_TYPE_NDP, 0, pEntry->sndgMcs, pEntry); pEntry->bfState = WAIT_SNDG_FB0; pEntry->noSndgCnt = 0; } else if (pEntry->bfState == READY_FOR_SNDG0) { pEntry->noSndgCnt++; } else pEntry->noSndgCnt = 0; } /* clientSupportsETxBF - returns true if client supports compatible Sounding */ BOOLEAN clientSupportsETxBF( IN PRTMP_ADAPTER pAd, IN HT_BF_CAP *pTxBFCap) { BOOLEAN compCompat, noncompCompat; compCompat = (pTxBFCap->ExpComBF > 0) && /*(pTxBFCap->ComSteerBFAntSup+1 >= pAd->Antenna.field.TxPath) && */ (pAd->CommonCfg.ETxBfNoncompress == 0); noncompCompat = (pTxBFCap->ExpNoComBF > 0) /* && (pTxBFCap->NoComSteerBFAntSup+1 >= pAd->Antenna.field.TxPath)*/; return pTxBFCap->RxNDPCapable==1 && (compCompat || noncompCompat); } #ifdef VHT_TXBF_SUPPORT /* clientSupportsETxBF - returns true if client supports compatible Sounding */ BOOLEAN clientSupportsVHTETxBF( IN PRTMP_ADAPTER pAd, IN VHT_CAP_INFO *pTxBFCap) { return pTxBFCap->bfee_cap_su; } #endif /* setETxBFCap - sets our ETxBF capabilities */ void setETxBFCap(RTMP_ADAPTER *pAd, HT_BF_CAP *pTxBFCap) { if (pAd->CommonCfg.ETxBfIncapable) { memset(pTxBFCap, 0, sizeof(*pTxBFCap)); } else { pTxBFCap->RxNDPCapable = TRUE; pTxBFCap->TxNDPCapable = TRUE; pTxBFCap->ExpNoComSteerCapable = TRUE; pTxBFCap->ExpComSteerCapable = !pAd->CommonCfg.ETxBfNoncompress; pTxBFCap->ExpNoComBF = HT_ExBF_FB_CAP_IMMEDIATE; pTxBFCap->ExpComBF = pAd->CommonCfg.ETxBfNoncompress? HT_ExBF_FB_CAP_NONE: HT_ExBF_FB_CAP_IMMEDIATE; pTxBFCap->MinGrouping = 3; #ifndef MT76x2 pTxBFCap->NoComSteerBFAntSup = 2; pTxBFCap->ComSteerBFAntSup = 2; #else pTxBFCap->NoComSteerBFAntSup = 1; // 2 Tx antenna sounding pTxBFCap->ComSteerBFAntSup = 1; // 2 Tx antenna sounding pTxBFCap->TxSoundCapable = TRUE; // Support staggered sounding frames #endif pTxBFCap->ChanEstimation = pAd->Antenna.field.RxPath-1; } } #ifdef VHT_TXBF_SUPPORT void setVHTETxBFCap(RTMP_ADAPTER *pAd, VHT_CAP_INFO *pTxBFCap) { if (pAd->CommonCfg.ETxBfIncapable) { pTxBFCap->num_snd_dimension = 0; pTxBFCap->bfee_cap_mu = 0; pTxBFCap->bfee_cap_su = 0; pTxBFCap->bfer_cap_mu = 0; pTxBFCap->bfer_cap_su = 0; pTxBFCap->cmp_st_num_bfer = 0; } else { pTxBFCap->bfee_cap_su = 1; pTxBFCap->bfer_cap_su = 1; pTxBFCap->num_snd_dimension = 1; pTxBFCap->cmp_st_num_bfer = 1; } } #endif #ifdef ETXBF_EN_COND3_SUPPORT /* 4. determine the best method among mfb0, mfb1, snrComb0, snrComb1 5. use the best method. if necessary, sndg with the mcs which resulting in the best snrComb. */ /*if mcs is not in group 1 */ VOID txSndgSameMcs( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry, IN UCHAR smoothMfb)/*smoothMfb should be the current mcs */ { PUCHAR pTable; UCHAR TableSize = 0; UCHAR InitTxRateIdx, i, step; BOOLEAN bWriteEnable; UCHAR SndgType = SNDG_TYPE_SOUNDING; if (pEntry->eTxBfEnCond == 0) { pEntry->bfState = READY_FOR_SNDG0; return; } /*0. write down the current mfb0 */ pEntry->mfb0 = smoothMfb; /* 1. sndg with current mcs, get snrComb0 */ if (smoothMfb >> 3 > 0 ) { pEntry->sndgMcs = smoothMfb; } else { pEntry->sndgMcs = 8; SndgType = SNDG_TYPE_NDP; } /* if ndp sndg is forced by iwpriv command */ if (pEntry->ndpSndgStreams == 2 ||pEntry->ndpSndgStreams == 3) { SndgType = SNDG_TYPE_NDP; if (pEntry->ndpSndgStreams == 3) pEntry->sndgMcs = 16; else pEntry->sndgMcs = 8; } /* smoothMfb is guaranteed included in the current pTable because it is converted from received MFB in handleHtcField() */ MlmeSelectTxRateTable(pAd, pEntry, &pTable, &TableSize, &InitTxRateIdx); #ifdef NEW_RATE_ADAPT_SUPPORT if (ADAPT_RATE_TABLE(pTable)) step = 10; else #endif /* NEW_RATE_ADAPT_SUPPORT */ step = 5; for (i=1; i<=TableSize; i++) { if (pTable[i*step+2] >= pEntry->sndgMcs) break; } if (i > TableSize) i = TableSize - 1; /* DBGPRINT(RT_DEBUG_TRACE, ("txSndgSameMcs i = %x, step = %x, sndgMcs = %x CurrentMCS = %x \n", i, step, pEntry->sndgMcs, pTable[i*step+2])); */ pEntry->sndgRateIdx = pTable[i*step]; if (pEntry->sndgMcs != pTable[i*step+2]) { /*pEntry->sndgMcs = pTable[i*step+2];*/ if (pTable[i*step+2] > 16) pEntry->sndgMcs = 16; else if (pTable[i*step+2] > 8) pEntry->sndgMcs = 8; else pEntry->sndgMcs = 0; SndgType = SNDG_TYPE_NDP; } /* Enable/disable BF matrix writing */ if (pEntry->eTxBfEnCond == 1 || pEntry->eTxBfEnCond == 2) { bWriteEnable = TRUE; pEntry->HTPhyMode.field.eTxBF = 1; } else { bWriteEnable = FALSE; } rtmp_asic_etxbf_write_change(pAd, bWriteEnable); /* send a sounding packet*/ Trigger_Sounding_Packet(pAd, SndgType, 0, pEntry->sndgMcs, pEntry); pEntry->bfState = WAIT_SNDG_FB0; pEntry->noSndgCnt = 0; } /* txSndgOtherGroup - NOTE: currently unused. Only called when ETxBfEnCond==3 */ VOID txSndgOtherGroup( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry) { PUCHAR pTable; UCHAR TableSize = 0; UCHAR InitTxRateIdx, i, step; UCHAR byteValue = 0; UCHAR SndgType = SNDG_TYPE_SOUNDING; /* tx sndg with mcs in the other group */ if ((pEntry->sndgMcs)>>3 == 2) { pEntry->sndgMcs = 8; SndgType = SNDG_TYPE_NDP; } else { pEntry->sndgMcs = 16; SndgType = SNDG_TYPE_NDP; } /* copied from txSndgSameMcs() */ MlmeSelectTxRateTable(pAd, pEntry, &pTable, &TableSize, &InitTxRateIdx); #ifdef NEW_RATE_ADAPT_SUPPORT if (ADAPT_RATE_TABLE(pTable)) step = 10; else #endif /* NEW_RATE_ADAPT_SUPPORT */ step = 5; for (i=1; i<=TableSize; i++) { if (pTable[i*step+2] >= pEntry->sndgMcs) break; } if (i > TableSize) i = TableSize - 1; pEntry->sndgRateIdx = pTable[i*step]; if (pEntry->sndgMcs != pTable[i*step+2]) { pEntry->sndgMcs = pTable[i*step+2]; SndgType = SNDG_TYPE_NDP; } /*---copied from txSndgSameMcs() end */ /* disable BF matrix writing */ rtmp_asic_etxbf_write_change(pAd, FALSE); Trigger_Sounding_Packet(pAd, SndgType, 0, pEntry->sndgMcs, pEntry); DBGPRINT(RT_DEBUG_TRACE,("ETxBF in txSndgOtherGroup(): tx the second SNDG, enter state WAIT_SNDG_FB1\n" )); pEntry->bfState = WAIT_SNDG_FB1; } VOID txMrqInvTxBF( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry) { pEntry->toTxMrq = TRUE; pEntry->msiToTx = MSI_TOGGLE_BF; /* pEntry->HTPhyMode.field.TxBF = ~pEntry->HTPhyMode.field.TxBF;done in another function call*/ RTMPSetTimer(&pEntry->eTxBfProbeTimer, ETXBF_PROBE_TIME); DBGPRINT(RT_DEBUG_TRACE,("ETxBF in txMrqInvTxBF(): tx the second MRQ, enter state WAIT_MFB\n" )); pEntry->bfState = WAIT_MFB; } UINT convertSnrToThroughput( IN UCHAR streamsIn, IN INT snr0, IN INT snr1, IN INT snr2, IN PUCHAR pTable, OUT UCHAR *bestMcsPtr, OUT UCHAR *bestRateIdxPtr ) { UCHAR streams; INT snrTemp; UCHAR i, j; SHORT idx; SHORT group; INT snrTemp1[3]; INT snr[] = {snr0, snr1, snr2}; INT snrSum, tpTemp, bestTp=0; SHORT thrdTemp; BOOLEAN isMcsValid[24]; UCHAR rateIdx[24], step, tableSize; UCHAR mcs; #ifdef NEW_RATE_ADAPT_SUPPORT if (ADAPT_RATE_TABLE(pTable)) step = 10; else #endif /* NEW_RATE_ADAPT_SUPPORT */ step = 5; tableSize = RATE_TABLE_SIZE(pTable); for (i=0; i<24; i++) { isMcsValid[i] = FALSE; rateIdx[i] = 0; } for (i=1; i<=tableSize; i++) { isMcsValid[pTable[i*step+2]] = TRUE; rateIdx[pTable[i*step+2]] = pTable[i*step]; } if (streamsIn > 3) { DBGPRINT(RT_DEBUG_TRACE,("convertSnrToThroughput(): %d streams are not supported!!!", streamsIn)); streams = 3; } else streams = streamsIn; for (i=0; i=0; group--) { snrTemp1[0] = snr[0]; snrTemp1[1] = snr[1]; snrTemp1[2] = snr[2]; /*SNR processing for each group according to the baseband implementation, for example MRC*/ switch (group) { case 0: snrTemp1[1] = 0; snrTemp1[2] = 0; break; case 1: snrTemp1[2] = 0; break; case 2: break; default: break; } snrSum = snr[0] + snr[1] + snr[2]; for (idx=7; idx>=0; idx--){ mcs = group*8+idx; thrdTemp = groupThrd[mcs]; tpTemp = 0; if (groupMethod[mcs] == 0) { if (snrSum > thrdTemp) tpTemp = ((snrSum - thrdTemp) * dataRate[mcs])>>groupShift[group]; } else { if (group == 1) snrTemp1[2] = thrdTemp + 1; if (snrTemp1[0] > thrdTemp && snrTemp1[1] > thrdTemp && snrTemp1[2] > thrdTemp) tpTemp = ((snrTemp1[0] - thrdTemp)*(snrTemp1[1] - thrdTemp)*(snrTemp1[2] - thrdTemp) * dataRate[mcs])>>groupShift[group];/* have to be revised!!!*/ } if (tpTemp > dataRate[mcs]) tpTemp = dataRate[mcs]; if (tpTemp > bestTp && isMcsValid[mcs] == TRUE) { bestTp = tpTemp; (*bestMcsPtr) = mcs; (*bestRateIdxPtr) = rateIdx[mcs]; DBGPRINT(RT_DEBUG_TRACE,("convertSnrToThroughput(): new candidate snr0=%d, snr1=%d, snr2=%d, tp=%d, best MCS=%d\n", snrTemp1[0], snrTemp1[1], snrTemp1[2], tpTemp, *bestMcsPtr)); } } } DBGPRINT(RT_DEBUG_TRACE,("convertSnrToThroughput(): snr0=%d, snr1=%d, snr2=%d, tp=%d, best MCS=%d\n", snr0, snr1, snr2, bestTp, *bestMcsPtr)); return bestTp; } /* NOTE: currently unused. Only called when ETxBfEnCond==3 */ VOID chooseBestMethod( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry, IN UCHAR mfb) { /* UCHAR bestMethod;0:original, 1:inverted TxBF, 2:first sndg, 3:second sndg*/ UINT tp[4], bestTp; UCHAR streams, i; PUCHAR pTable; UCHAR TableSize = 0; UCHAR InitTxRateIdx; UCHAR byteValue = 0; pEntry->mfb1 = mfb; DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): received the second MFB %d, noted as mfb1\n", pEntry->mfb1 )); if ((pEntry->HTCapability.MCSSet[2] == 0xff) && (pAd->CommonCfg.TxStream == 3)) { streams = 3; } else if (pEntry->HTCapability.MCSSet[0] == 0xff && pEntry->HTCapability.MCSSet[1] == 0xff && pAd->CommonCfg.TxStream > 1 && (pAd->CommonCfg.TxStream == 2 || pEntry->HTCapability.MCSSet[2] == 0x0)) { streams = 2; } else { streams = 1; } MlmeSelectTxRateTable(pAd, pEntry, &pTable, &TableSize, &InitTxRateIdx); tp[2] = convertSnrToThroughput(streams, pEntry->sndg0Snr0, pEntry->sndg0Snr1, pEntry->sndg0Snr2, pTable, &(pEntry->bf0Mcs), &(pEntry->bf0RateIdx)); tp[3] = convertSnrToThroughput(streams, pEntry->sndg1Snr0, pEntry->sndg1Snr1, pEntry->sndg1Snr2, pTable, &(pEntry->bf1Mcs), &(pEntry->bf1RateIdx)); tp[0] = dataRate[pEntry->mfb0]; tp[1] = dataRate[pEntry->mfb1]; bestTp = 0; for (i=0; i<4; i++) { DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): predicted throughput of method %d = %d\n", i, tp[i] )); if (tp[i] > bestTp) { bestTp = tp[i]; pEntry->bestMethod = i; } } DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): method %d is chosen\n", pEntry->bestMethod )); switch (pEntry->bestMethod) { case 0:/*do nothing*/ pEntry->bfState = READY_FOR_SNDG0; DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): do nothing, and enter state READY_FOR_SNDG0\n" )); break; case 1: pEntry->HTPhyMode.field.eTxBF = ~pEntry->HTPhyMode.field.eTxBF; pEntry->bfState = READY_FOR_SNDG0; DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): invert the ETxBF status, and enter state READY_FOR_SNDG0\n" )); break; case 2: pEntry->sndgMcs = pEntry->sndg0Mcs; pEntry->sndgRateIdx = pEntry->sndg0RateIdx; /* enable BF matrix writing */ rtmp_asic_etxbf_write_change(pAd, TRUE); if (pEntry->sndgRateIdx == pEntry->CurrTxRateIndex) Trigger_Sounding_Packet(pAd, SNDG_TYPE_SOUNDING, 0, pEntry->sndgMcs, pEntry); else Trigger_Sounding_Packet(pAd, SNDG_TYPE_NDP, 0, pEntry->sndgMcs, pEntry); DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): tx the SNDG of the best method, enter state WAIT_BEST_SNDG\n" )); pEntry->bfState = WAIT_BEST_SNDG; break; case 3: /* tx sndg with mcs in the other group */ pEntry->sndgMcs = pEntry->sndg1Mcs; pEntry->sndgRateIdx = pEntry->sndg1RateIdx; /* enable BF matrix writing */ rtmp_asic_etxbf_write_change(pAd, TRUE); if (pEntry->sndgRateIdx == pEntry->CurrTxRateIndex) Trigger_Sounding_Packet(pAd, SNDG_TYPE_SOUNDING, 0, pEntry->sndgMcs, pEntry); else Trigger_Sounding_Packet(pAd, SNDG_TYPE_NDP, 0, pEntry->sndgMcs, pEntry); DBGPRINT(RT_DEBUG_TRACE,("ETxBF in chooseBestMethod(): tx the SNDG of the best method, enter state WAIT_BEST_SNDG\n" )); pEntry->bfState = WAIT_BEST_SNDG; break; } } /* NOTE: currently unused. Only called when ETxBfEnCond==3 */ VOID rxBestSndg( IN PRTMP_ADAPTER pAd, IN MAC_TABLE_ENTRY *pEntry) { /*set the best mcs of this BF matrix*/ if (pEntry->bestMethod == 2) { pEntry->CurrTxRate = pEntry->bf0Mcs; pEntry->CurrTxRateIndex = pEntry->bf0RateIdx; } else if (pEntry->bestMethod == 3) { pEntry->CurrTxRate = pEntry->bf1Mcs; pEntry->CurrTxRateIndex = pEntry->bf1RateIdx; } pEntry->HTPhyMode.field.eTxBF = 1; /*must sync the timing of using new BF matrix and its bfRateIdx!!!*/ /*need to reset counter for rateAdapt and may have to skip one adaptation when the new BF matrix is applied!!!*/ DBGPRINT(RT_DEBUG_TRACE,("ETxBF in rxBestSndg(): received the feedback of the best SNDG, and enter state READY_FOR_SNDG0\n" )); pEntry->bfState = READY_FOR_SNDG0; } #endif /* ETXBF_EN_COND3_SUPPORT */ VOID handleBfFb(RTMP_ADAPTER *pAd, RX_BLK *pRxBlk) { MAC_TABLE_ENTRY *pEntry = NULL; if (pRxBlk->wcid >= MAX_LEN_OF_MAC_TABLE) return; pEntry = &(pAd->MacTab.Content[pRxBlk->wcid]); /* DBGPRINT(RT_DEBUG_TRACE, ("ETxBF :(%02x:%02x:%02x:%02x:%02x:%02x)\n", PRINT_MAC(pEntry->Addr))); */ if (pEntry->bfState == WAIT_SNDG_FB0) { int Nc = ((pRxBlk ->pData)[2] & 0x3) + 1; /*record the snr comb*/ pEntry->sndg0Snr0 = 88+(CHAR)(pRxBlk ->pData[8]); pEntry->sndg0Snr1 = (Nc<2)? 0: 88+(CHAR)(pRxBlk ->pData[9]); pEntry->sndg0Snr2 = (Nc<3)? 0: 88+(CHAR)(pRxBlk ->pData[10]); pEntry->sndg0Mcs = pEntry->sndgMcs; DBGPRINT(RT_DEBUG_INFO,(" ETxBF: aid=%d snr %d.%02d %d.%02d %d.%02d\n", pRxBlk->wcid, pEntry->sndg0Snr0/4, 25*(pEntry->sndg0Snr0 & 0x3), pEntry->sndg0Snr1/4, 25*(pEntry->sndg0Snr1 & 0x3), pEntry->sndg0Snr2/4, 25*(pEntry->sndg0Snr2 & 0x3)) ); #ifdef ETXBF_EN_COND3_SUPPORT if (pEntry->eTxBfEnCond == 1 ||pEntry->eTxBfEnCond == 2) pEntry->bfState = READY_FOR_SNDG0; /* 2. sndg with current mcs+8 or -8, get snrComb1*/ else if (pEntry->eTxBfEnCond == 3) txSndgOtherGroup(pAd, pEntry); #else pEntry->bfState = READY_FOR_SNDG0; #endif } #ifdef ETXBF_EN_COND3_SUPPORT else if (pEntry->bfState == WAIT_SNDG_FB1) { /* 3. mrq with inverted TxBF status, get mfb1*/ if (TRUE) { int Nc = ((pRxBlk ->pData)[2] & 0x3) + 1; /* record the snr comb */ pEntry->sndg1Snr0 = 88+(CHAR)(pRxBlk ->pData[8]); pEntry->sndg1Snr1 = (Nc<2)? 0: 88+(CHAR)(pRxBlk ->pData[9]); pEntry->sndg1Snr2 = (Nc<3)? 0: 88+(CHAR)(pRxBlk ->pData[10]); pEntry->sndg1Mcs = pEntry->sndgMcs; DBGPRINT(RT_DEBUG_INFO,(" ETxBF: mcs%d, snr %d %d %d\n", pEntry->sndg1Mcs, pEntry->sndg1Snr0, pEntry->sndg1Snr1, pEntry->sndg1Snr2 )); txMrqInvTxBF(pAd, pEntry); } else chooseBestMethod(pAd, pEntry, 0); } else if (pEntry->bfState == WAIT_USELESS_RSP) { int Nc = ((pRxBlk ->pData)[2] & 0x3) + 1; pEntry->sndg0Snr0 = 88+(CHAR)(pRxBlk ->pData[8]); pEntry->sndg0Snr1 = (Nc<2)? 0: 88+(CHAR)(pRxBlk ->pData[9]); pEntry->sndg0Snr2 = (Nc<3)? 0: 88+(CHAR)(pRxBlk ->pData[10]); DBGPRINT(RT_DEBUG_INFO,(" ETxBF: mcs%d, snr %d %d %d\n", pEntry->sndg1Mcs, pEntry->sndg1Snr0, pEntry->sndg1Snr1, pEntry->sndg1Snr2 )); txSndgSameMcs(pAd, pEntry, /*pRxBlk,*/ pEntry->lastLegalMfb); } else if (pEntry->bfState == WAIT_BEST_SNDG) { rxBestSndg(pAd, pEntry); } #endif /* ETXBF_EN_COND3_SUPPORT */ } VOID handleHtcField(RTMP_ADAPTER *pAd, RX_BLK *pRxBlk) { #ifdef MFB_SUPPORT UCHAR mfb = ((PHT_CONTROL)(pRxBlk->pData))-> MFBorASC; UCHAR mfsi = ((PHT_CONTROL)(pRxBlk->pData))-> MFSI; UCHAR legalMfb = 0, legalMfbIdx=0, smoothMfb = 0; MAC_TABLE_ENTRY *pEntry = NULL; UCHAR i, j; UCHAR *pTable; UCHAR TableSize = 0; UCHAR InitTxRateIdx; UCHAR snr[] = {pRxBlk->rx_signal.raw_snr[0], pRxBlk->rx_signal.raw_snr[1], pRxBlk->rx_signal.raw_snr[2]}; UCHAR snrTemp1[3]; UCHAR snrTemp; UCHAR streams; UINT tpTemp, bestTp = 0, snrSum; SHORT thrdTemp; SHORT group, idx; UCHAR mcs; RTMP_RA_GRP_TB *pLegalMfbRS3S = NULL; RTMP_RA_LEGACY_TB *pLegalMfbRS = NULL; if (pRxBlk->wcid >= MAX_LEN_OF_MAC_TABLE) { return; } pEntry = &(pAd->MacTab.Content[pRxBlk->wcid]); /* if MFB is received, have to rule out the case when mai==14 */ if (!(((PHT_CONTROL)(pRxBlk->pData))->MRQ == 0 && ((PHT_CONTROL)(pRxBlk->pData))->MSI == 7) && mfb != 127) {/* need a timer in case there is no mfb with this mfsi */ DBGPRINT(RT_DEBUG_INFO, (" MFB in handleHtcField(): MFB %d is received\n", mfb)); /* check if the mfb is valid. if not, convert to a valid mcs */ if (mfb > 76) DBGPRINT(RT_DEBUG_TRACE, ("Error in handleHtcField: received MFB > 76\n")); if (pEntry->HTCapability.MCSSet[0] == 0xff && pEntry->HTCapability.MCSSet[1] == 0xff && pEntry->HTCapability.MCSSet[2] == 0xff && pAd->CommonCfg.TxStream == 3) legalMfb = mcsToLowerMcs[4*mfb + 3]; else if (pEntry->HTCapability.MCSSet[0] == 0xff && pEntry->HTCapability.MCSSet[1] == 0xff && pAd->CommonCfg.TxStream > 1 && (pAd->CommonCfg.TxStream == 2 || pEntry->HTCapability.MCSSet[2] == 0x0)) legalMfb = mcsToLowerMcs[4*mfb + 2]; else if (pEntry->HTCapability.MCSSet[0] == 0xff &&( pAd->CommonCfg.TxStream == 1 ||pEntry->HTCapability.MCSSet[1] == 0x0)) legalMfb = mcsToLowerMcs[4*mfb + 1]; else DBGPRINT(RT_DEBUG_TRACE, ("no available MFB mapping for the received MFB\n")); /* have to rule out the mfb that the Rx shouldn't be able to suggest??? for example, mrq was sent with 2 streams but the Rx suggests MCS with 3 streams */ #ifdef ETXBF_EN_COND3_SUPPORT if (mfsi == MSI_TOGGLE_BF) { if (pEntry->bfState == WAIT_MFB) chooseBestMethod(pAd, pEntry, legalMfb); else if (pEntry->bfState == WAIT_USELESS_RSP) txSndgSameMcs(pAd, pEntry,/* pRxBlk,*/ legalMfb); } #endif } if (!(((PHT_CONTROL)(pRxBlk->pData))->MRQ == 0 && ((PHT_CONTROL)(pRxBlk->pData))->MSI == 7 && mfsi != MSI_TOGGLE_BF) && mfb != 127) { /* the main body of algorithm */ /* legalMfb smoothing */ MlmeSelectTxRateTable(pAd, pEntry, &pTable, &TableSize, &InitTxRateIdx); #ifdef NEW_RATE_ADAPT_SUPPORT if (ADAPT_RATE_TABLE(pTable)) { for (i=1; i<=RATE_TABLE_SIZE(pTable); i++) { if (legalMfb == pTable[i*10+2]) { legalMfbIdx = pTable[i*10]; pLegalMfbRS3S = (RTMP_RA_GRP_TB *) &pTable[i*10]; pLegalMfbRS = (RTMP_RA_LEGACY_TB *) &pTable[i*10]; break; } } /* pLegalMfbRS3S may be null if pLegalMfbRS3S is not found!!! */ if (pEntry->lastLegalMfb == pTable[(pLegalMfbRS3S->downMcs+1)*10+2] ||pEntry->lastLegalMfb == pTable[(pLegalMfbRS3S->upMcs1+1)*10+2] ||pEntry->lastLegalMfb == pTable[(pLegalMfbRS3S->upMcs2+1)*10+2] ||pEntry->lastLegalMfb == pTable[(pLegalMfbRS3S->upMcs3+1)*10+2]) smoothMfb = pEntry->lastLegalMfb; else smoothMfb = legalMfb; } else #endif /* NEW_RATE_ADAPT_SUPPORT */ { for (i=1; i<=RATE_TABLE_SIZE(pTable); i++) { if (legalMfb == pTable[i*5+2]) { legalMfbIdx = pTable[i*5]; pLegalMfbRS = (RTMP_RA_LEGACY_TB *) &pTable[i*5]; break; } } if ((pEntry->lastLegalMfb <= legalMfb+1 || pEntry->lastLegalMfb+1 >= legalMfb) && ((legalMfb>>3) == (pEntry->lastLegalMfb >>3))) smoothMfb = pEntry->lastLegalMfb; else smoothMfb = legalMfb; } if (smoothMfb != pEntry->lastLegalMfb && smoothMfb != pTable[(pEntry->CurrTxRateIndex+1)*10+2]) {/* if mfb changes and mfb is different from current mcs: means channel change */ #ifdef NEW_RATE_ADAPT_SUPPORT if ((ADAPT_RATE_TABLE(pTable))) MlmeSetMcsGroup(pAd, pEntry); #endif /* NEW_RATE_ADAPT_SUPPORT */ pEntry->CurrTxRateIndex = legalMfbIdx; MlmeClearTxQuality(pEntry);/* clear all history, same as train up, purpose??? */ NdisAcquireSpinLock(&pEntry->fLastChangeAccordingMfbLock); NdisMoveMemory(pEntry->LegalMfbRS, pLegalMfbRS, sizeof(RTMP_RA_LEGACY_TB)); pEntry->fLastChangeAccordingMfb = TRUE; /* reset all OneSecTx counters */ /* RESET_ONE_SEC_TX_CNT(pEntry); */ NdisReleaseSpinLock(&pEntry->fLastChangeAccordingMfbLock); DBGPRINT(RT_DEBUG_INFO,(" MFB in handleHtcField(): MFB changes and use the new mfb=%d, mfbIdx=%d\n", legalMfb, legalMfbIdx)); /* pEntry->isMfbChanged = TRUE; */ if ((pEntry->HTCapability.TxBFCap.ExpNoComBF && pAd->CommonCfg.HtCapability.TxBFCap.TxSoundCapable && pAd->CommonCfg.HtCapability.TxBFCap.ExpNoComSteerCapable)) {/* support ETxBF. what's the correct criterion???? */ /* the process is triggered by channel change here. have to add the mechanism where the process is triggered by timer expiration!!! */ #ifdef ETXBF_EN_COND3_SUPPORT if (pEntry->eTxBfEnCond == 3) { if (pEntry->bfState == READY_FOR_SNDG0) { DBGPRINT(RT_DEBUG_OFF,("ETxBF in handleHtcField(): detect MFB change, set pEntry->mfb0=%d\n", pEntry->mfb0 )); txSndgSameMcs(pAd, pEntry, /*pRxBlk,*/ legalMfb); } else { DBGPRINT(RT_DEBUG_TRACE,("ETxBF in handleHtcField(): detect channel change before enter the ETxBF probe process is complete, enter state WAIT_USELESS_RSP\n" )); pEntry->bfState = WAIT_USELESS_RSP; } } #endif /* write down the current MFB and ixTxBF. currentMFB is recorded in lastLegalMfb */ /* send one packet with reverse TxBF, 2 stream sounding */ } } /* post-prossessing */ if (pEntry->fLastChangeAccordingMfb) pEntry->lastLegalMfb = legalMfb; } /*if mrq is received*/ if (((PHT_CONTROL)(pRxBlk->pData))->MRQ == 1) { DBGPRINT(RT_DEBUG_INFO, (" MFB in handleHtcField(): MRQ is received\n")); /* Assumption: snr are not sorted, wait for John or Julian's answer as to the SNR values of unused streams */ if ((pEntry->HTCapability.MCSSet[2] == 0xff && pAd->CommonCfg.TxStream == 3)) { streams = 3; } else if (pEntry->HTCapability.MCSSet[0] == 0xff && pEntry->HTCapability.MCSSet[1] == 0xff && pAd->CommonCfg.TxStream > 1 && (pAd->CommonCfg.TxStream == 2 || pEntry->HTCapability.MCSSet[2] == 0x0)) { streams = 2; } else { streams = 1; } /*sort such that snr[0]>=snr[1]>=snr[2]. sorting is required for group 2 so that the best 2 streams are used.*/ for (i=0; i=0; group--) { snrTemp1[0] = snr[0]; snrTemp1[1] = snr[1]; snrTemp1[2] = snr[2]; /*SNR processing for each group according to the baseband implementation, for example MRC*/ switch (group) { case 0: snrTemp1[1] = 0; snrTemp1[2] = 0; break; case 1: snrTemp1[2] = 0; break; case 2: break; default: break; } snrSum = snr[0] + snr[1] + snr[2]; for (idx=8; idx>=1; idx--){ mcs = group*8+idx-1; thrdTemp = groupThrd[mcs]; tpTemp = 0; if (groupMethod[mcs] == 0) { if (snrSum > thrdTemp) tpTemp = ((snrSum - thrdTemp) * dataRate[mcs])>>groupShift[group]; } else { if (group == 1) snrTemp1[2] = thrdTemp + 1; if (snrTemp1[0] > thrdTemp && snrTemp1[1] > thrdTemp && snrTemp1[2] > thrdTemp) tpTemp = ((snrTemp1[0] - thrdTemp)*(snrTemp1[1] - thrdTemp)*(snrTemp1[2] - thrdTemp) * dataRate[mcs])>>groupShift[group];/* have to be revised!!! */ } if (tpTemp > dataRate[mcs]) tpTemp = dataRate[mcs]; if (tpTemp > bestTp) { bestTp = tpTemp; pEntry->mfbToTx = mcs; } } } pEntry->toTxMfb = 1;/*should be reset to 0 when mfb is actually sent out!!!*/ DBGPRINT(RT_DEBUG_INFO,(" MFB in handleHtcField(): MFB %d is going to be sent\n", pEntry->mfbToTx)); } #endif /* MFB_SUPPORT */ } void eTxBfProbeTimerExec( IN PVOID SystemSpecific1, IN PVOID FunctionContext, IN PVOID SystemSpecific2, IN PVOID SystemSpecific3) { MAC_TABLE_ENTRY *pEntry = (PMAC_TABLE_ENTRY) FunctionContext; #ifdef ETXBF_EN_COND3_SUPPORT PRTMP_ADAPTER pAd = (PRTMP_ADAPTER)pEntry->pAd; #endif if (pEntry->bfState == WAIT_SNDG_FB0) { /*record the snr comb*/ pEntry->sndg0Snr0 = -128; pEntry->sndg0Snr1 = -128; pEntry->sndg0Snr2 = -128; pEntry->sndg0Mcs = pEntry->sndgMcs; DBGPRINT(RT_DEBUG_TRACE,(" ETxBF: timer of WAIT_SNDG_FB0 expires\n" )); #ifdef ETXBF_EN_COND3_SUPPORT if (pEntry->eTxBfEnCond == 1 || pEntry->eTxBfEnCond == 2) pEntry->bfState =READY_FOR_SNDG0; else if (pEntry->eTxBfEnCond == 3) txSndgOtherGroup(pAd, pEntry); #else pEntry->bfState =READY_FOR_SNDG0; #endif } #ifdef ETXBF_EN_COND3_SUPPORT else if (pEntry->bfState == WAIT_SNDG_FB1) { /*record the snr comb*/ pEntry->sndg1Snr0 = -128; pEntry->sndg1Snr1 = -128; pEntry->sndg1Snr2 = -128; pEntry->sndg1Mcs = pEntry->sndgMcs; DBGPRINT(RT_DEBUG_TRACE,(" ETxBF: timer of WAIT_SNDG_FB1 expires, run txMrqInvTxBF()\n" )); txMrqInvTxBF(pAd, pEntry); } else if (pEntry->bfState == WAIT_MFB) { DBGPRINT(RT_DEBUG_TRACE,(" ETxBF: timer of WAIT_MFB expires, run chooseBestMethod()\n" )); chooseBestMethod(pAd, pEntry, 0); } else if (pEntry->bfState == WAIT_BEST_SNDG) { DBGPRINT(RT_DEBUG_TRACE,(" ETxBF: timer of WAIT_BEST_SNDG expires, run rxBestSndg()\n" )); rxBestSndg(pAd, pEntry); } #endif /* ETXBF_EN_COND3_SUPPORT */ } #ifdef MFB_SUPPORT VOID MFB_PerPareMRQ( IN PRTMP_ADAPTER pAd, OUT VOID* pBuf, IN PMAC_TABLE_ENTRY pEntry) { PHT_CONTROL pHT_Control; /* DBGPRINT(RT_DEBUG_TRACE, ("-----> MFB_PerPareMRQ\n"));*/ if (pEntry->HTCapability.ExtHtCapInfo.MCSFeedback >= MCSFBK_MRQ) { pHT_Control = (HT_CONTROL *)pBuf; pHT_Control->MRQ = 1; if (pEntry->msiToTx == MSI_TOGGLE_BF) { if (pEntry->mrqCnt == 0) pEntry->mrqCnt = TOGGLE_BF_PKTS; else { (pEntry->mrqCnt)--; if (pEntry->mrqCnt == 0) pEntry->msiToTx = 0; } } pHT_Control->MSI = pEntry->msiToTx; /*update region*/ if (pEntry->msiToTx == MSI_TOGGLE_BF-1)/*MSI_TOGGLE_BF==6 is used to indicate the TxBF status is inverted for this packet*/ pEntry->msiToTx = 0; else if (pEntry->msiToTx != MSI_TOGGLE_BF) pEntry->msiToTx++; } /* DBGPRINT(RT_DEBUG_TRACE, ("<----- MFB_PerPareMRQ\n"));*/ } /* Need to be completed!!!!!!!!!!!!!!!!! */ VOID MFB_PerPareMFB( IN PRTMP_ADAPTER pAd, OUT VOID* pBuf, IN PMAC_TABLE_ENTRY pEntry) { /* DBGPRINT(RT_DEBUG_TRACE, ("-----> MFB_PerPareMRQ\n")); */ #if 0 if (pAd->CommonCfg.HtCapability.ExtHtCapInfo.MCSFeedback >= MCSFBK_MRQ) { PHT_CONTROL pHT_Control = (HT_CONTROL *)pBuf; pHT_Control-> = 1; if (pEntry->msiToTx == MSI_TOGGLE_BF) if (pEntry->mrqCnt == 0) pEntry->mrqCnt = TOGGLE_BF_PKTS; else { (pEntry->mrqCnt)--; if (pEntry->mrqCnt == 0) pEntry->msiToTx = 0; } pHT_Control->MFBorASC = ; pHT_Control->MFSI = /*update region*/ if (pEntry->msiToTx == MSI_TOGGLE_BF-1)/*MSI_TOGGLE_BF==6 is used to indicate the TxBF status is inverted for this packet*/ pEntry->msiToTx = 0; else if (pEntry->msiToTx != MSI_TOGGLE_BF) pEntry->msiToTx++; } #endif /* DBGPRINT(RT_DEBUG_TRACE, ("<----- MFB_PerPareMRQ\n"));*/ } #endif /* MFB_SUPPORT */ /* MlmeTxBfAllowed - returns true if ETxBF or ITxBF is supported and pTxRate is a valid BF mode */ BOOLEAN MlmeTxBfAllowed( IN RTMP_ADAPTER *pAd, IN MAC_TABLE_ENTRY *pEntry, IN RTMP_RA_LEGACY_TB *pTxRate) { /* ETxBF */ if ((pEntry->eTxBfEnCond > 0) && (pTxRate->Mode == MODE_HTMIX || pTxRate->Mode == MODE_HTGREENFIELD) #ifdef DBG_CTRL_SUPPORT && (!((pAd->CommonCfg.DebugFlags & DBF_NO_TXBF_3SS) && pTxRate->CurrMCS>20)) #endif /* DBG_CTRL_SUPPORT */ ) return TRUE; /* ITxBF */ if (pEntry->iTxBfEn && pTxRate->CurrMCS<16 && pTxRate->Mode!=MODE_CCK) return TRUE; return FALSE; } #endif /* TXBF_SUPPORT */