/*
 *************************************************************************
 * Ralink Tech Inc.
 * 5F., No.36, Taiyuan St., Jhubei City,
 * Hsinchu County 302,
 * Taiwan, R.O.C.
 *
 * (c) Copyright 2002-2007, Ralink Technology, Inc.
 *
 * This program is free software; you can redistribute it and/or modify  * 
 * it under the terms of the GNU General Public License as published by  * 
 * the Free Software Foundation; either version 2 of the License, or     * 
 * (at your option) any later version.                                   * 
 *                                                                       * 
 * This program is distributed in the hope that it will be useful,       * 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        * 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         * 
 * GNU General Public License for more details.                          * 
 *                                                                       * 
 * You should have received a copy of the GNU General Public License     * 
 * along with this program; if not, write to the                         * 
 * Free Software Foundation, Inc.,                                       * 
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             * 
 *                                                                       * 
 *************************************************************************/
    
/****************************************************************************

	Abstract:

	All related AGS (Adaptive Group Switching) function body.

***************************************************************************/ 
    
#define MODULE_AGS
#include "rt_config.h"
    
#ifdef AGS_SUPPORT
    

/* */
/* AGS: 1x1 HT-capable rate table */
/* */
    UCHAR AGS1x1HTRateTable[] = 
 {
	
	    /* */
	    /* [Item no.] [Mode]* [CurrMCS] [TrainUp] [TrainDown] [downMCS  ] [upMCS3] [upMCS2] [upMCS1] */
	    /* */
	    /* [Mode]*: */
	    /* bit0: STBC */
	    /* bit1: Short GI */
	    /* bit2: BW */
	    /* bit4~bit5: Mode (0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF) */
	    /* */
	    0x09, 0x08, 0, 0, 0, 0, 0, 0, 0,	/* Initial used item after association: the number of rate indexes, the initial mcs */
	    0x00, 0x21, 0, 30, 101, 0, 16, 8, 1,	/* MCS 0 */
	    0x01, 0x21, 1, 20, 50, 0, 16, 9, 2,	/* MCS 1 */
	    0x02, 0x21, 2, 20, 50, 1, 17, 9, 3,	/* MCS 2 */
	    0x03, 0x21, 3, 15, 50, 2, 17, 10, 4,	/* MCS 3 */
	    0x04, 0x21, 4, 15, 30, 3, 18, 11, 5,	/* MCS 4 */
	    0x05, 0x21, 5, 10, 25, 4, 18, 12, 6,	/* MCS 5 */
	    0x06, 0x21, 6, 8, 14, 5, 19, 12, 7,	/* MCS 6 */
	    0x07, 0x21, 7, 8, 14, 6, 19, 12, 8,	/* MCS 7 */
	    0x08, 0x23, 7, 8, 14, 7, 19, 12, 8,	/* MCS 7 + Short GI */
};



/* */
/* AGS: 2x2 HT-capable rate table */
/* */
    UCHAR AGS2x2HTRateTable[] = 
 {
	
	    /* */
	    /* [Item no.] [Mode]* [CurrMCS] [TrainUp] [TrainDown] [downMCS  ] [upMCS3] [upMCS2] [upMCS1] */
	    /* */
	    /* [Mode]*: */
	    /* bit0: STBC */
	    /* bit1: Short GI */
	    /* bit2: BW */
	    /* bit4~bit5: Mode (0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF) */
	    /* */
	    0x11, 0x10, 0, 0, 0, 0, 0, 0, 0,	/* Initial used item after association: the number of rate indexes, the initial mcs */
	    0x00, 0x21, 0, 30, 101, 0, 16, 8, 1,	/* MCS 0 */
	    0x01, 0x21, 1, 20, 50, 0, 16, 9, 2,	/* MCS 1 */
	    0x02, 0x21, 2, 20, 50, 1, 17, 9, 3,	/* MCS 2 */
	    0x03, 0x21, 3, 15, 50, 2, 17, 10, 4,	/* MCS 3 */
	    0x04, 0x21, 4, 15, 30, 3, 18, 11, 5,	/* MCS 4 */
	    0x05, 0x21, 5, 10, 25, 4, 18, 12, 6,	/* MCS 5 */
	    0x06, 0x21, 6, 8, 14, 5, 19, 12, 7,	/* MCS 6 */
	    0x07, 0x21, 7, 8, 14, 6, 19, 12, 7,	/* MCS 7 */
	    0x08, 0x20, 8, 30, 50, 0, 16, 9, 2,	/* MCS 8 */
	    0x09, 0x20, 9, 20, 50, 8, 17, 10, 4,	/* MCS 9 */
	    0x0A, 0x20, 10, 20, 50, 9, 18, 11, 5,	/* MCS 10 */
	    0x0B, 0x20, 11, 15, 30, 10, 18, 12, 6,	/* MCS 11 */
	    0x0C, 0x20, 12, 15, 30, 11, 20, 13, 12,	/* MCS 12 */
	    0x0D, 0x20, 13, 8, 20, 12, 20, 14, 13,	/* MCS 13 */
	    0x0E, 0x20, 14, 8, 18, 13, 21, 15, 14,	/* MCS 14 */
	    0x0F, 0x20, 15, 8, 25, 14, 21, 16, 15,	/* MCS 15 */
	    0x10, 0x22, 15, 8, 25, 15, 21, 16, 16,	/* MCS 15 + Short GI */
};



/* */
/* AGS: 3x3 HT-capable rate table */
/* */
    UCHAR AGS3x3HTRateTable[] = 
 {
	
	    /* */
	    /* [Item no.] [Mode]* [CurrMCS] [TrainUp] [TrainDown] [downMCS  ] [upMCS3] [upMCS2] [upMCS1] */
	    /* */
	    /* [Mode]*: */
	    /* bit0: STBC */
	    /* bit1: Short GI */
	    /* bit2: BW */
	    /* bit4~bit5: Mode (0:CCK, 1:OFDM, 2:HT Mix, 3:HT GF) */
	    /* */
	    0x19, 0x18, 0, 0, 0, 0, 0, 0, 0,	/* Initial used item after association: the number of rate indexes, the initial mcs */
	    0x00, 0x21, 0, 30, 101, 0, 16, 8, 1,	/* MCS 0 */
	    0x01, 0x21, 1, 20, 50, 0, 16, 9, 2,	/* MCS 1 */
	    0x02, 0x21, 2, 20, 50, 1, 17, 9, 3,	/* MCS 2 */
	    0x03, 0x21, 3, 15, 50, 2, 17, 10, 4,	/* MCS 3 */
	    0x04, 0x21, 4, 15, 30, 3, 18, 11, 5,	/* MCS 4 */
	    0x05, 0x21, 5, 10, 25, 4, 18, 12, 6,	/* MCS 5 */
	    0x06, 0x21, 6, 8, 14, 5, 19, 12, 7,	/* MCS 6 */
	    0x07, 0x21, 7, 8, 14, 6, 19, 12, 7,	/* MCS 7 */
	    0x08, 0x20, 8, 30, 50, 0, 16, 9, 2,	/* MCS 8 */
	    0x09, 0x20, 9, 20, 50, 8, 17, 10, 4,	/* MCS 9 */
	    0x0A, 0x20, 10, 20, 50, 9, 18, 11, 5,	/* MCS 10 */
	    0x0B, 0x20, 11, 15, 30, 10, 18, 12, 6,	/* MCS 11 */
	    0x0C, 0x20, 12, 15, 30, 11, 20, 13, 12,	/* MCS 12 */
	    0x0D, 0x20, 13, 8, 20, 12, 20, 14, 13,	/* MCS 13 */
	    0x0E, 0x20, 14, 8, 18, 13, 21, 15, 14,	/* MCS 14 */
	    0x0F, 0x20, 15, 8, 14, 14, 21, 15, 15,	/* MCS 15 */
	    0x10, 0x20, 16, 30, 50, 8, 17, 9, 3,	/* MCS 16 */
	    0x11, 0x20, 17, 20, 50, 16, 18, 11, 5,	/* MCS 17 */
	    0x12, 0x20, 18, 20, 50, 17, 19, 12, 7,	/* MCS 18 */
	    0x13, 0x20, 19, 15, 30, 18, 20, 13, 19,	/* MCS 19 */
	    0x14, 0x20, 20, 15, 30, 19, 21, 15, 20,	/* MCS 20 */
	    0x15, 0x20, 21, 8, 20, 20, 22, 21, 21,	/* MCS 21 */
	    0x16, 0x20, 22, 8, 20, 21, 23, 22, 22,	/* MCS 22 */
	    0x17, 0x20, 23, 6, 18, 22, 24, 23, 23,	/* MCS 23 */
	    0x18, 0x22, 23, 6, 14, 23, 24, 24, 24,	/* MCS 23 + Short GI */
};




INT Show_AGS_Proc(
    IN  PRTMP_ADAPTER	pAd, 
    IN  PSTRING			arg)
{
	MAC_TABLE_ENTRY *pEntry = &pAd->MacTab.Content[1];
	UINT32 IdQuality;


	printk("MCS Group\t\tMCS Index\n");
	printk("%d\t\t\t%d\n\n", pEntry->AGSCtrl.MCSGroup, pEntry->CurrTxRateIndex);

	printk("MCS Quality:\n");
	for(IdQuality=0; IdQuality<=23; IdQuality++)
		printk("%02d\t\t%d\n", IdQuality, pEntry->TxQuality[IdQuality]);

	return TRUE;
}


/* */
/* The dynamic Tx rate switching for AGS (Adaptive Group Switching) */
/* */
/* Parameters */
/*	pAd: The adapter data structure */
/*	pEntry: Pointer to a caller-supplied variable in which points to a MAC table entry */
/*	pTable: Pointer to a caller-supplied variable in wich points to a Tx rate switching table */
/*	TableSize: The size, in bytes, of the specified Tx rate switching table */
/*	pAGSStatisticsInfo: Pointer to a caller-supplied variable in which points to the statistics information */
/* */
/* Return Value: */
/*	None */
/* */
VOID MlmeDynamicTxRateSwitchingAGS(
	IN PRTMP_ADAPTER pAd, 
	IN PMAC_TABLE_ENTRY pEntry, 
	IN PUCHAR pTable, 
	IN UCHAR TableSize, 
	IN PAGS_STATISTICS_INFO pAGSStatisticsInfo,
	IN UCHAR InitTxRateIdx)
{
	UCHAR UpRateIdx = 0, DownRateIdx = 0, CurrRateIdx = 0;
	BOOLEAN bTxRateChanged = TRUE, bUpgradeQuality = FALSE;
	PRTMP_TX_RATE_SWITCH_AGS pCurrTxRate = NULL;
	PRTMP_TX_RATE_SWITCH	pNextTxRate = NULL;
	UCHAR TrainUp = 0, TrainDown = 0;
	CHAR RssiOffset = 0;

	DBGPRINT_RAW(RT_DEBUG_TRACE, ("\n\nAGS: ---> %s\n", __FUNCTION__));

	DBGPRINT(RT_DEBUG_TRACE, ("%s: QuickAGS: AccuTxTotalCnt = %lu, TxSuccess = %lu, TxRetransmit = %lu, TxFailCount = %lu, TxErrorRatio = %lu\n",
		__FUNCTION__, 
		pAGSStatisticsInfo->AccuTxTotalCnt, 
		pAGSStatisticsInfo->TxSuccess, 
		pAGSStatisticsInfo->TxRetransmit, 
		pAGSStatisticsInfo->TxFailCount, 
		pAGSStatisticsInfo->TxErrorRatio));

	/* for 3*3, 1st time, pEntry->CurrTxRateIndex = 24 in StaAddMacTableEntry() */
	CurrRateIdx = pEntry->CurrTxRateIndex;	

	if (CurrRateIdx >= TableSize)
	{
		CurrRateIdx = TableSize - 1;
	}

	pCurrTxRate = (PRTMP_TX_RATE_SWITCH_AGS)(&pTable[(CurrRateIdx + 1) *
												SIZE_OF_AGS_RATE_TABLE_ENTRY]);

	/* */
	/* Select the next upgrade rate and the next downgrade rate, if any */
	/* */
	do 
	{
		if (InitTxRateIdx == AGS3x3HTRateTable[1])
		{
			/* 3*3 table */
			DBGPRINT_RAW(RT_DEBUG_TRACE,
				("%s: AGS: pEntry->AGSCtrl.MCSGroup = %d, TxQuality2[%d] = %d,  "
				"TxQuality1[%d] = %d, TxQuality0[%d] = %d, pCurrTxRate->upMcs1 = %d, "
				"pCurrTxRate->ItemNo = %d\n",
				__FUNCTION__, 
				pEntry->AGSCtrl.MCSGroup, 
				pCurrTxRate->upMcs3, 
				pEntry->TxQuality[pCurrTxRate->upMcs3], 
				pCurrTxRate->upMcs2, 
				pEntry->TxQuality[pCurrTxRate->upMcs2], 
				pCurrTxRate->upMcs1, 
				pEntry->TxQuality[pCurrTxRate->upMcs1], 
				pCurrTxRate->upMcs1, 
				pCurrTxRate->ItemNo));

			/* */
			/* 3x3 peer device (Adhoc, DLS or AP) */
			/* */
			/* for 3*3, pEntry->AGSCtrl.MCSGroup = 0, 3, 3, 3, ... */
			switch (pEntry->AGSCtrl.MCSGroup)
			{
				case 0: /* MCS selection in round robin policy (different MCS group) */
				{
					UpRateIdx = pCurrTxRate->upMcs3;
					DBGPRINT_RAW(RT_DEBUG_TRACE,
							("ags> MCSGroup = 0; UpRateIdx = %d in group 3 is better.\n",
							UpRateIdx));

					/* MCS group #2 has better Tx quality */
					if ((pEntry->TxQuality[UpRateIdx] >
								pEntry->TxQuality[pCurrTxRate->upMcs2]) && 
					     (pCurrTxRate->upMcs2 != pCurrTxRate->ItemNo))
					{
						/* quality for group 2 is better */
						UpRateIdx = pCurrTxRate->upMcs2;

						DBGPRINT_RAW(RT_DEBUG_TRACE,
							("ags> MCSGroup = 0; UpRateIdx = %d in group 2 is better.\n",
							UpRateIdx));
					}

					/* MCS group #1 has better Tx quality */
					if ((pEntry->TxQuality[UpRateIdx] >
								pEntry->TxQuality[pCurrTxRate->upMcs1]) && 
					     (pCurrTxRate->upMcs1 != pCurrTxRate->ItemNo))
					{
						/* quality for group 1 is better */
						UpRateIdx = pCurrTxRate->upMcs1;

						DBGPRINT_RAW(RT_DEBUG_TRACE,
							("ags> MCSGroup = 0; UpRateIdx = %d in group 1 is better.\n",
							UpRateIdx));
					}
				}
				break;
				
				case 3:
				{
					UpRateIdx = pCurrTxRate->upMcs3;

					DBGPRINT_RAW(RT_DEBUG_TRACE,
							("ags> MCSGroup = 3; UpRateIdx = %d\n",
							UpRateIdx));
				}
				break;
				
				case 2:
				{
					UpRateIdx = pCurrTxRate->upMcs2;

					DBGPRINT_RAW(RT_DEBUG_TRACE,
							("ags> MCSGroup = 2; UpRateIdx = %d\n",
							UpRateIdx));
				}
				break;
				
				case 1:
				{
					UpRateIdx = pCurrTxRate->upMcs1;

					DBGPRINT_RAW(RT_DEBUG_TRACE,
							("ags> MCSGroup = 1; UpRateIdx = %d\n",
							UpRateIdx));
				}
				break;
				
				default:
				{
					DBGPRINT_RAW(RT_DEBUG_ERROR,
						("%s: AGS: [3x3 peer device (Adhoc, DLS or AP)], "
						"Incorrect MCS group, pEntry->AGSCtrl.MCSGroup = %d\n", 
						__FUNCTION__, 
						pEntry->AGSCtrl.MCSGroup));
				}
				break;
			}			

			if ((pEntry->AGSCtrl.MCSGroup == 0) && 
			     (((pEntry->TxQuality[pCurrTxRate->upMcs3] >
						pEntry->TxQuality[pCurrTxRate->upMcs2]) &&
					(pCurrTxRate->upMcs2 != pCurrTxRate->ItemNo)) || 
			     ((pEntry->TxQuality[pCurrTxRate->upMcs3] >
						pEntry->TxQuality[pCurrTxRate->upMcs1]) &&
					(pCurrTxRate->upMcs1 != pCurrTxRate->ItemNo))))
			{
				/* quality of group 2 or 1 is better than group 3 */
				/* just show debug information here */
				DBGPRINT_RAW(RT_DEBUG_TRACE,
					("%s: ###################################################"
					"#######################\n", 
					__FUNCTION__));

				DBGPRINT_RAW(RT_DEBUG_TRACE,
					("%s: AGS: [3x3 peer device (Adhoc, DLS or AP)], Before - "
					"pEntry->AGSCtrl.MCSGroup = %d, TxQuality2[%d] = %d,  "
					"TxQuality1[%d] = %d, TxQuality0[%d] = %d\n",
					__FUNCTION__, 
					pEntry->AGSCtrl.MCSGroup, 
					pCurrTxRate->upMcs3, 
					pEntry->TxQuality[pCurrTxRate->upMcs3], 
					pCurrTxRate->upMcs2, 
					pEntry->TxQuality[pCurrTxRate->upMcs2], 
					pCurrTxRate->upMcs1, 
					pEntry->TxQuality[pCurrTxRate->upMcs1]));
			}
		}
		else if (InitTxRateIdx == AGS2x2HTRateTable[1])
		{
			/* 2*2 table */

			DBGPRINT_RAW(RT_DEBUG_TRACE,
				("%s: AGS: pEntry->AGSCtrl.MCSGroup = %d, TxQuality1[%d] = %d, "
				"TxQuality0[%d] = %d, pCurrTxRate->upMcs1 = %d, "
				"pCurrTxRate->ItemNo = %d\n",
				__FUNCTION__, 
				pEntry->AGSCtrl.MCSGroup, 
				pCurrTxRate->upMcs2, 
				pEntry->TxQuality[pCurrTxRate->upMcs2], 
				pCurrTxRate->upMcs1, 
				pEntry->TxQuality[pCurrTxRate->upMcs1], 
				pCurrTxRate->upMcs1, 
				pCurrTxRate->ItemNo));

			/* */
			/* 2x2 peer device (Adhoc, DLS or AP) */
			/* */
			switch (pEntry->AGSCtrl.MCSGroup)
			{
				case 0: /* MCS selection in round robin policy */
				{
					UpRateIdx = pCurrTxRate->upMcs2;

					/* MCS group #1 has better Tx quality */
					if ((pEntry->TxQuality[UpRateIdx] >
								pEntry->TxQuality[pCurrTxRate->upMcs1]) && 
					     (pCurrTxRate->upMcs1 != pCurrTxRate->ItemNo))
					{
						UpRateIdx = pCurrTxRate->upMcs1;
					}
				}
				break;
				
				case 2:
				{
					UpRateIdx = pCurrTxRate->upMcs2;
				}
				break;
				
				case 1:
				{
					UpRateIdx = pCurrTxRate->upMcs1;
				}
				break;
				
				default:
				{
					DBGPRINT_RAW(RT_DEBUG_ERROR,
						("%s: AGS: [2x2 peer device (Adhoc, DLS or AP)], "
						"Incorrect MCS group, pEntry->AGSCtrl.MCSGroup = %d\n", 
						__FUNCTION__, 
						pEntry->AGSCtrl.MCSGroup));
				}
				break;
			}	

			if ((pEntry->AGSCtrl.MCSGroup == 0) && 
			     ((pEntry->TxQuality[pCurrTxRate->upMcs2] >
					pEntry->TxQuality[pCurrTxRate->upMcs1]) &&
					(pCurrTxRate->upMcs1 != pCurrTxRate->ItemNo)))
			{
				DBGPRINT_RAW(RT_DEBUG_TRACE,
					("%s: ###################################################"
					"#######################\n", 
					__FUNCTION__));

				DBGPRINT_RAW(RT_DEBUG_TRACE,
					("%s: AGS: [2x2 peer device (Adhoc, DLS or AP)], Before - "
					"pEntry->AGSCtrl.MCSGroup = %d, TxQuality1[%d] = %d, "
					"TxQuality0[%d] = %d\n",
					__FUNCTION__, 
					pEntry->AGSCtrl.MCSGroup, 
					pCurrTxRate->upMcs2, 
					pEntry->TxQuality[pCurrTxRate->upMcs2], 
					pCurrTxRate->upMcs1, 
					pEntry->TxQuality[pCurrTxRate->upMcs1]));
			}
		} 
		else 
		{
			/* */
			/* 1x1 peer device (Adhoc, DLS or AP) */
			/* */
			switch (pEntry->AGSCtrl.MCSGroup)
			{
				case 1:
				case 0:
				{
					UpRateIdx = pCurrTxRate->upMcs1;
				}
				break;
				
				default:
				{
					DBGPRINT_RAW(RT_DEBUG_ERROR,
						("%s: AGS: [1x1 peer device (Adhoc, DLS or AP)], "
						"Incorrect MCS group, pEntry->AGSCtrl.MCSGroup = %d\n", 
						__FUNCTION__, 
						pEntry->AGSCtrl.MCSGroup));
				}
				break;
			}	
		}


		/* */
		/* The STA uses the best Tx rate at this moment. */
		/* */
		if (UpRateIdx == pEntry->CurrTxRateIndex)
		{
			/* current rate is the best one */
			pEntry->AGSCtrl.MCSGroup = 0; /* Try to escape the local optima */

			DBGPRINT_RAW(RT_DEBUG_TRACE,
						("ags> Current rate is the best one!\n"));
			break;
		}
		
		if ((pEntry->TxQuality[UpRateIdx] > 0) &&
			(pEntry->AGSCtrl.MCSGroup > 0))
		{
			/*
				Quality of up rate is bad try to use lower group.
				So continue to get the up rate index.
			*/
			pEntry->AGSCtrl.MCSGroup--; /* Try to use the MCS of the lower MCS group */
		}
		else
		{
			break;
		}
	} while (1);


	DownRateIdx = pCurrTxRate->downMcs;

	DBGPRINT_RAW(RT_DEBUG_TRACE,
				("ags> UpRateIdx = %d, DownRateIdx = %d\n",
				UpRateIdx, DownRateIdx));

#ifdef DOT11_N_SUPPORT
	if ((pAGSStatisticsInfo->RSSI > -65) &&
		(pCurrTxRate->Mode >= MODE_HTMIX))
	{
		TrainUp = (pCurrTxRate->TrainUp + (pCurrTxRate->TrainUp >> 1));
		TrainDown	= (pCurrTxRate->TrainDown + (pCurrTxRate->TrainDown >> 1));
	}
	else
#endif /* DOT11_N_SUPPORT */
	{
		TrainUp = pCurrTxRate->TrainUp;
		TrainDown	= pCurrTxRate->TrainDown;
	}

	/* */
	/* Keep the TxRateChangeAction status */
	/* */
	pEntry->LastTimeTxRateChangeAction = pEntry->LastSecTxRateChangeAction;



	/* */
	/* MCS selection based on the RSSI information when the Tx samples are fewer than 15. */
	/* */
	if (pAGSStatisticsInfo->AccuTxTotalCnt <= 15)
	{
		CHAR idx = 0;
		UCHAR TxRateIdx;
		UCHAR MCS0 = 0, MCS1 = 0, MCS2 = 0, MCS3 = 0, MCS4 = 0,  MCS5 =0, MCS6 = 0, MCS7 = 0;	
		UCHAR MCS8 = 0, MCS9 = 0, MCS10 = 0, MCS11 = 0, MCS12 = 0, MCS13 = 0, MCS14 = 0, MCS15 = 0;
		UCHAR MCS16 = 0, MCS17 = 0, MCS18 = 0, MCS19 = 0, MCS20 = 0, MCS21 = 0, MCS22 = 0, MCS23 = 0;

		/* */
		/* Check the existence and index of each needed MCS */
		/* */
		/* for 3*3, maximum is 0x19 columns */
		while (idx < pTable[0])
		{
			pCurrTxRate = (PRTMP_TX_RATE_SWITCH_AGS)(&pTable[(idx + 1) * SIZE_OF_AGS_RATE_TABLE_ENTRY]);

			if (pCurrTxRate->CurrMCS == MCS_0)
			{
				MCS0 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_1)
			{
				MCS1 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_2)
			{
				MCS2 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_3)
			{
				MCS3 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_4)
			{
				MCS4 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_5)
			{
			 	MCS5 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_6)
			{
			    MCS6 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_7)
			{
				MCS7 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_8)
			{
				MCS8 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_9)
			{
				MCS9 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_10)
			{
				MCS10 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_11)
			{
				MCS11 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_12)
			{
				MCS12 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_13)
			{
				MCS13 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_14)
			{
				MCS14 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_15)
			{
				MCS15 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_16)
			{
				MCS16 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_17)
			{
				MCS17 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_18)
			{
				MCS18 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_19)
			{
				MCS19 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_20)
			{
				MCS20 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_21)
			{
				MCS21 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_22)
			{
				MCS22 = idx;
			}
			else if (pCurrTxRate->CurrMCS == MCS_23)
			{
				MCS23 = idx;
			}
			
			idx++;
		}

		RssiOffset = 0;
		
		if (InitTxRateIdx == AGS3x3HTRateTable[1])
		{
			/* */
			/* 3x3 peer device (Adhoc, DLS or AP) */
			/* */
			if (MCS23 && (pAGSStatisticsInfo->RSSI > (-67 + RssiOffset)))
			{
				TxRateIdx = MCS23;
			}
			else if (MCS22 && (pAGSStatisticsInfo->RSSI > (-69 + RssiOffset)))
			{
				TxRateIdx = MCS22;
			}
			else if (MCS21 && (pAGSStatisticsInfo->RSSI > (-72 + RssiOffset)))
			{
				TxRateIdx = MCS21;
			}
			else if (MCS20 && (pAGSStatisticsInfo->RSSI > (-74 + RssiOffset)))
			{
				TxRateIdx = MCS20;
			}
			else if (MCS19 && (pAGSStatisticsInfo->RSSI > (-78 + RssiOffset)))
			{
				TxRateIdx = MCS19;
			}
			else if (MCS18 && (pAGSStatisticsInfo->RSSI > (-80 + RssiOffset)))
			{
				TxRateIdx = MCS18;
			}
			else if (MCS17 && (pAGSStatisticsInfo->RSSI > (-85 + RssiOffset)))
			{
				TxRateIdx = MCS17;
			}
			else
			{
				TxRateIdx = MCS16;
			}

			pEntry->AGSCtrl.MCSGroup = 3;

			DBGPRINT_RAW(RT_DEBUG_TRACE,
					("ags> Group3 RSSI = %d, TxRateIdx = %d\n",
					pAGSStatisticsInfo->RSSI, TxRateIdx));
		}
		else if (InitTxRateIdx == AGS2x2HTRateTable[1])
		{
			/* */
			/* 2x2 peer device (Adhoc, DLS or AP) */
			/* */
			if (MCS15 && (pAGSStatisticsInfo->RSSI > (-69 + RssiOffset)))
			{
				TxRateIdx = MCS15;
			}
			else if (MCS14 && (pAGSStatisticsInfo->RSSI > (-71 + RssiOffset)))
			{
				TxRateIdx = MCS14;
			}
			else if (MCS13 && (pAGSStatisticsInfo->RSSI > (-74 + RssiOffset)))
			{
				TxRateIdx = MCS13;
			}
			else if (MCS12 && (pAGSStatisticsInfo->RSSI > (-76 + RssiOffset)))
			{
				TxRateIdx = MCS12;
			}
			else if (MCS11 && (pAGSStatisticsInfo->RSSI > (-80 + RssiOffset)))
			{
				TxRateIdx = MCS11;
			}
			else if (MCS10 && (pAGSStatisticsInfo->RSSI > (-82 + RssiOffset)))
			{
				TxRateIdx = MCS10;
			}
			else if (MCS9 && (pAGSStatisticsInfo->RSSI > (-87 + RssiOffset)))
			{
				TxRateIdx = MCS9;
			}
			else
			{
				TxRateIdx = MCS8;
			}
			
			pEntry->AGSCtrl.MCSGroup = 2;
		} 
		else 
		{
			/* */
			/* 1x1 peer device (Adhoc, DLS or AP) */
			/* */
			if (MCS7 && (pAGSStatisticsInfo->RSSI > (-71 + RssiOffset)))
			{
				TxRateIdx = MCS7;
			}
			else if (MCS6 && (pAGSStatisticsInfo->RSSI > (-73 + RssiOffset)))
			{
				TxRateIdx = MCS6;
			}
			else if (MCS5 && (pAGSStatisticsInfo->RSSI > (-76 + RssiOffset)))
			{
				TxRateIdx = MCS5;
			}
			else if (MCS4 && (pAGSStatisticsInfo->RSSI > (-78 + RssiOffset)))
			{
				TxRateIdx = MCS4;
			}
			else if (MCS3 && (pAGSStatisticsInfo->RSSI > (-82 + RssiOffset)))
			{
				TxRateIdx = MCS3;
			}
			else if (MCS2 && (pAGSStatisticsInfo->RSSI > (-84 + RssiOffset)))
			{
				TxRateIdx = MCS2;
			}
			else if (MCS1 && (pAGSStatisticsInfo->RSSI > (-89 + RssiOffset)))
			{
				TxRateIdx = MCS1;
			}
			else
			{
				TxRateIdx = MCS0;
			}
			
			pEntry->AGSCtrl.MCSGroup = 1;
		}

		pEntry->AGSCtrl.lastRateIdx = pEntry->CurrTxRateIndex;
		pEntry->CurrTxRateIndex = TxRateIdx;

		pNextTxRate = (PRTMP_TX_RATE_SWITCH)(&pTable[(pEntry->CurrTxRateIndex + 1) * SIZE_OF_AGS_RATE_TABLE_ENTRY]);
		MlmeSetTxRate(pAd, pEntry, pNextTxRate);

		RTMPZeroMemory(pEntry->TxQuality, (sizeof(USHORT) * MAX_STEP_OF_TX_RATE_SWITCH));
		RTMPZeroMemory(pEntry->PER, (sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH));

		pEntry->fLastSecAccordingRSSI = TRUE;			
		/* reset all OneSecTx counters */
		RESET_ONE_SEC_TX_CNT(pEntry);
		return;
	}


	/* */
	/* The MCS selection is based on the RSSI and skips the rate tuning this time. */
	/* */
	if (pEntry->fLastSecAccordingRSSI == TRUE)
	{
		pEntry->fLastSecAccordingRSSI = FALSE;
		pEntry->LastSecTxRateChangeAction = 0;
		/* reset all OneSecTx counters */
		RESET_ONE_SEC_TX_CNT(pEntry);
		
		DBGPRINT_RAW(RT_DEBUG_TRACE,
			("%s: AGS: The MCS selection is based on the RSSI, and skips "
			"the rate tuning this time.\n", 
			__FUNCTION__));

		return;
	}


	DBGPRINT_RAW(RT_DEBUG_TRACE,
				("%s: AGS: TrainUp:%d, TrainDown:%d\n",
				__FUNCTION__, TrainUp, TrainDown));

	do
	{
		BOOLEAN	bTrainUpDown = FALSE;

		DBGPRINT_RAW(RT_DEBUG_TRACE,
					("%s: AGS: TxQuality[CurrRateIdx(%d)] = %d, UpPenalty:%d\n",
					__FUNCTION__, CurrRateIdx,
					pEntry->TxQuality[CurrRateIdx], pEntry->TxRateUpPenalty));

		if (pAGSStatisticsInfo->TxErrorRatio >= TrainDown) /* Poor quality */
		{
			/* error ratio too high, do rate down */
			DBGPRINT_RAW(RT_DEBUG_TRACE,
						("%s: AGS: (DOWN) TxErrorRatio >= TrainDown\n",__FUNCTION__));
			bTrainUpDown = TRUE;
			pEntry->TxQuality[CurrRateIdx] = AGS_TX_QUALITY_WORST_BOUND;
		}
		else if (pAGSStatisticsInfo->TxErrorRatio <= TrainUp) /* Good quality */
		{
			/* error ratio low, maybe rate up */
			bTrainUpDown = TRUE;
			bUpgradeQuality = TRUE;

			DBGPRINT_RAW(RT_DEBUG_TRACE,
				("%s: AGS: (UP) pEntry->TxQuality[CurrRateIdx] = %d, "
				"pEntry->TxRateUpPenalty = %d\n",
				__FUNCTION__, 
				pEntry->TxQuality[CurrRateIdx], 
				pEntry->TxRateUpPenalty));

			if (pEntry->TxQuality[CurrRateIdx])
			{
				/* Good quality in the current Tx rate */
				pEntry->TxQuality[CurrRateIdx]--;
			}

			if (pEntry->TxRateUpPenalty) /* no use for the parameter */
			{
				pEntry->TxRateUpPenalty--;
			}
			else
			{
				if (pEntry->TxQuality[pCurrTxRate->upMcs3] &&
					(pCurrTxRate->upMcs3 != CurrRateIdx))
				{
					/* hope do rate up next time for the MCS */
					pEntry->TxQuality[pCurrTxRate->upMcs3]--;
				}
				
				if (pEntry->TxQuality[pCurrTxRate->upMcs2] &&
					(pCurrTxRate->upMcs2 != CurrRateIdx))
				{
					/* hope do rate up next time for the MCS */
					pEntry->TxQuality[pCurrTxRate->upMcs2]--;
				}
				
				if (pEntry->TxQuality[pCurrTxRate->upMcs1] &&
					(pCurrTxRate->upMcs1 != CurrRateIdx))
				{
					/* hope do rate up next time for the MCS */
					pEntry->TxQuality[pCurrTxRate->upMcs1]--;
				}
			}
		}
		else if (pEntry->AGSCtrl.MCSGroup > 0) /*even if TxErrorRatio > TrainUp */
		{
			/* not bad and not good */
			if (UpRateIdx != 0)
			{
				bTrainUpDown = TRUE;
				
				if (pEntry->TxQuality[CurrRateIdx])
				{
					/* Good quality in the current Tx rate */
					pEntry->TxQuality[CurrRateIdx]--;
				}

				if (pEntry->TxQuality[UpRateIdx])
				{
					/* It may improve next train-up Tx rate's quality */
					pEntry->TxQuality[UpRateIdx]--;
				}

				DBGPRINT_RAW(RT_DEBUG_TRACE, ("ags> not bad and not good\n"));
			}
		}

		/* update error ratio for current MCS */
		pEntry->PER[CurrRateIdx] = (UCHAR)(pAGSStatisticsInfo->TxErrorRatio);

		/* */
		/* Update the current Tx rate */
		/* */
		if (bTrainUpDown)
		{
			/* need to rate up or down */
			DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: AGS: bTrainUpDown = %d, CurrRateIdx = %d, DownRateIdx = %d, UpRateIdx = %d, pEntry->TxQuality[CurrRateIdx] = %d, pEntry->TxQuality[UpRateIdx] = %d\n",
				__FUNCTION__, 
				bTrainUpDown, 
				CurrRateIdx, 
				DownRateIdx, 
				UpRateIdx, 
				pEntry->TxQuality[CurrRateIdx], 
				pEntry->TxQuality[UpRateIdx]));
			
			/* Downgrade Tx rate */
			if ((CurrRateIdx != DownRateIdx) && 
			     (pEntry->TxQuality[CurrRateIdx] >= AGS_TX_QUALITY_WORST_BOUND))
			{
				pEntry->CurrTxRateIndex = DownRateIdx;
				pEntry->LastSecTxRateChangeAction = 2; /* Tx rate down */
				DBGPRINT_RAW(RT_DEBUG_TRACE, ("ags> rate down!\n"));
			}
			else if ((CurrRateIdx != UpRateIdx) && 
					(pEntry->TxQuality[UpRateIdx] <= 0)) /* Upgrade Tx rate */
			{
				pEntry->CurrTxRateIndex = UpRateIdx;
				pEntry->LastSecTxRateChangeAction = 1; /* Tx rate up */
				DBGPRINT_RAW(RT_DEBUG_TRACE, ("ags> rate up!\n"));
			}
		}
	} while (FALSE);


	DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: AGS: pEntry->CurrTxRateIndex = %d, CurrRateIdx = %d, pEntry->LastSecTxRateChangeAction = %d\n", 
		__FUNCTION__, 
		pEntry->CurrTxRateIndex, 
		CurrRateIdx, 
		pEntry->LastSecTxRateChangeAction));

	/* rate up/down post handle */
	/* Tx rate up */
	if ((pEntry->CurrTxRateIndex != CurrRateIdx) && 
	     (pEntry->LastSecTxRateChangeAction == 1))
	{
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: AGS: ++TX rate from %d to %d\n", 
			__FUNCTION__, 
			CurrRateIdx, 
			pEntry->CurrTxRateIndex));
		
		pEntry->TxRateUpPenalty = 0;
		pEntry->LastSecTxRateChangeAction = 1; /* Tx rate up */
		RTMPZeroMemory(pEntry->PER, sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH);
		pEntry->AGSCtrl.lastRateIdx = CurrRateIdx;

		/* */
		/* Tx rate fast train up */
		/* */
		if (!pAd->StaCfg.StaQuickResponeForRateUpTimerRunning)
		{
			RTMPSetTimer(&pAd->StaCfg.StaQuickResponeForRateUpTimer, 100);

			pAd->StaCfg.StaQuickResponeForRateUpTimerRunning = TRUE;
		}

		bTxRateChanged = TRUE;
	}
	else if ((pEntry->CurrTxRateIndex != CurrRateIdx) && 
	             (pEntry->LastSecTxRateChangeAction == 2)) /* Tx rate down */
	{
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: AGS: --TX rate from %d to %d\n", 
			__FUNCTION__, 
			CurrRateIdx, 
			pEntry->CurrTxRateIndex));
		
		pEntry->TxRateUpPenalty = 0; /* No penalty */
		pEntry->LastSecTxRateChangeAction = 2; /* Tx rate down */
		pEntry->TxQuality[pEntry->CurrTxRateIndex] = 0;
		pEntry->PER[pEntry->CurrTxRateIndex] = 0;
		pEntry->AGSCtrl.lastRateIdx = CurrRateIdx;

		/* */
		/* Tx rate fast train down */
		/* */
		if (!pAd->StaCfg.StaQuickResponeForRateUpTimerRunning)
		{
			RTMPSetTimer(&pAd->StaCfg.StaQuickResponeForRateUpTimer, 100);
		
			pAd->StaCfg.StaQuickResponeForRateUpTimerRunning = TRUE;
		}

		bTxRateChanged = TRUE;
	}
	else /* Tx rate remains unchanged. */
	{
		pEntry->LastSecTxRateChangeAction = 0; /* Tx rate remains unchanged. */
		bTxRateChanged = FALSE;
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("ags> no rate up/down!\n"));
	}

	pEntry->LastTxOkCount = pAGSStatisticsInfo->TxSuccess;

	/* set new tx rate */
	pNextTxRate = (PRTMP_TX_RATE_SWITCH)\
									(&pTable[(pEntry->CurrTxRateIndex + 1) *
									SIZE_OF_AGS_RATE_TABLE_ENTRY]);

	if ((bTxRateChanged == TRUE) && (pNextTxRate != NULL))
	{
		DBGPRINT_RAW(RT_DEBUG_TRACE,
					("ags> set new rate MCS = %d!\n", pEntry->CurrTxRateIndex));
		MlmeSetTxRate(pAd, pEntry, pNextTxRate);
	}

	/* */
	/* RDG threshold control for the infrastructure mode only */
	/* */
/*	if (INFRA_ON(pAd) && (pAd->OpMode == OPMODE_STA) && (!DLS_ON(pAd)) && (!TDLS_ON(pAd))) */
	if (INFRA_ON(pAd))
	{
		if ((pAd->CommonCfg.bRdg == TRUE) &&
			CLIENT_STATUS_TEST_FLAG(&pAd->MacTab.Content[BSSID_WCID],
									fCLIENT_STATUS_RDG_CAPABLE)) /* RDG capable */
		{
			TXOP_THRESHOLD_CFG_STRUC TxopThCfg = {{0}};
			TX_LINK_CFG_STRUC TxLinkCfg = {{0}};
			
			if ((pAd->RalinkCounters.OneSecReceivedByteCount > (pAd->RalinkCounters.OneSecTransmittedByteCount * 5)) && 
			     (pNextTxRate->CurrMCS != MCS_23) && 
			     ((pAd->RalinkCounters.OneSecReceivedByteCount + pAd->RalinkCounters.OneSecTransmittedByteCount) >= (50 * 1024)))
			{
				RTMP_IO_READ32(pAd, TX_LINK_CFG, &TxLinkCfg.word);
				TxLinkCfg.field.TxRDGEn = 0;
				RTMP_IO_WRITE32(pAd, TX_LINK_CFG, TxLinkCfg.word);

				RTMP_IO_READ32(pAd, TXOP_THRES_CFG, &TxopThCfg.word);
				TxopThCfg.field.RDG_IN_THRES = 0xFF; /* Similar to diable Rx RDG */
				TxopThCfg.field.RDG_OUT_THRES = 0x00;
				RTMP_IO_WRITE32(pAd, TXOP_THRES_CFG, TxopThCfg.word);

				DBGPRINT_RAW(RT_DEBUG_TRACE, ("AGS: %s: RDG_IN_THRES = 0xFF\n", __FUNCTION__));
			}
			else
			{
				RTMP_IO_READ32(pAd, TX_LINK_CFG, &TxLinkCfg.word);
				TxLinkCfg.field.TxRDGEn = 1;
				RTMP_IO_WRITE32(pAd, TX_LINK_CFG, TxLinkCfg.word);

				RTMP_IO_READ32(pAd, TXOP_THRES_CFG, &TxopThCfg.word);
				TxopThCfg.field.RDG_IN_THRES = 0x00;
				TxopThCfg.field.RDG_OUT_THRES = 0x00;
				RTMP_IO_WRITE32(pAd, TXOP_THRES_CFG, TxopThCfg.word);

				DBGPRINT_RAW(RT_DEBUG_TRACE, ("AGS: %s: RDG_IN_THRES = 0x00\n", __FUNCTION__));
			}
		}
	}

	/* reset all OneSecTx counters */
	RESET_ONE_SEC_TX_CNT(pEntry);

	DBGPRINT_RAW(RT_DEBUG_TRACE, ("AGS: <--- %s\n", __FUNCTION__));
}

/* */
/* Auto Tx rate faster train up/down for AGS (Adaptive Group Switching) */
/* */
/* Parameters */
/*	pAd: The adapter data structure */
/*	pEntry: Pointer to a caller-supplied variable in which points to a MAC table entry */
/*	pTable: Pointer to a caller-supplied variable in wich points to a Tx rate switching table */
/*	TableSize: The size, in bytes, of the specified Tx rate switching table */
/*	pAGSStatisticsInfo: Pointer to a caller-supplied variable in which points to the statistics information */
/* */
/* Return Value: */
/*	None */
/* */
VOID StaQuickResponeForRateUpExecAGS(
	IN PRTMP_ADAPTER pAd, 
	IN PMAC_TABLE_ENTRY pEntry, 
	IN PUCHAR pTable, 
	IN UCHAR TableSize, 
	IN PAGS_STATISTICS_INFO pAGSStatisticsInfo,
	IN UCHAR InitTxRateIdx)
{
	UCHAR UpRateIdx = 0, DownRateIdx = 0, CurrRateIdx = 0;
	BOOLEAN bTxRateChanged = TRUE;
	PRTMP_TX_RATE_SWITCH_AGS pCurrTxRate = NULL;
	PRTMP_TX_RATE_SWITCH	 pNextTxRate = NULL;
	UCHAR TrainDown = 0, TrainUp = 0;
	CHAR ratio = 0;
	ULONG OneSecTxNoRetryOKRationCount = 0;


	DBGPRINT_RAW(RT_DEBUG_TRACE, ("QuickAGS: ---> %s\n", __FUNCTION__));

	pAd->StaCfg.StaQuickResponeForRateUpTimerRunning = FALSE;

	DBGPRINT(RT_DEBUG_TRACE,
		("%s: QuickAGS: AccuTxTotalCnt = %lu, TxSuccess = %lu, "
		"TxRetransmit = %lu, TxFailCount = %lu, TxErrorRatio = %lu\n",
		__FUNCTION__, 
		pAGSStatisticsInfo->AccuTxTotalCnt, 
		pAGSStatisticsInfo->TxSuccess, 
		pAGSStatisticsInfo->TxRetransmit, 
		pAGSStatisticsInfo->TxFailCount, 
		pAGSStatisticsInfo->TxErrorRatio));

	CurrRateIdx = pEntry->CurrTxRateIndex;	

	if (CurrRateIdx >= TableSize)
	{
		CurrRateIdx = TableSize - 1;
	}

	UpRateIdx = DownRateIdx = pEntry->AGSCtrl.lastRateIdx;

	pCurrTxRate = (PRTMP_TX_RATE_SWITCH_AGS)(&pTable[(CurrRateIdx + 1) *
												SIZE_OF_AGS_RATE_TABLE_ENTRY]);

	if ((pAGSStatisticsInfo->RSSI > -65) && (pCurrTxRate->Mode >= MODE_HTMIX))
	{
		TrainUp = (pCurrTxRate->TrainUp + (pCurrTxRate->TrainUp >> 1));
		TrainDown	= (pCurrTxRate->TrainDown + (pCurrTxRate->TrainDown >> 1));
	}
	else
	{
		TrainUp = pCurrTxRate->TrainUp;
		TrainDown	= pCurrTxRate->TrainDown;
	}
		
	/* */
	/* MCS selection based on the RSSI information when the Tx samples are fewer than 15. */
	/* */
	if (pAGSStatisticsInfo->AccuTxTotalCnt <= 15)
	{
		RTMPZeroMemory(pEntry->TxQuality, sizeof(USHORT) * MAX_STEP_OF_TX_RATE_SWITCH);
		RTMPZeroMemory(pEntry->PER, sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH);

		if ((pEntry->LastSecTxRateChangeAction == 1) && (CurrRateIdx != DownRateIdx))
		{
			pEntry->CurrTxRateIndex = DownRateIdx;
			pEntry->TxQuality[CurrRateIdx] = AGS_TX_QUALITY_WORST_BOUND;
		}
		else if ((pEntry->LastSecTxRateChangeAction == 2) && (CurrRateIdx != UpRateIdx))
		{
			pEntry->CurrTxRateIndex = UpRateIdx;
		}

		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: AccuTxTotalCnt <= 15, train back to original rate\n", 
			__FUNCTION__));
		
		return;
	}

	do
	{
		if (pEntry->LastTimeTxRateChangeAction == 0)
		{
			ratio = 5;
		}
		else
		{
			ratio = 4;
		}

		if (pAGSStatisticsInfo->TxErrorRatio >= TrainDown) /* Poor quality */
		{
			pEntry->TxQuality[CurrRateIdx] = AGS_TX_QUALITY_WORST_BOUND;
		}

		pEntry->PER[CurrRateIdx] = (UCHAR)(pAGSStatisticsInfo->TxErrorRatio);

		OneSecTxNoRetryOKRationCount = (pAGSStatisticsInfo->TxSuccess * ratio);
		
		/* Tx rate down */
		if ((pEntry->LastSecTxRateChangeAction == 1) &&
			(CurrRateIdx != DownRateIdx))
		{
			if ((pEntry->LastTxOkCount + 2) >= OneSecTxNoRetryOKRationCount) /* Poor quality */
			{
				pEntry->CurrTxRateIndex = DownRateIdx;
				pEntry->TxQuality[CurrRateIdx] = AGS_TX_QUALITY_WORST_BOUND;

				DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: (UP) bad Tx ok count (L:%lu, C:%lu)\n", 
					__FUNCTION__, 
					pEntry->LastTxOkCount, 
					OneSecTxNoRetryOKRationCount));
			}
			else /* Good quality */
			{
				DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: (UP) keep rate-up (L:%lu, C:%lu)\n", 
					__FUNCTION__, 
					pEntry->LastTxOkCount, 
					OneSecTxNoRetryOKRationCount));

				RTMPZeroMemory(pEntry->TxQuality, sizeof(USHORT) * MAX_STEP_OF_TX_RATE_SWITCH);

				if (pEntry->AGSCtrl.MCSGroup == 0)
				{
					if (InitTxRateIdx == AGS3x3HTRateTable[1])
					{
						/* */
						/* 3x3 peer device (Adhoc, DLS or AP) */
						/* */
						pEntry->AGSCtrl.MCSGroup = 3;
					}
					else if (InitTxRateIdx == AGS2x2HTRateTable[1])
					{
						/* */
						/* 2x2 peer device (Adhoc, DLS or AP) */
						/* */
						pEntry->AGSCtrl.MCSGroup = 2;
					}
					else
					{
						pEntry->AGSCtrl.MCSGroup = 1;
					}
				}
			}
		}
		else if ((pEntry->LastSecTxRateChangeAction == 2) &&
				(CurrRateIdx != UpRateIdx)) /* Tx rate up */
		{
			if ((pAGSStatisticsInfo->TxErrorRatio >= 50) ||
				(pAGSStatisticsInfo->TxErrorRatio >= TrainDown)) /* Poor quality */
			{
				if (InitTxRateIdx == AGS3x3HTRateTable[1])
				{
					/* */
					/* 3x3 peer device (Adhoc, DLS or AP) */
					/* */
					pEntry->AGSCtrl.MCSGroup = 3;
				}
				else if (InitTxRateIdx == AGS2x2HTRateTable[1])
				{
					/* */
					/* 2x2 peer device (Adhoc, DLS or AP) */
					/* */
					pEntry->AGSCtrl.MCSGroup = 2;
				}
				else
				{
					pEntry->AGSCtrl.MCSGroup = 1;
				}

				DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: (DOWN) direct train down (TxErrorRatio[%lu] >= TrainDown[%d])\n", 
					__FUNCTION__, 
					pAGSStatisticsInfo->TxErrorRatio, 
					TrainDown));
			}
			else if ((pEntry->LastTxOkCount + 2) >= OneSecTxNoRetryOKRationCount)
			{
				pEntry->CurrTxRateIndex = UpRateIdx;
				
				DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: (DOWN) bad tx ok count (L:%lu, C:%lu)\n", 
					__FUNCTION__, 
					pEntry->LastTxOkCount, 
					OneSecTxNoRetryOKRationCount));
			}
			else
			{
				if (InitTxRateIdx == AGS3x3HTRateTable[1])
				{
					/* */
					/* 3x3 peer device (Adhoc, DLS or AP) */
					/* */
					pEntry->AGSCtrl.MCSGroup = 3;
				}
				else if (InitTxRateIdx == AGS2x2HTRateTable[1])
				{
					/* */
					/* 2x2 peer device (Adhoc, DLS or AP) */
					/* */
					pEntry->AGSCtrl.MCSGroup = 2;
				}
				else
				{
					pEntry->AGSCtrl.MCSGroup = 1;
				}

				DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: (Down) keep rate-down (L:%lu, C:%lu)\n", 
					__FUNCTION__, 
					pEntry->LastTxOkCount, 
					OneSecTxNoRetryOKRationCount));
			}
		}
	}while (FALSE);

	DBGPRINT_RAW(RT_DEBUG_TRACE,
				("ags> new group = %d\n", pEntry->AGSCtrl.MCSGroup));

	/* */
	/* Last action is rate-up */
	/* */
	if (pEntry->LastSecTxRateChangeAction == 1) 
	{
		/* looking for the next group with valid MCS */
		if ((pEntry->CurrTxRateIndex != CurrRateIdx) && (pEntry->AGSCtrl.MCSGroup > 0))
		{
			pEntry->AGSCtrl.MCSGroup--; /* Try to use the MCS of the lower MCS group */
			pCurrTxRate = (PRTMP_TX_RATE_SWITCH_AGS)(&pTable[(DownRateIdx + 1) * SIZE_OF_AGS_RATE_TABLE_ENTRY]);
		}
		
		/* UpRateIdx is for temp use in this section */
		switch (pEntry->AGSCtrl.MCSGroup)
		{
			case 3: 
			{
				UpRateIdx = pCurrTxRate->upMcs3;
			}
			break;
			
			case 2: 
			{
				UpRateIdx = pCurrTxRate->upMcs2;
			}
			break;
			
			case 1: 
			{
				UpRateIdx = pCurrTxRate->upMcs1;
			}
			break;
			
			case 0: 
			{
				UpRateIdx = CurrRateIdx;
			}
			break;
			
			default: 
			{
				DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: Incorrect MCS group, pEntry->AGSCtrl.MCSGroup = %d\n", 
					__FUNCTION__, 
					pEntry->AGSCtrl.MCSGroup));
			}
			break;
		}

		if (UpRateIdx == pEntry->CurrTxRateIndex)
		{
			pEntry->AGSCtrl.MCSGroup = 0; /* Try to escape the local optima */
		}
		
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: next MCS group,  pEntry->AGSCtrl.MCSGroup = %d\n", 
			__FUNCTION__, 
			pEntry->AGSCtrl.MCSGroup));
		
	}

	if ((pEntry->CurrTxRateIndex != CurrRateIdx) && 
	     (pEntry->LastSecTxRateChangeAction == 2)) /* Tx rate up */
	{
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: ++TX rate from %d to %d\n", 
			__FUNCTION__, 
			CurrRateIdx, 
			pEntry->CurrTxRateIndex));	
		
		pEntry->TxRateUpPenalty = 0;
		pEntry->TxQuality[pEntry->CurrTxRateIndex] = 0; /*restore the TxQuality from max to 0 */
		RTMPZeroMemory(pEntry->PER, sizeof(UCHAR) * MAX_STEP_OF_TX_RATE_SWITCH);
	}
	else if ((pEntry->CurrTxRateIndex != CurrRateIdx) && 
	            (pEntry->LastSecTxRateChangeAction == 1)) /* Tx rate down */
	{
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: --TX rate from %d to %d\n", 
			__FUNCTION__, 
			CurrRateIdx, 
			pEntry->CurrTxRateIndex));
		
		pEntry->TxRateUpPenalty = 0; /* No penalty */
		pEntry->TxQuality[pEntry->CurrTxRateIndex] = 0;
		pEntry->PER[pEntry->CurrTxRateIndex] = 0;
	}
	else
	{
		bTxRateChanged = FALSE;
		
		DBGPRINT_RAW(RT_DEBUG_TRACE, ("%s: QuickAGS: rate is not changed\n", 
			__FUNCTION__));
	}

	pNextTxRate = (PRTMP_TX_RATE_SWITCH)(&pTable[(pEntry->CurrTxRateIndex + 1) * SIZE_OF_AGS_RATE_TABLE_ENTRY]);
	if ((bTxRateChanged == TRUE) && (pNextTxRate != NULL))
	{
		DBGPRINT_RAW(RT_DEBUG_TRACE,
					("ags> confirm current rate MCS = %d!\n", pEntry->CurrTxRateIndex));

		MlmeSetTxRate(pAd, pEntry, pNextTxRate);
	}

	/* reset all OneSecTx counters */
	RESET_ONE_SEC_TX_CNT(pEntry);

	DBGPRINT_RAW(RT_DEBUG_TRACE, ("QuickAGS: <--- %s\n", __FUNCTION__));
}



#endif	/* AGS_SUPPORT */
    
/* End of ags.c */ 
