[WCNCR00155320] misc: Add last second Tx PER and T/Rx MCS distribution support

[Description]
Add driver command "GET_MCS_INFO" for outputing following result:
1. Last Second Tx MCS PER
2. Last Second Tx MCS distribution
3. Last Second Rx MCS distribution

Change-Id: I027e1743dac1efdc8ad761dc137598a8da8569ea
Signed-off-by: Glenn Tung <glenn.tung@mediatek.com>
Feature: misc
CR-Id: WCNCR00166815
diff --git a/common/wlan_oid.c b/common/wlan_oid.c
index a12eddd..1aa4094 100644
--- a/common/wlan_oid.c
+++ b/common/wlan_oid.c
@@ -11941,6 +11941,48 @@
 }				/* wlanoidQueryMibInfo */
 #endif
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+WLAN_STATUS
+wlanoidTxMcsInfo(IN P_ADAPTER_T prAdapter,
+		       IN PVOID pvQueryBuffer, IN UINT_32 u4QueryBufferLen, OUT PUINT_32 pu4QueryInfoLen)
+{
+	struct PARAM_TX_MCS_INFO *prMcsInfo;
+
+	DEBUGFUNC("wlanoidQueryWlanInfo");
+	DBGLOG(REQ, LOUD, "\n");
+
+	ASSERT(prAdapter);
+	if (u4QueryBufferLen)
+		ASSERT(pvQueryBuffer);
+	ASSERT(pu4QueryInfoLen);
+
+	*pu4QueryInfoLen = sizeof(struct PARAM_TX_MCS_INFO);
+
+	if (prAdapter->rAcpiState == ACPI_STATE_D3) {
+		DBGLOG(REQ, WARN,
+		       "Fail in query receive error! (Adapter not ready). ACPI=D%d, Radio=%d\n",
+		       prAdapter->rAcpiState, prAdapter->fgIsRadioOff);
+		*pu4QueryInfoLen = sizeof(UINT_32);
+		return WLAN_STATUS_ADAPTER_NOT_READY;
+	} else if (u4QueryBufferLen < sizeof(struct PARAM_TX_MCS_INFO)) {
+		DBGLOG(REQ, WARN, "Too short length %ld\n", u4QueryBufferLen);
+		return WLAN_STATUS_INVALID_LENGTH;
+	}
+
+	prMcsInfo = (struct PARAM_TX_MCS_INFO *)pvQueryBuffer;
+
+	return wlanSendSetQueryCmd(prAdapter,
+				   CMD_ID_TX_MCS_INFO,
+				   FALSE,
+				   TRUE,
+				   TRUE,
+				   nicCmdEventTxMcsInfo,
+				   nicOidCmdTimeoutCommon,
+				   sizeof(struct PARAM_TX_MCS_INFO), (PUINT_8)prMcsInfo,
+				   pvQueryBuffer, u4QueryBufferLen);
+
+}
+#endif
 
 /*----------------------------------------------------------------------------*/
 /*!
diff --git a/include/config.h b/include/config.h
index 0695220..7edcf6e 100644
--- a/include/config.h
+++ b/include/config.h
@@ -847,6 +847,15 @@
  */
 #define CFG_SUPPORT_REPLAY_DETECTION		1
 
+/*------------------------------------------------------------------------------
+ * Flags of Last Second MCS Tx/Rx Info
+ *------------------------------------------------------------------------------
+ */
+#define CFG_SUPPORT_LAST_SEC_MCS_INFO	1
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+#define MCS_INFO_SAMPLE_CNT			10
+#endif
+
 
 /*------------------------------------------------------------------------------
  * Flags of driver fw customization
diff --git a/include/mgmt/ais_fsm.h b/include/mgmt/ais_fsm.h
index 99eadcb..7968855 100644
--- a/include/mgmt/ais_fsm.h
+++ b/include/mgmt/ais_fsm.h
@@ -380,6 +380,10 @@
 
 VOID aisFsmRunEventDeauthTimeout(IN P_ADAPTER_T prAdapter, ULONG ulParamPtr);
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+VOID aisRxMcsCollectionTimeout(IN P_ADAPTER_T prAdapter, ULONG ulParamPtr);
+#endif
+
 /*----------------------------------------------------------------------------*/
 /* OID/IOCTL Handling                                                         */
 /*----------------------------------------------------------------------------*/
diff --git a/include/mgmt/cnm_mem.h b/include/mgmt/cnm_mem.h
index ed3df96..4b3a132 100644
--- a/include/mgmt/cnm_mem.h
+++ b/include/mgmt/cnm_mem.h
@@ -543,6 +543,10 @@
 	UINT_32 u4RxVector3;
 	UINT_32 u4RxVector4;
 #endif
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+	UINT_32 au4RxVect0Que[MCS_INFO_SAMPLE_CNT];
+	UINT_32 au4RxVect1Que[MCS_INFO_SAMPLE_CNT];
+#endif
 	UINT_8 ucSmDialogToken;	/* Spectrum Mngt Dialog Token */
 	UINT_8 ucSmMsmtRequestMode; /* Measurement Request Mode */
 	UINT_8 ucSmMsmtToken; /* Measurement Request Token */
diff --git a/include/nic/adapter.h b/include/nic/adapter.h
index ec504d4..a184640 100644
--- a/include/nic/adapter.h
+++ b/include/nic/adapter.h
@@ -1117,6 +1117,11 @@
 	EVENT_WLAN_INFO rEventWlanInfo;
 #endif
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+	TIMER_T rRxMcsInfoTimer;
+	BOOLEAN fgIsMcsInfoValid;
+#endif
+
 	EVENT_LINK_QUALITY rLinkQuality;
 	OS_SYSTIME rLinkQualityUpdateTime;
 	BOOLEAN fgIsLinkQualityValid;
diff --git a/include/nic_cmd_event.h b/include/nic_cmd_event.h
index 509a359..d187fb1 100644
--- a/include/nic_cmd_event.h
+++ b/include/nic_cmd_event.h
@@ -537,6 +537,10 @@
 	CMD_ID_WLAN_INFO	= 0xCD, /* 0xcd (Query) */
 	CMD_ID_MIB_INFO		= 0xCE, /* 0xce (Query) */
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+	CMD_ID_TX_MCS_INFO	= 0xCF, /* 0xcf (Query) */
+#endif
+
 	CMD_ID_SET_RDD_CH = 0xE1,
 
 #if CFG_SUPPORT_QA_TOOL
@@ -648,6 +652,10 @@
 	EVENT_ID_WLAN_INFO = 0xCD,
 	EVENT_ID_MIB_INFO = 0xCE,
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+	EVENT_ID_TX_MCS_INFO = 0xCF,
+#endif
+
 	EVENT_ID_NIC_CAPABILITY_V2 = 0xEC,		/* 0xEC (Query - CMD_ID_GET_NIC_CAPABILITY_V2) */
 /*#if (CFG_EFUSE_BUFFER_MODE_DELAY_CAL == 1)*/
 	EVENT_ID_LAYER_0_EXT_MAGIC_NUM	= 0xED,	    /* magic number for Extending MT6630 original EVENT header  */
@@ -3042,6 +3050,14 @@
 } EVENT_MIB_INFO, *P_EVENT_MIB_INFO;
 #endif
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+struct EVENT_TX_MCS_INFO {
+	UINT_16		au2TxRateCode[MCS_INFO_SAMPLE_CNT];
+	UINT_8		aucTxRatePer[MCS_INFO_SAMPLE_CNT];
+	UINT_8      aucReserved[2];
+};
+#endif
+
 /*#if (CFG_EEPROM_PAGE_ACCESS == 1)*/
 typedef struct _EVENT_ACCESS_EFUSE {
 
@@ -3245,6 +3261,10 @@
 
 VOID nicCmdEventQueryMibInfo(IN P_ADAPTER_T prAdapter, IN P_CMD_INFO_T prCmdInfo, IN PUINT_8 pucEventBuf);
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+VOID nicCmdEventTxMcsInfo(IN P_ADAPTER_T prAdapter, IN P_CMD_INFO_T prCmdInfo, IN PUINT_8 pucEventBuf);
+#endif
+
 VOID nicCmdEventQueryNicCapabilityV2(IN P_ADAPTER_T prAdapter, IN PUINT_8 pucEventBuf);
 
 WLAN_STATUS nicCmdEventQueryNicTxResource(IN P_ADAPTER_T prAdapter, IN PUINT_8 pucEventBuf);
@@ -3269,6 +3289,9 @@
 VOID nicEventStatistics(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
 VOID nicEventWlanInfo(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
 VOID nicEventMibInfo(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+VOID nicEventTxMcsInfo(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
+#endif
 VOID nicEventBeaconTimeout(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
 VOID nicEventUpdateNoaParams(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
 VOID nicEventStaAgingTimeout(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent);
diff --git a/include/wlan_oid.h b/include/wlan_oid.h
index da9444a..51a832b 100644
--- a/include/wlan_oid.h
+++ b/include/wlan_oid.h
@@ -1820,6 +1820,13 @@
 } PARAM_HW_MIB_INFO_T, *P_PARAM_HW_MIB_INFO_T;
 #endif
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+struct PARAM_TX_MCS_INFO {
+	UINT_8		ucStaIndex;
+	UINT_16		au2TxRateCode[MCS_INFO_SAMPLE_CNT];
+	UINT_8		aucTxRatePer[MCS_INFO_SAMPLE_CNT];
+};
+#endif
 
 /*--------------------------------------------------------------*/
 /*! \brief For Fixed Rate Configuration (Registry)              */
@@ -2645,6 +2652,11 @@
 WLAN_STATUS
 wlanoidQueryMibInfo(IN P_ADAPTER_T prAdapter,
 	OUT PVOID pvQueryBuffer, IN UINT_32 u4QueryBufferLen, OUT PUINT_32 pu4QueryInfoLen);
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+WLAN_STATUS
+wlanoidTxMcsInfo(IN P_ADAPTER_T prAdapter,
+	IN PVOID pvQueryBuffer, IN UINT_32 u4QueryBufferLen, OUT PUINT_32 pu4QueryInfoLen);
+#endif
 
 WLAN_STATUS
 wlanoidSetFwLog2Host(
diff --git a/mgmt/ais_fsm.c b/mgmt/ais_fsm.c
index 08ff61b..fce992f 100644
--- a/mgmt/ais_fsm.c
+++ b/mgmt/ais_fsm.c
@@ -3133,6 +3133,22 @@
 	aisDeauthXmitComplete(prAdapter, NULL, TX_RESULT_LIFE_TIMEOUT);
 }
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+VOID aisRxMcsCollectionTimeout(IN P_ADAPTER_T prAdapter, ULONG ulParamPtr)
+{
+	static UINT_8 ucSmapleCnt;
+	UINT_8 ucStaIdx = prAdapter->prAisBssInfo->prStaRecOfAP->ucIndex;
+
+	if (prAdapter->arStaRec[ucStaIdx].fgIsValid && prAdapter->arStaRec[ucStaIdx].fgIsInUse) {
+		prAdapter->arStaRec[ucStaIdx].au4RxVect0Que[ucSmapleCnt] = prAdapter->arStaRec[ucStaIdx].u4RxVector0;
+		prAdapter->arStaRec[ucStaIdx].au4RxVect1Que[ucSmapleCnt] = prAdapter->arStaRec[ucStaIdx].u4RxVector1;
+		ucSmapleCnt = (ucSmapleCnt + 1) % MCS_INFO_SAMPLE_CNT;
+	}
+
+	cnmTimerStartTimer(prAdapter, &prAdapter->rRxMcsInfoTimer, 100);
+}
+#endif
+
 #if defined(CFG_TEST_MGMT_FSM) && (CFG_TEST_MGMT_FSM != 0)
 /*----------------------------------------------------------------------------*/
 /*!
diff --git a/nic/nic_cmd_event.c b/nic/nic_cmd_event.c
index 929d15e..d2a9a93 100644
--- a/nic/nic_cmd_event.c
+++ b/nic/nic_cmd_event.c
@@ -2489,6 +2489,36 @@
 }
 #endif
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+VOID nicCmdEventTxMcsInfo(IN P_ADAPTER_T prAdapter, IN P_CMD_INFO_T prCmdInfo, IN PUINT_8 pucEventBuf)
+{
+	UINT_32 u4QueryInfoLen;
+	P_GLUE_INFO_T prGlueInfo;
+	struct EVENT_TX_MCS_INFO *prTxMcsEvent;
+	struct PARAM_TX_MCS_INFO *prTxMcsInfo;
+
+	ASSERT(prAdapter);
+	ASSERT(prCmdInfo);
+	ASSERT(pucEventBuf);
+	ASSERT(prCmdInfo->pvInformationBuffer);
+
+	if (prCmdInfo->fgIsOid) {
+		prGlueInfo = prAdapter->prGlueInfo;
+		prTxMcsEvent = (struct EVENT_TX_MCS_INFO *) pucEventBuf;
+		prTxMcsInfo = (struct PARAM_TX_MCS_INFO *) prCmdInfo->pvInformationBuffer;
+
+		u4QueryInfoLen = sizeof(struct EVENT_TX_MCS_INFO);
+
+		kalMemCopy(prTxMcsInfo->au2TxRateCode, prTxMcsEvent->au2TxRateCode,
+				sizeof(prTxMcsEvent->au2TxRateCode));
+		kalMemCopy(prTxMcsInfo->aucTxRatePer, prTxMcsEvent->aucTxRatePer,
+				sizeof(prTxMcsEvent->aucTxRatePer));
+
+		kalOidComplete(prGlueInfo, prCmdInfo->fgSetQuery, u4QueryInfoLen, WLAN_STATUS_SUCCESS);
+	}
+}
+#endif
+
 #if CFG_TCP_IP_CHKSUM_OFFLOAD
 WLAN_STATUS nicCmdEventQueryNicCsumOffload(IN P_ADAPTER_T prAdapter, IN PUINT_8 pucEventBuf)
 {
@@ -2926,6 +2956,27 @@
 
 }
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+VOID nicEventTxMcsInfo(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent)
+{
+	P_CMD_INFO_T prCmdInfo;
+
+	DBGLOG(RSN, INFO, "EVENT_ID_TX_MCS_INFO");
+	/* command response handling */
+	prCmdInfo = nicGetPendingCmdInfo(prAdapter, prEvent->ucSeqNum);
+
+	if (prCmdInfo != NULL) {
+		if (prCmdInfo->pfCmdDoneHandler)
+			prCmdInfo->pfCmdDoneHandler(prAdapter, prCmdInfo, prEvent->aucBuffer);
+		else if (prCmdInfo->fgIsOid)
+			kalOidComplete(prAdapter->prGlueInfo, prCmdInfo->fgSetQuery, 0, WLAN_STATUS_SUCCESS);
+		/* return prCmdInfo */
+		cmdBufFreeCmdInfo(prAdapter, prCmdInfo);
+	}
+
+}
+#endif
+
 VOID nicEventBeaconTimeout(IN P_ADAPTER_T prAdapter, IN P_WIFI_EVENT_T prEvent)
 {
 	DBGLOG(NIC, INFO, "EVENT_ID_BSS_BEACON_TIMEOUT\n");
diff --git a/nic/nic_rx.c b/nic/nic_rx.c
index cf4491e..1dca538 100644
--- a/nic/nic_rx.c
+++ b/nic/nic_rx.c
@@ -148,6 +148,9 @@
 	{EVENT_ID_STATISTICS,				nicEventStatistics},
 	{EVENT_ID_WLAN_INFO,				nicEventWlanInfo},
 	{EVENT_ID_MIB_INFO,					nicEventMibInfo},
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+	{EVENT_ID_TX_MCS_INFO,				nicEventTxMcsInfo},
+#endif
 	{EVENT_ID_CH_PRIVILEGE,				cnmChMngrHandleChEvent},
 	{EVENT_ID_BSS_ABSENCE_PRESENCE,		qmHandleEventBssAbsencePresence},
 	{EVENT_ID_STA_CHANGE_PS_MODE,		qmHandleEventStaChangePsMode},
diff --git a/os/linux/gl_wext_priv.c b/os/linux/gl_wext_priv.c
index 5d99d2e..f48a537 100644
--- a/os/linux/gl_wext_priv.c
+++ b/os/linux/gl_wext_priv.c
@@ -2223,6 +2223,9 @@
 #define CMD_GET_STA_STATISTICS	"GET_STA_STATISTICS"
 #define CMD_GET_WTBL_INFO	"GET_WTBL"
 #define CMD_GET_MIB_INFO	"GET_MIB"
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+#define CMD_GET_MCS_INFO	"GET_MCS_INFO"
+#endif
 #define CMD_GET_STA_INFO	"GET_STA"
 #define CMD_SET_FW_LOG		"SET_FWLOG"
 #define CMD_GET_QUE_INFO	"GET_QUE"
@@ -3992,6 +3995,185 @@
 
 }				/* priv_driver_get_test_result */
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+INT_32 priv_driver_last_sec_mcs_info(IN P_ADAPTER_T prAdapter, IN char *pcCommand, IN int i4TotalLen,
+	P_PARAM_HW_WLAN_INFO_T prHwWlanInfo, struct PARAM_TX_MCS_INFO *prTxMcsInfo)
+{
+	UINT_8 i, j, txmode, rate, stbc;
+	UINT_8 nsts;
+	INT_32 i4BytesWritten = 0;
+	UINT_32 au4RxVect0Que[MCS_INFO_SAMPLE_CNT], au4RxVect1Que[MCS_INFO_SAMPLE_CNT];
+	UINT_8 ucStaIdx = prAdapter->prAisBssInfo->prStaRecOfAP->ucIndex;
+
+	i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten, "\nTx MCS:\n");
+
+	for (i = 0; i < MCS_INFO_SAMPLE_CNT; i++) {
+		UINT_8 tmpPerSum = 0, cnt = 0;
+		UINT_16 tmpRateCode = 0xFFFF;
+
+		if (prTxMcsInfo->au2TxRateCode[i] == 0xFFFF)
+			continue;
+
+		if (tmpRateCode == 0xFFFF)
+			tmpRateCode = prTxMcsInfo->au2TxRateCode[i];
+
+		txmode = HW_TX_RATE_TO_MODE(prTxMcsInfo->au2TxRateCode[i]);
+		if (txmode >= MAX_TX_MODE)
+			txmode = MAX_TX_MODE;
+		rate = HW_TX_RATE_TO_MCS(prTxMcsInfo->au2TxRateCode[i], txmode);
+		nsts = HW_TX_RATE_TO_NSS(prTxMcsInfo->au2TxRateCode[i]) + 1;
+		stbc = HW_TX_RATE_TO_STBC(prTxMcsInfo->au2TxRateCode[i]);
+
+		for (j = 0; j < MCS_INFO_SAMPLE_CNT; j++) {
+			if (tmpRateCode == prTxMcsInfo->au2TxRateCode[j]) {
+				tmpPerSum += prTxMcsInfo->aucTxRatePer[j];
+				cnt++;
+				prTxMcsInfo->au2TxRateCode[j] = 0xFFFF;
+			}
+		}
+
+		if (txmode == TX_RATE_MODE_CCK)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	%s, ", HW_TX_RATE_CCK_STR[rate & 0x3]);
+		else if (txmode == TX_RATE_MODE_OFDM)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	%s, ", hw_rate_ofdm_str(rate));
+		else if ((txmode == TX_RATE_MODE_HTMIX) || (txmode == TX_RATE_MODE_HTGF))
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	MCS%d, ", rate);
+		else
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	NSS%d_MCS%d, ", nsts, rate);
+
+		if ((txmode == TX_RATE_MODE_CCK) || (txmode == TX_RATE_MODE_OFDM))
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s, ", HW_TX_RATE_BW[0]);
+		else
+			if (i > prHwWlanInfo->rWtblPeerCap.ucChangeBWAfterRateN)
+				i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten,
+					 i4TotalLen - i4BytesWritten, "%s, ",
+					 prHwWlanInfo->rWtblPeerCap.ucFrequencyCapability < 4 ?
+						(prHwWlanInfo->rWtblPeerCap.ucFrequencyCapability > BW_20 ?
+						HW_TX_RATE_BW[prHwWlanInfo->rWtblPeerCap.ucFrequencyCapability - 1] :
+						HW_TX_RATE_BW[prHwWlanInfo->rWtblPeerCap.ucFrequencyCapability]) :
+						HW_TX_RATE_BW[4]);
+			else
+				i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten,
+					  i4TotalLen - i4BytesWritten, "%s, ",
+					  prHwWlanInfo->rWtblPeerCap.ucFrequencyCapability < 4 ?
+						HW_TX_RATE_BW[prHwWlanInfo->rWtblPeerCap.ucFrequencyCapability] :
+						HW_TX_RATE_BW[4]);
+
+		if (txmode == TX_RATE_MODE_CCK)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s, ", rate < 4 ? "LP" : "SP");
+		else if (txmode == TX_RATE_MODE_OFDM)
+			;
+		else
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s, ", priv_driver_get_sgi_info(&prHwWlanInfo->rWtblPeerCap) == 0 ? "LGI" : "SGI");
+
+		i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+			"%s%s%s [PER: %02d%]\t", txmode < 5 ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[5],
+			stbc ? ", STBC, " : ", ",
+			((priv_driver_get_ldpc_info(&prHwWlanInfo->rWtblTxConfig) == 0) ||
+			(txmode == TX_RATE_MODE_CCK) || (txmode == TX_RATE_MODE_OFDM)) ? "BCC" : "LDPC",
+			tmpPerSum/cnt);
+
+		for (j = 0; j < cnt; j++)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten, "*");
+
+		i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten, "\n");
+	}
+
+	i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten, "\nRx MCS:\n");
+
+	kalMemCopy(au4RxVect0Que, prAdapter->arStaRec[ucStaIdx].au4RxVect0Que, sizeof(au4RxVect0Que));
+	kalMemCopy(au4RxVect1Que, prAdapter->arStaRec[ucStaIdx].au4RxVect1Que, sizeof(au4RxVect1Que));
+
+	for (i = 0; i < MCS_INFO_SAMPLE_CNT; i++) {
+		UINT_8 cnt = 0;
+		UINT_32 u4RxVector0 = 0xFFFFFFFF;
+		UINT_32 txmode, rate, frmode, sgi, nsts, ldpc, stbc, groupid, mu;
+		#define RX_MCS_INFO_MASK BITS(0, 17)
+
+		if (au4RxVect0Que[i] == 0xFFFFFFFF)
+			continue;
+
+		if (u4RxVector0 == 0xFFFFFFFF)
+			u4RxVector0 = au4RxVect0Que[i];
+
+		txmode = (au4RxVect0Que[i] & RX_VT_RX_MODE_MASK) >> RX_VT_RX_MODE_OFFSET;
+		rate = (au4RxVect0Que[i] & RX_VT_RX_RATE_MASK) >> RX_VT_RX_RATE_OFFSET;
+		frmode = (au4RxVect0Que[i] & RX_VT_FR_MODE_MASK) >> RX_VT_FR_MODE_OFFSET;
+		nsts = ((au4RxVect1Que[i] & RX_VT_NSTS_MASK) >> RX_VT_NSTS_OFFSET);
+		stbc = (au4RxVect0Que[i] & RX_VT_STBC_MASK) >> RX_VT_STBC_OFFSET;
+		sgi = au4RxVect0Que[i] & RX_VT_SHORT_GI;
+		ldpc = au4RxVect0Que[i] & RX_VT_LDPC;
+		groupid = (au4RxVect1Que[i] & RX_VT_GROUP_ID_MASK) >> RX_VT_GROUP_ID_OFFSET;
+
+		for (j = 0; j < MCS_INFO_SAMPLE_CNT; j++) {
+			if ((u4RxVector0 & RX_MCS_INFO_MASK) == (au4RxVect0Que[j] & RX_MCS_INFO_MASK)) {
+				au4RxVect0Que[j] = 0xFFFFFFFF;
+				cnt++;
+			}
+		}
+
+		if (groupid && groupid != 63) {
+			mu = 1;
+		} else {
+			mu = 0;
+			nsts += 1;
+		}
+
+		if (txmode == TX_RATE_MODE_CCK)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	%s, ", rate < 4 ? HW_TX_RATE_CCK_STR[rate] : HW_TX_RATE_CCK_STR[4]);
+		else if (txmode == TX_RATE_MODE_OFDM)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	%s, ", hw_rate_ofdm_str(rate));
+		else if ((txmode == TX_RATE_MODE_HTMIX) || (txmode == TX_RATE_MODE_HTGF))
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	MCS%d, ", rate);
+		else
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"	NSS%d_MCS%d, ", nsts, rate);
+
+		i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+			"%s, ", frmode < 4 ? HW_TX_RATE_BW[frmode] : HW_TX_RATE_BW[4]);
+
+		if (txmode == TX_RATE_MODE_CCK)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s, ", rate < 4 ? "LP" : "SP");
+		else if (txmode == TX_RATE_MODE_OFDM)
+			;
+		else
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s,", sgi == 0 ? "LGI" : "SGI");
+
+		i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+			"%s", stbc == 0 ? " " : " STBC, ");
+
+		if (mu) {
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s, %s, %s (%d)\t", txmode < 5 ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[5],
+				ldpc == 0 ? "BCC" : "LDPC", "MU", groupid);
+		} else {
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten,
+				"%s, %s\t", txmode < 5 ? HW_TX_MODE_STR[txmode] : HW_TX_MODE_STR[5],
+				ldpc == 0 ? "BCC" : "LDPC");
+		}
+
+		for (j = 0; j < cnt; j++)
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten, "*");
+
+		i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten, i4TotalLen - i4BytesWritten, "\n");
+	}
+
+	return i4BytesWritten;
+}
+#endif
+
 INT_32 priv_driver_tx_rate_info(IN char *pcCommand, IN int i4TotalLen, BOOLEAN fgDumpAll,
 	P_PARAM_HW_WLAN_INFO_T prHwWlanInfo, P_PARAM_GET_STA_STATISTICS prQueryStaStatistics)
 {
@@ -8568,6 +8750,102 @@
 	return i4BytesWritten;
 }
 
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+static int priv_driver_get_mcs_info(IN struct net_device *prNetDev, IN char *pcCommand, IN int i4TotalLen)
+{
+	P_GLUE_INFO_T prGlueInfo = NULL;
+	WLAN_STATUS rStatus = WLAN_STATUS_SUCCESS;
+	UINT_32 u4BufLen = 0;
+	INT_32 i4BytesWritten = 0, i4Argc = 0;
+	PCHAR apcArgv[WLAN_CFG_ARGV_MAX] = { 0 };
+	P_PARAM_HW_WLAN_INFO_T prHwWlanInfo = NULL;
+	struct PARAM_TX_MCS_INFO *prTxMcsInfo = NULL;
+
+	ASSERT(prNetDev);
+	if (GLUE_CHK_PR2(prNetDev, pcCommand) == FALSE)
+		return -1;
+
+	prGlueInfo = *((P_GLUE_INFO_T *) netdev_priv(prNetDev));
+	DBGLOG(REQ, LOUD, "command is %s\n", pcCommand);
+	wlanCfgParseArgument(pcCommand, &i4Argc, apcArgv);
+	DBGLOG(REQ, LOUD, "argc is %i\n", i4Argc);
+
+	if (prGlueInfo->prAdapter->rRxMcsInfoTimer.pfMgmtTimeOutFunc == NULL) {
+		cnmTimerInitTimer(prGlueInfo->prAdapter,
+			&prGlueInfo->prAdapter->rRxMcsInfoTimer,
+			(PFN_MGMT_TIMEOUT_FUNC) aisRxMcsCollectionTimeout, (ULONG) NULL);
+	}
+
+	if (i4Argc >= 2) {
+		if (strnicmp(apcArgv[1], "START", strlen("START")) == 0) {
+			cnmTimerStartTimer(prGlueInfo->prAdapter, &prGlueInfo->prAdapter->rRxMcsInfoTimer, 100);
+			prGlueInfo->prAdapter->fgIsMcsInfoValid = TRUE;
+
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten,
+					i4TotalLen - i4BytesWritten,
+					"\nStart the MCS Info Function\n");
+			return i4BytesWritten;
+		} else if (strnicmp(apcArgv[1], "STOP", strlen("STOP")) == 0) {
+			cnmTimerStopTimer(prGlueInfo->prAdapter, &prGlueInfo->prAdapter->rRxMcsInfoTimer);
+			prGlueInfo->prAdapter->fgIsMcsInfoValid = FALSE;
+
+			i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten,
+				i4TotalLen - i4BytesWritten,
+				"\nStop the MCS Info Function\n");
+			return i4BytesWritten;
+		}
+	}
+
+	if (prGlueInfo->prAdapter->fgIsMcsInfoValid != TRUE) {
+		i4BytesWritten += kalScnprintf(pcCommand + i4BytesWritten,
+			i4TotalLen - i4BytesWritten,
+			"\nUse GET_MCS_INFO [START/STOP] to control the MCS Info Function\n");
+		return i4BytesWritten;
+	}
+
+	if (prGlueInfo->prAdapter->prAisBssInfo->eConnectionState != PARAM_MEDIA_STATE_CONNECTED)
+		return -1;
+
+	prHwWlanInfo = (P_PARAM_HW_WLAN_INFO_T)kalMemAlloc(sizeof(PARAM_HW_WLAN_INFO_T), VIR_MEM_TYPE);
+	if (!prHwWlanInfo)
+		return -1;
+
+	rStatus = kalIoctl(prGlueInfo,
+				   wlanoidQueryWlanInfo,
+				   prHwWlanInfo, sizeof(PARAM_HW_WLAN_INFO_T), TRUE, TRUE, TRUE, &u4BufLen);
+
+	DBGLOG(REQ, INFO, "rStatus %u u4BufLen = %d\n", rStatus, u4BufLen);
+	if (rStatus != WLAN_STATUS_SUCCESS)
+		goto out;
+
+	prTxMcsInfo = (struct PARAM_TX_MCS_INFO *)kalMemAlloc(sizeof(struct PARAM_TX_MCS_INFO), VIR_MEM_TYPE);
+	if (!prTxMcsInfo)
+		goto out;
+
+	prTxMcsInfo->ucStaIndex = prGlueInfo->prAdapter->prAisBssInfo->prStaRecOfAP->ucIndex;
+	rStatus = kalIoctl(prGlueInfo,
+				   wlanoidTxMcsInfo,
+				   prTxMcsInfo, sizeof(struct PARAM_TX_MCS_INFO), TRUE, TRUE, TRUE, &u4BufLen);
+
+	DBGLOG(REQ, INFO, "rStatus %u u4BufLen = %d\n", rStatus, u4BufLen);
+	if (rStatus != WLAN_STATUS_SUCCESS)
+		goto out;
+
+	i4BytesWritten = priv_driver_last_sec_mcs_info(prGlueInfo->prAdapter,
+							pcCommand, i4TotalLen, prHwWlanInfo, prTxMcsInfo);
+
+	DBGLOG(REQ, INFO, "%s: command result is %s\n", __func__, pcCommand);
+
+out:
+	if (prHwWlanInfo)
+		kalMemFree(prHwWlanInfo, VIR_MEM_TYPE, sizeof(PARAM_HW_WLAN_INFO_T));
+	if (prTxMcsInfo)
+		kalMemFree(prTxMcsInfo, VIR_MEM_TYPE, sizeof(struct PARAM_TX_MCS_INFO));
+
+	return i4BytesWritten;
+}
+#endif
+
 static int priv_driver_get_deep_sleep_cnt(IN struct net_device *prNetDev, IN char *pcCommand, IN int i4TotalLen)
 {
 	P_GLUE_INFO_T prGlueInfo = NULL;
@@ -10304,6 +10582,10 @@
 			i4BytesWritten = priv_driver_get_wtbl_info(prNetDev, pcCommand, i4TotalLen);
 		else if (strnicmp(pcCommand, CMD_GET_MIB_INFO, strlen(CMD_GET_MIB_INFO)) == 0)
 			i4BytesWritten = priv_driver_get_mib_info(prNetDev, pcCommand, i4TotalLen);
+#if CFG_SUPPORT_LAST_SEC_MCS_INFO
+		else if (strnicmp(pcCommand, CMD_GET_MCS_INFO, strlen(CMD_GET_MCS_INFO)) == 0)
+			i4BytesWritten = priv_driver_get_mcs_info(prNetDev, pcCommand, i4TotalLen);
+#endif
 		else if (strnicmp(pcCommand, CMD_SET_FW_LOG, strlen(CMD_SET_FW_LOG)) == 0)
 			i4BytesWritten = priv_driver_set_fw_log(prNetDev, pcCommand, i4TotalLen);
 #endif