Browse Source

qcacld-3.0: Add support to send RX stats to framework

As a part of the cfg80211_get_station command, the driver sends TX stats
(tx rate, MCS index, NSS and flags). Currently there is no support to
send the similar RX stats.

Add support to send RX stats to the framework.

Change-Id: Ic66596d118ad1395706db7638da1b4fdef7dc2d5
CRs-Fixed: 2303306
Sourav Mohapatra 6 years ago
parent
commit
49ce4cec0f
3 changed files with 510 additions and 383 deletions
  1. 481 375
      core/hdd/src/wlan_hdd_stats.c
  2. 8 4
      core/sme/inc/csr_api.h
  3. 21 4
      core/wma/src/wma_utils.c

+ 481 - 375
core/hdd/src/wlan_hdd_stats.c

@@ -44,6 +44,7 @@
 #define HDD_INFO_TX_RETRIES             STATION_INFO_TX_RETRIES
 #define HDD_INFO_TX_FAILED              STATION_INFO_TX_FAILED
 #define HDD_INFO_TX_BITRATE             STATION_INFO_TX_BITRATE
+#define HDD_INFO_RX_BITRATE             STATION_INFO_RX_BITRATE
 #define HDD_INFO_TX_BYTES               STATION_INFO_TX_BYTES
 #define HDD_INFO_CHAIN_SIGNAL_AVG       STATION_INFO_CHAIN_SIGNAL_AVG
 #define HDD_INFO_RX_BYTES               STATION_INFO_RX_BYTES
@@ -59,6 +60,7 @@
 #define HDD_INFO_TX_RETRIES             BIT(NL80211_STA_INFO_TX_RETRIES)
 #define HDD_INFO_TX_FAILED              BIT(NL80211_STA_INFO_TX_FAILED)
 #define HDD_INFO_TX_BITRATE             BIT(NL80211_STA_INFO_TX_BITRATE)
+#define HDD_INFO_RX_BITRATE             BIT(NL80211_STA_INFO_RX_BITRATE)
 #define HDD_INFO_TX_BYTES               BIT(NL80211_STA_INFO_TX_BYTES)
 #define HDD_INFO_CHAIN_SIGNAL_AVG       BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG)
 #define HDD_INFO_RX_BYTES               BIT(NL80211_STA_INFO_RX_BYTES)
@@ -3930,6 +3932,410 @@ int wlan_hdd_get_station_remote(struct wiphy *wiphy,
 	return status;
 }
 
+/**
+ * hdd_report_max_rate() - Fill the max rate stats in the station info structure
+ * to be sent to the userspace.
+ *
+ * @mac_handle: The mac handle
+ * @config: The HDD config structure
+ * @sinfo: The station_info struct to be filled
+ * @tx_rate_flags: The TX rate flags computed from tx rate
+ * @tx_mcs_index; The TX mcs index computed from tx rate
+ * @my_tx_rate: The tx_rate from fw stats
+ * @tx_nss: The TX NSS from fw stats
+ *
+ * Return: 0 for success
+ */
+static int hdd_report_max_rate(mac_handle_t mac_handle,
+			       struct hdd_config *config,
+			       struct station_info *sinfo,
+			       uint8_t tx_rate_flags,
+			       uint8_t tx_mcs_index,
+			       uint16_t my_tx_rate, uint8_t tx_nss)
+{
+	uint8_t i, j, rssidx;
+	uint16_t max_rate = 0;
+	uint32_t vht_mcs_map;
+	uint16_t current_rate = 0;
+	uint8_t or_leng = CSR_DOT11_SUPPORTED_RATES_MAX;
+	uint8_t operational_rates[CSR_DOT11_SUPPORTED_RATES_MAX];
+	uint8_t extended_rates[CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX];
+	uint32_t er_leng = CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX;
+	uint8_t mcs_rates[SIZE_OF_BASIC_MCS_SET];
+	uint32_t mcs_leng = SIZE_OF_BASIC_MCS_SET;
+	struct index_vht_data_rate_type *supported_vht_mcs_rate;
+	struct index_data_rate_type *supported_mcs_rate;
+	enum data_rate_11ac_max_mcs vht_max_mcs;
+	uint8_t max_speed_mcs = 0;
+	uint8_t max_mcs_idx = 0;
+	uint8_t rate_flag = 1;
+	int mode = 0, max_ht_idx;
+
+	/* we do not want to necessarily report the current speed */
+	if (eHDD_LINK_SPEED_REPORT_MAX == config->reportMaxLinkSpeed) {
+		/* report the max possible speed */
+		rssidx = 0;
+	} else if (eHDD_LINK_SPEED_REPORT_MAX_SCALED ==
+		   config->reportMaxLinkSpeed) {
+		/* report the max possible speed with RSSI scaling */
+		if (sinfo->signal >= config->linkSpeedRssiHigh) {
+			/* report the max possible speed */
+			rssidx = 0;
+		} else if (sinfo->signal >= config->linkSpeedRssiMid) {
+			/* report middle speed */
+			rssidx = 1;
+		} else if (sinfo->signal >= config->linkSpeedRssiLow) {
+			/* report middle speed */
+			rssidx = 2;
+		} else {
+			/* report actual speed */
+			rssidx = 3;
+		}
+	} else {
+		/* unknown, treat as eHDD_LINK_SPEED_REPORT_MAX */
+		hdd_err("Invalid value for reportMaxLinkSpeed: %u",
+			config->reportMaxLinkSpeed);
+		rssidx = 0;
+	}
+
+	max_rate = 0;
+
+	/* Get Basic Rate Set */
+	if (0 != sme_cfg_get_str(mac_handle,
+				 WNI_CFG_OPERATIONAL_RATE_SET,
+				 operational_rates,
+				 (uint32_t *)&or_leng)) {
+		hdd_err("cfg get returned failure");
+		/*To keep GUI happy */
+		return 0;
+	}
+
+	for (i = 0; i < or_leng; i++) {
+		for (j = 0;
+			 j < ARRAY_SIZE(supported_data_rate); j++) {
+			/* Validate Rate Set */
+			if (supported_data_rate[j].beacon_rate_index ==
+				(operational_rates[i] & 0x7F)) {
+				current_rate =
+					supported_data_rate[j].
+					supported_rate[rssidx];
+				break;
+			}
+		}
+		/* Update MAX rate */
+		max_rate = (current_rate > max_rate) ? current_rate : max_rate;
+	}
+
+	/* Get Extended Rate Set */
+	if (0 != sme_cfg_get_str(mac_handle,
+				 WNI_CFG_EXTENDED_OPERATIONAL_RATE_SET,
+				 extended_rates, &er_leng)) {
+		hdd_err("cfg get returned failure");
+		/*To keep GUI happy */
+		return 0;
+	}
+
+	for (i = 0; i < er_leng; i++) {
+		for (j = 0; j < ARRAY_SIZE(supported_data_rate); j++) {
+			if (supported_data_rate[j].beacon_rate_index ==
+			    (extended_rates[i] & 0x7F)) {
+				current_rate = supported_data_rate[j].
+					       supported_rate[rssidx];
+				break;
+			}
+		}
+		/* Update MAX rate */
+		max_rate = (current_rate > max_rate) ? current_rate : max_rate;
+	}
+	/* Get MCS Rate Set --
+	 * Only if we are connected in non legacy mode and not reporting
+	 * actual speed
+	 */
+	if ((3 != rssidx) && !(tx_rate_flags & TX_RATE_LEGACY)) {
+		if (0 != sme_cfg_get_str(mac_handle,
+					 WNI_CFG_CURRENT_MCS_SET, mcs_rates,
+					 &mcs_leng)) {
+			hdd_err("cfg get returned failure");
+			/*To keep GUI happy */
+			return 0;
+		}
+		rate_flag = 0;
+		supported_vht_mcs_rate = (struct index_vht_data_rate_type *)
+					  ((tx_nss == 1) ?
+					  &supported_vht_mcs_rate_nss1 :
+					  &supported_vht_mcs_rate_nss2);
+
+		if (tx_rate_flags & TX_RATE_VHT80)
+			mode = 2;
+		else if ((tx_rate_flags & TX_RATE_VHT40) ||
+			 (tx_rate_flags & TX_RATE_HT40))
+			mode = 1;
+		else
+			mode = 0;
+
+		/* VHT80 rate has separate rate table */
+		if (tx_rate_flags & (TX_RATE_VHT20 | TX_RATE_VHT40 |
+		    TX_RATE_VHT80)) {
+			sme_cfg_get_int(mac_handle,
+					WNI_CFG_VHT_TX_MCS_MAP,
+					&vht_mcs_map);
+			vht_max_mcs = (enum data_rate_11ac_max_mcs)
+				(vht_mcs_map & DATA_RATE_11AC_MCS_MASK);
+			if (tx_rate_flags & TX_RATE_SGI)
+				rate_flag |= 1;
+
+			if (DATA_RATE_11AC_MAX_MCS_7 == vht_max_mcs) {
+				max_mcs_idx = 7;
+			} else if (DATA_RATE_11AC_MAX_MCS_8 == vht_max_mcs) {
+				max_mcs_idx = 8;
+			} else if (DATA_RATE_11AC_MAX_MCS_9 == vht_max_mcs) {
+				max_mcs_idx = 9;
+			}
+
+			if (rssidx != 0) {
+				for (i = 0; i <= max_mcs_idx; i++) {
+					if (sinfo->signal <=
+						rssi_mcs_tbl[mode][i]) {
+						max_mcs_idx = i;
+						break;
+					}
+				}
+			}
+
+			if (tx_rate_flags & TX_RATE_VHT80) {
+				current_rate =
+				  supported_vht_mcs_rate[tx_mcs_index].
+				  supported_VHT80_rate[rate_flag];
+				max_rate =
+				  supported_vht_mcs_rate[max_mcs_idx].
+					supported_VHT80_rate[rate_flag];
+			} else if (tx_rate_flags & TX_RATE_VHT40) {
+				current_rate =
+				  supported_vht_mcs_rate[tx_mcs_index].
+				  supported_VHT40_rate[rate_flag];
+				max_rate =
+				  supported_vht_mcs_rate[max_mcs_idx].
+					supported_VHT40_rate[rate_flag];
+			} else if (tx_rate_flags & TX_RATE_VHT20) {
+				current_rate =
+				  supported_vht_mcs_rate[tx_mcs_index].
+				  supported_VHT20_rate[rate_flag];
+				max_rate =
+				  supported_vht_mcs_rate[max_mcs_idx].
+				  supported_VHT20_rate[rate_flag];
+			}
+
+			max_speed_mcs = 1;
+			if (current_rate > max_rate)
+				max_rate = current_rate;
+
+		} else {
+			if (tx_rate_flags & TX_RATE_HT40)
+				rate_flag |= 1;
+			if (tx_rate_flags & TX_RATE_SGI)
+				rate_flag |= 2;
+
+			supported_mcs_rate =
+				(struct index_data_rate_type *)
+				((tx_nss ==
+				  1) ? &supported_mcs_rate_nss1 :
+				 &supported_mcs_rate_nss2);
+
+			max_ht_idx = MAX_HT_MCS_IDX;
+			if (rssidx != 0) {
+				for (i = 0; i < MAX_HT_MCS_IDX; i++) {
+					if (sinfo->signal <=
+						rssi_mcs_tbl[mode][i]) {
+						max_ht_idx = i + 1;
+						break;
+					}
+				}
+			}
+
+			for (i = 0; i < mcs_leng; i++) {
+				for (j = 0; j < max_ht_idx; j++) {
+					if (supported_mcs_rate[j].
+						beacon_rate_index ==
+						mcs_rates[i]) {
+						current_rate =
+						  supported_mcs_rate[j].
+						  supported_rate
+						  [rate_flag];
+						max_mcs_idx =
+						  supported_mcs_rate[j].
+						  beacon_rate_index;
+						break;
+					}
+				}
+
+				if ((j < MAX_HT_MCS_IDX) &&
+				    (current_rate > max_rate)) {
+					max_rate = current_rate;
+				}
+				max_speed_mcs = 1;
+			}
+			if (tx_nss == 2)
+				max_mcs_idx += MAX_HT_MCS_IDX;
+		}
+	}
+
+	else if (!(tx_rate_flags & TX_RATE_LEGACY)) {
+		max_rate = my_tx_rate;
+		max_speed_mcs = 1;
+		max_mcs_idx = tx_mcs_index;
+	}
+	/* report a value at least as big as current rate */
+	if ((max_rate < my_tx_rate) || (0 == max_rate)) {
+		max_rate = my_tx_rate;
+		if (tx_rate_flags & TX_RATE_LEGACY) {
+			max_speed_mcs = 0;
+		} else {
+			max_speed_mcs = 1;
+			max_mcs_idx = tx_mcs_index;
+			}
+	}
+
+	if (tx_rate_flags & TX_RATE_LEGACY) {
+		sinfo->txrate.legacy = max_rate;
+
+		hdd_info("Reporting legacy rate %d",
+			 sinfo->txrate.legacy);
+	} else {
+		sinfo->txrate.mcs = max_mcs_idx;
+		sinfo->txrate.nss = tx_nss;
+		if (tx_rate_flags & TX_RATE_VHT80)
+			hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_80);
+		else if (tx_rate_flags & TX_RATE_VHT40)
+			hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_40);
+		else if (tx_rate_flags & TX_RATE_VHT20)
+			hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_20);
+
+		if (tx_rate_flags &
+		    (TX_RATE_HT20 | TX_RATE_HT40)) {
+			sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
+			if (tx_rate_flags & TX_RATE_HT40)
+				hdd_set_rate_bw(&sinfo->txrate,
+						HDD_RATE_BW_40);
+			else if (tx_rate_flags & TX_RATE_HT20)
+				hdd_set_rate_bw(&sinfo->txrate,
+						HDD_RATE_BW_20);
+		} else {
+			sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
+		}
+
+		if (tx_rate_flags & TX_RATE_SGI) {
+			if (!
+			    (sinfo->txrate.
+			     flags & RATE_INFO_FLAGS_VHT_MCS))
+				sinfo->txrate.flags |=
+					RATE_INFO_FLAGS_MCS;
+			sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
+		}
+		linkspeed_dbg("Reporting MCS rate %d flags %x\n",
+			      sinfo->txrate.mcs, sinfo->txrate.flags);
+	}
+	return 1;
+}
+
+/**
+ * hdd_report_actual_rate() - Fill the actual rate stats.
+ *
+ * @rate_flags: The rate flags computed from rate
+ * @my_rate: The rate from fw stats
+ * @rate: The station_info struct member strust rate_info to be filled
+ * @mcs_index; The mcs index computed from rate
+ * @nss: The NSS from fw stats
+ *
+ * Return: None
+ */
+static void hdd_report_actual_rate(uint8_t rate_flags, uint16_t my_rate,
+				   struct rate_info *rate, uint8_t mcs_index,
+				   uint8_t nss)
+{
+	/* report current rate instead of max rate */
+
+	if (rate_flags & TX_RATE_LEGACY) {
+		/* provide to the UI in units of 100kbps */
+		rate->legacy = my_rate;
+		linkspeed_dbg("Reporting actual legacy rate %d",
+			      rate->legacy);
+	} else {
+		/* must be MCS */
+		rate->mcs = mcs_index;
+		rate->nss = nss;
+
+		if (rate_flags & TX_RATE_VHT80)
+			hdd_set_rate_bw(rate, HDD_RATE_BW_80);
+		else if (rate_flags & TX_RATE_VHT40)
+			hdd_set_rate_bw(rate, HDD_RATE_BW_40);
+
+		if (rate_flags &
+			(TX_RATE_HT20 | TX_RATE_HT40)) {
+			rate->flags |= RATE_INFO_FLAGS_MCS;
+			if (rate_flags & TX_RATE_HT40)
+				hdd_set_rate_bw(rate, HDD_RATE_BW_40);
+		} else {
+			rate->flags |= RATE_INFO_FLAGS_VHT_MCS;
+		}
+
+		if (rate_flags & TX_RATE_SGI) {
+			rate->flags |= RATE_INFO_FLAGS_MCS;
+			rate->flags |= RATE_INFO_FLAGS_SHORT_GI;
+		}
+
+		linkspeed_dbg("Reporting actual MCS rate %d flags %x\n",
+			      rate->mcs, rate->flags);
+	}
+}
+
+/**
+ * hdd_wlan_fill_per_chain_rssi_stats() - Fill per chain rssi stats
+ *
+ * @sinfo: The station_info structure to be filled.
+ * @adapter: The HDD adapter structure
+ *
+ * Return: None
+ */
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
+static inline
+void hdd_wlan_fill_per_chain_rssi_stats(struct station_info *sinfo,
+					struct hdd_adapter *adapter)
+{
+	bool rssi_stats_valid = false;
+	uint8_t i;
+
+	sinfo->signal_avg = WLAN_HDD_TGT_NOISE_FLOOR_DBM;
+	for (i = 0; i < NUM_CHAINS_MAX; i++) {
+		sinfo->chain_signal_avg[i] =
+			   adapter->hdd_stats.per_chain_rssi_stats.rssi[i];
+		sinfo->chains |= 1 << i;
+		if (sinfo->chain_signal_avg[i] > sinfo->signal_avg &&
+		    sinfo->chain_signal_avg[i] != 0)
+			sinfo->signal_avg = sinfo->chain_signal_avg[i];
+
+		hdd_debug("RSSI for chain %d, vdev_id %d is %d",
+			  i, adapter->session_id, sinfo->chain_signal_avg[i]);
+
+		if (!rssi_stats_valid && sinfo->chain_signal_avg[i])
+			rssi_stats_valid = true;
+	}
+
+	if (rssi_stats_valid) {
+		sinfo->filled |= HDD_INFO_CHAIN_SIGNAL_AVG;
+		sinfo->filled |= HDD_INFO_SIGNAL_AVG;
+	}
+}
+
+#else
+
+static inline
+void hdd_wlan_fill_per_chain_rssi_stats(struct station_info *sinfo,
+					struct hdd_adapter *adapter)
+{
+}
+
+#endif
+
 /**
  * wlan_hdd_get_sta_stats() - get aggregate STA stats
  * @wiphy: wireless phy
@@ -3948,36 +4354,15 @@ static int wlan_hdd_get_sta_stats(struct wiphy *wiphy,
 				  struct station_info *sinfo)
 {
 	struct hdd_station_ctx *sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
-	uint8_t rate_flags;
-	uint8_t mcs_index;
-
+	uint8_t rate_flags, tx_rate_flags, rx_rate_flags;
+	uint8_t tx_mcs_index, rx_mcs_index;
 	struct hdd_context *hdd_ctx = (struct hdd_context *) wiphy_priv(wiphy);
 	struct hdd_config *pCfg = hdd_ctx->config;
 	mac_handle_t mac_handle;
-	uint8_t OperationalRates[CSR_DOT11_SUPPORTED_RATES_MAX];
-	uint32_t ORLeng = CSR_DOT11_SUPPORTED_RATES_MAX;
-	uint8_t ExtendedRates[CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX];
-	uint32_t ERLeng = CSR_DOT11_EXTENDED_SUPPORTED_RATES_MAX;
-	uint8_t MCSRates[SIZE_OF_BASIC_MCS_SET];
-	uint32_t MCSLeng = SIZE_OF_BASIC_MCS_SET;
 	uint16_t maxRate = 0;
 	int8_t snr = 0;
-	uint16_t myRate;
-	uint16_t currentRate = 0;
-	uint8_t maxSpeedMCS = 0;
-	uint8_t maxMCSIdx = 0;
-	uint8_t rateFlag = 1;
-	uint8_t i, j, rssidx;
-	uint8_t nss = 1;
-	int mode = 0, maxHtIdx;
-	struct index_vht_data_rate_type *supported_vht_mcs_rate;
-	struct index_data_rate_type *supported_mcs_rate;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
-	bool rssi_stats_valid = false;
-#endif
-
-	uint32_t vht_mcs_map;
-	enum data_rate_11ac_max_mcs vht_max_mcs;
+	uint16_t my_tx_rate, my_rx_rate;
+	uint8_t tx_nss = 1, rx_nss = 1;
 	int32_t rcpi_value;
 
 	if (eConnectionState_Associated != sta_ctx->conn_info.connState) {
@@ -4033,352 +4418,88 @@ static int wlan_hdd_get_sta_stats(struct wiphy *wiphy,
 	 */
 	hdd_lpass_notify_connect(adapter);
 
-	rate_flags = adapter->hdd_stats.class_a_stat.tx_rate_flags;
-	mcs_index = adapter->hdd_stats.class_a_stat.mcs_index;
+	rate_flags = adapter->hdd_stats.class_a_stat.tx_rx_rate_flags;
+	tx_rate_flags = rx_rate_flags = rate_flags;
+
+	tx_mcs_index = adapter->hdd_stats.class_a_stat.tx_mcs_index;
+	rx_mcs_index = adapter->hdd_stats.class_a_stat.rx_mcs_index;
 	mac_handle = hdd_ctx->mac_handle;
 
 	/* convert to the UI units of 100kbps */
-	myRate = adapter->hdd_stats.class_a_stat.tx_rate * 5;
+	my_tx_rate = adapter->hdd_stats.class_a_stat.tx_rate * 5;
+	my_rx_rate = adapter->hdd_stats.class_a_stat.rx_rate * 5;
+
 	if (!(rate_flags & TX_RATE_LEGACY)) {
-		nss = adapter->hdd_stats.class_a_stat.nss;
-		if ((nss > 1) &&
+		tx_nss = adapter->hdd_stats.class_a_stat.tx_nss;
+		rx_nss = adapter->hdd_stats.class_a_stat.rx_nss;
+
+		if ((tx_nss > 1) &&
 		    policy_mgr_is_current_hwmode_dbs(hdd_ctx->hdd_psoc) &&
 		    !policy_mgr_is_hw_dbs_2x2_capable(hdd_ctx->hdd_psoc)) {
-			hdd_debug("Hw mode is DBS, Reduce nss(%d) to 1", nss);
-			nss--;
+			hdd_debug("Hw mode is DBS, Reduce nss(%d) to 1",
+				  tx_nss);
+			tx_nss--;
+		}
+
+		if ((rx_nss > 1) &&
+		    policy_mgr_is_current_hwmode_dbs(hdd_ctx->hdd_psoc) &&
+		    !policy_mgr_is_hw_dbs_2x2_capable(hdd_ctx->hdd_psoc)) {
+			hdd_debug("Hw mode is DBS, Reduce nss(%d) to 1",
+				  rx_nss);
+			rx_nss--;
 		}
 
 		if (eHDD_LINK_SPEED_REPORT_ACTUAL == pCfg->reportMaxLinkSpeed) {
 			/* Get current rate flags if report actual */
 			/* WMA fails to find mcs_index for legacy tx rates */
-			if (mcs_index == INVALID_MCS_IDX && myRate)
-				rate_flags = TX_RATE_LEGACY;
+			if (tx_mcs_index == INVALID_MCS_IDX && my_tx_rate)
+				tx_rate_flags = TX_RATE_LEGACY;
 			else
-				rate_flags =
-				 adapter->hdd_stats.class_a_stat.mcs_rate_flags;
+				tx_rate_flags =
+			      adapter->hdd_stats.class_a_stat.tx_mcs_rate_flags;
+
+			if (rx_mcs_index == INVALID_MCS_IDX && my_rx_rate)
+				rx_rate_flags = TX_RATE_LEGACY;
+			else
+				rx_rate_flags =
+			      adapter->hdd_stats.class_a_stat.rx_mcs_rate_flags;
 		}
 
-		if (mcs_index == INVALID_MCS_IDX)
-			mcs_index = 0;
+		if (tx_mcs_index == INVALID_MCS_IDX)
+			tx_mcs_index = 0;
+		if (rx_mcs_index == INVALID_MCS_IDX)
+			rx_mcs_index = 0;
 	}
 
-	hdd_debug("RSSI %d, RLMS %u, rate %d, rssi high %d, rssi mid %d, rssi low %d, rate_flags 0x%x, MCS %d",
-		 sinfo->signal, pCfg->reportMaxLinkSpeed, myRate,
-		 (int)pCfg->linkSpeedRssiHigh, (int)pCfg->linkSpeedRssiMid,
-		 (int)pCfg->linkSpeedRssiLow, (int)rate_flags, (int)mcs_index);
+	hdd_debug("RSSI %d, RLMS %u, rssi high %d, rssi mid %d, rssi low %d",
+		  sinfo->signal, pCfg->reportMaxLinkSpeed,
+		  (int)pCfg->linkSpeedRssiHigh, (int)pCfg->linkSpeedRssiMid,
+		  (int)pCfg->linkSpeedRssiLow);
+	hdd_debug("Rate info: TX: %d, RX: %d", my_tx_rate, my_rx_rate);
+	hdd_debug("Rate flags: TX: 0x%x, RX: 0x%x", (int)tx_rate_flags,
+		  (int)rx_rate_flags);
+	hdd_debug("MCS Index: TX: %d, RX: %d", (int)tx_mcs_index,
+		  (int)rx_mcs_index);
 
 	/* assume basic BW. anything else will override this later */
 	hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_20);
 
 	if (eHDD_LINK_SPEED_REPORT_ACTUAL != pCfg->reportMaxLinkSpeed) {
-		/* we do not want to necessarily report the current speed */
-		if (eHDD_LINK_SPEED_REPORT_MAX == pCfg->reportMaxLinkSpeed) {
-			/* report the max possible speed */
-			rssidx = 0;
-		} else if (eHDD_LINK_SPEED_REPORT_MAX_SCALED ==
-			   pCfg->reportMaxLinkSpeed) {
-			/* report the max possible speed with RSSI scaling */
-			if (sinfo->signal >= pCfg->linkSpeedRssiHigh) {
-				/* report the max possible speed */
-				rssidx = 0;
-			} else if (sinfo->signal >= pCfg->linkSpeedRssiMid) {
-				/* report middle speed */
-				rssidx = 1;
-			} else if (sinfo->signal >= pCfg->linkSpeedRssiLow) {
-				/* report middle speed */
-				rssidx = 2;
-			} else {
-				/* report actual speed */
-				rssidx = 3;
-			}
-		} else {
-			/* unknown, treat as eHDD_LINK_SPEED_REPORT_MAX */
-			hdd_err("Invalid value for reportMaxLinkSpeed: %u",
-			       pCfg->reportMaxLinkSpeed);
-			rssidx = 0;
-		}
-
-		maxRate = 0;
-
-		/* Get Basic Rate Set */
-		if (0 !=
-		    sme_cfg_get_str(mac_handle,
-				    WNI_CFG_OPERATIONAL_RATE_SET,
-				    OperationalRates,
-				    &ORLeng)) {
-			hdd_err("cfg get returned failure");
-			/*To keep GUI happy */
+		if (!hdd_report_max_rate(mac_handle, pCfg, sinfo,
+					 tx_rate_flags, tx_mcs_index,
+					 my_tx_rate, tx_nss)) {
+			/* Keep GUI happy */
 			return 0;
 		}
-
-		for (i = 0; i < ORLeng; i++) {
-			for (j = 0;
-			     j < ARRAY_SIZE(supported_data_rate); j++) {
-				/* Validate Rate Set */
-				if (supported_data_rate[j].beacon_rate_index ==
-				    (OperationalRates[i] & 0x7F)) {
-					currentRate =
-						supported_data_rate[j].
-						supported_rate[rssidx];
-					break;
-				}
-			}
-			/* Update MAX rate */
-			maxRate =
-				(currentRate > maxRate) ? currentRate : maxRate;
-		}
-
-		/* Get Extended Rate Set */
-		if (0 !=
-		    sme_cfg_get_str(mac_handle,
-				    WNI_CFG_EXTENDED_OPERATIONAL_RATE_SET,
-				    ExtendedRates, &ERLeng)) {
-			hdd_err("cfg get returned failure");
-			/*To keep GUI happy */
-			return 0;
-		}
-
-		for (i = 0; i < ERLeng; i++) {
-			for (j = 0;
-			     j < ARRAY_SIZE(supported_data_rate); j++) {
-				if (supported_data_rate[j].beacon_rate_index ==
-				    (ExtendedRates[i] & 0x7F)) {
-					currentRate =
-						supported_data_rate[j].
-						supported_rate[rssidx];
-					break;
-				}
-			}
-			/* Update MAX rate */
-			maxRate =
-				(currentRate > maxRate) ? currentRate : maxRate;
-		}
-		/*
-		 * Get MCS Rate Set --
-		 * Only if we are connected in non legacy mode and not
-		 * reporting actual speed
-		 */
-		if ((3 != rssidx) && !(rate_flags & TX_RATE_LEGACY)) {
-			if (0 !=
-			    sme_cfg_get_str(mac_handle,
-					    WNI_CFG_CURRENT_MCS_SET, MCSRates,
-					    &MCSLeng)) {
-				hdd_err("cfg get returned failure");
-				/*To keep GUI happy */
-				return 0;
-			}
-			rateFlag = 0;
-			supported_vht_mcs_rate =
-				(struct index_vht_data_rate_type *)
-				((nss ==
-				  1) ? &supported_vht_mcs_rate_nss1 :
-				 &supported_vht_mcs_rate_nss2);
-
-			if (rate_flags & TX_RATE_VHT80)
-				mode = 2;
-			else if ((rate_flags & TX_RATE_VHT40) ||
-				 (rate_flags & TX_RATE_HT40))
-				mode = 1;
-			else
-				mode = 0;
-
-			/* VHT80 rate has separate rate table */
-			if (rate_flags &
-			    (TX_RATE_VHT20 | TX_RATE_VHT40 |
-			     TX_RATE_VHT80)) {
-				sme_cfg_get_int(mac_handle,
-						WNI_CFG_VHT_TX_MCS_MAP,
-						&vht_mcs_map);
-				vht_max_mcs = (enum data_rate_11ac_max_mcs)
-					(vht_mcs_map & DATA_RATE_11AC_MCS_MASK);
-				if (rate_flags & TX_RATE_SGI)
-					rateFlag |= 1;
-
-				if (DATA_RATE_11AC_MAX_MCS_7 == vht_max_mcs)
-					maxMCSIdx = 7;
-				else if (DATA_RATE_11AC_MAX_MCS_8 == vht_max_mcs)
-					maxMCSIdx = 8;
-				else if (DATA_RATE_11AC_MAX_MCS_9 == vht_max_mcs)
-					maxMCSIdx = 9;
-
-				if (rssidx != 0) {
-					for (i = 0; i <= maxMCSIdx; i++) {
-						if (sinfo->signal <=
-						    rssi_mcs_tbl[mode][i]) {
-							maxMCSIdx = i;
-							break;
-						}
-					}
-				}
-
-				if (rate_flags & TX_RATE_VHT80) {
-					currentRate =
-					  supported_vht_mcs_rate[mcs_index].
-					  supported_VHT80_rate[rateFlag];
-					maxRate =
-					  supported_vht_mcs_rate[maxMCSIdx].
-						supported_VHT80_rate[rateFlag];
-				} else if (rate_flags & TX_RATE_VHT40) {
-					currentRate =
-					  supported_vht_mcs_rate[mcs_index].
-					  supported_VHT40_rate[rateFlag];
-					maxRate =
-					  supported_vht_mcs_rate[maxMCSIdx].
-						supported_VHT40_rate[rateFlag];
-				} else if (rate_flags & TX_RATE_VHT20) {
-					currentRate =
-					  supported_vht_mcs_rate[mcs_index].
-					  supported_VHT20_rate[rateFlag];
-					maxRate =
-					  supported_vht_mcs_rate[maxMCSIdx].
-					  supported_VHT20_rate[rateFlag];
-				}
-
-				maxSpeedMCS = 1;
-				if (currentRate > maxRate)
-					maxRate = currentRate;
-
-			} else {
-				if (rate_flags & TX_RATE_HT40)
-					rateFlag |= 1;
-				if (rate_flags & TX_RATE_SGI)
-					rateFlag |= 2;
-
-				supported_mcs_rate =
-					(struct index_data_rate_type *)
-					((nss ==
-					  1) ? &supported_mcs_rate_nss1 :
-					 &supported_mcs_rate_nss2);
-
-				maxHtIdx = MAX_HT_MCS_IDX;
-				if (rssidx != 0) {
-					for (i = 0; i < MAX_HT_MCS_IDX; i++) {
-						if (sinfo->signal <=
-						    rssi_mcs_tbl[mode][i]) {
-							maxHtIdx = i + 1;
-							break;
-						}
-					}
-				}
-
-				for (i = 0; i < MCSLeng; i++) {
-					for (j = 0; j < maxHtIdx; j++) {
-						if (supported_mcs_rate[j].
-						    beacon_rate_index ==
-						    MCSRates[i]) {
-							currentRate =
-							  supported_mcs_rate[j].
-							  supported_rate
-							  [rateFlag];
-							maxMCSIdx =
-							  supported_mcs_rate[j].
-							  beacon_rate_index;
-							break;
-						}
-					}
-
-					if ((j < MAX_HT_MCS_IDX)
-					    && (currentRate > maxRate)) {
-						maxRate = currentRate;
-					}
-					maxSpeedMCS = 1;
-				}
-				if (nss == 2)
-					maxMCSIdx += MAX_HT_MCS_IDX;
-			}
-		}
-
-		else if (!(rate_flags & TX_RATE_LEGACY)) {
-			maxRate = myRate;
-			maxSpeedMCS = 1;
-			maxMCSIdx = mcs_index;
-		}
-		/* report a value at least as big as current rate */
-		if ((maxRate < myRate) || (0 == maxRate)) {
-			maxRate = myRate;
-			if (rate_flags & TX_RATE_LEGACY) {
-				maxSpeedMCS = 0;
-			} else {
-				maxSpeedMCS = 1;
-				maxMCSIdx = mcs_index;
-			}
-		}
-
-		if (rate_flags & TX_RATE_LEGACY) {
-			sinfo->txrate.legacy = maxRate;
-			linkspeed_dbg("Reporting legacy rate %d\n",
-				      sinfo->txrate.legacy);
-		} else {
-			sinfo->txrate.mcs = maxMCSIdx;
-			sinfo->txrate.nss = nss;
-
-			if (rate_flags & TX_RATE_VHT80)
-				hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_80);
-			else if (rate_flags & TX_RATE_VHT40)
-				hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_40);
-			else if (rate_flags & TX_RATE_VHT20)
-				hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_20);
-
-			if (rate_flags &
-			    (TX_RATE_HT20 | TX_RATE_HT40)) {
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
-				if (rate_flags & TX_RATE_HT40)
-					hdd_set_rate_bw(&sinfo->txrate,
-							HDD_RATE_BW_40);
-				else if (rate_flags & TX_RATE_HT20)
-					hdd_set_rate_bw(&sinfo->txrate,
-							HDD_RATE_BW_20);
-			} else {
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
-			}
-
-			if (rate_flags & TX_RATE_SGI) {
-				if (!
-				    (sinfo->txrate.
-				     flags & RATE_INFO_FLAGS_VHT_MCS))
-					sinfo->txrate.flags |=
-						RATE_INFO_FLAGS_MCS;
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
-			}
-			linkspeed_dbg("Reporting MCS rate %d flags %x\n",
-				      sinfo->txrate.mcs, sinfo->txrate.flags);
-		}
 	} else {
-		/* report current rate instead of max rate */
 
-		if (rate_flags & TX_RATE_LEGACY) {
-			/* provide to the UI in units of 100kbps */
-			sinfo->txrate.legacy = myRate;
-			linkspeed_dbg("Reporting actual legacy rate %d\n",
-				      sinfo->txrate.legacy);
-		} else {
-			/* must be MCS */
-			sinfo->txrate.mcs = mcs_index;
-			sinfo->txrate.nss = nss;
+		/* Fill TX stats */
+		hdd_report_actual_rate(tx_rate_flags, my_tx_rate,
+				       &sinfo->txrate, tx_mcs_index, tx_nss);
 
-			if (rate_flags & TX_RATE_VHT80)
-				hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_80);
-			else if (rate_flags & TX_RATE_VHT40)
-				hdd_set_rate_bw(&sinfo->txrate, HDD_RATE_BW_40);
-
-			if (rate_flags &
-			    (TX_RATE_HT20 | TX_RATE_HT40)) {
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
-				if (rate_flags & TX_RATE_HT40)
-					hdd_set_rate_bw(&sinfo->txrate,
-							HDD_RATE_BW_40);
-			} else {
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_VHT_MCS;
-			}
-
-			if (rate_flags & TX_RATE_SGI) {
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_MCS;
-				sinfo->txrate.flags |= RATE_INFO_FLAGS_SHORT_GI;
-			}
-
-			linkspeed_dbg("Reporting actual MCS rate %d flags %x\n",
-				      sinfo->txrate.mcs, sinfo->txrate.flags);
-		}
+		/* Fill RX stats */
+		hdd_report_actual_rate(rx_rate_flags, my_rx_rate,
+				       &sinfo->rxrate, rx_mcs_index, rx_nss);
 	}
 
 	wlan_hdd_fill_summary_stats(&adapter->hdd_stats.summary_stat, sinfo);
@@ -4392,11 +4513,12 @@ static int wlan_hdd_get_sta_stats(struct wiphy *wiphy,
 		     &sinfo->txrate, sizeof(sinfo->txrate));
 
 	sinfo->filled |= HDD_INFO_TX_BITRATE |
+			 HDD_INFO_RX_BITRATE |
 			 HDD_INFO_TX_BYTES   |
 			 HDD_INFO_RX_BYTES   |
 			 HDD_INFO_RX_PACKETS;
 
-	if (rate_flags & TX_RATE_LEGACY)
+	if (tx_rate_flags & TX_RATE_LEGACY)
 		hdd_debug("Reporting legacy rate %d pkt cnt tx %d rx %d",
 			sinfo->txrate.legacy, sinfo->tx_packets,
 			sinfo->rx_packets);
@@ -4405,28 +4527,7 @@ static int wlan_hdd_get_sta_stats(struct wiphy *wiphy,
 			sinfo->txrate.mcs, sinfo->txrate.flags,
 			sinfo->tx_packets, sinfo->rx_packets);
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0))
-	sinfo->signal_avg = WLAN_HDD_TGT_NOISE_FLOOR_DBM;
-	for (i = 0; i < NUM_CHAINS_MAX; i++) {
-		sinfo->chain_signal_avg[i] =
-			   adapter->hdd_stats.per_chain_rssi_stats.rssi[i];
-		sinfo->chains |= 1 << i;
-		if (sinfo->chain_signal_avg[i] > sinfo->signal_avg &&
-				   sinfo->chain_signal_avg[i] != 0)
-			sinfo->signal_avg = sinfo->chain_signal_avg[i];
-
-		hdd_debug("RSSI for chain %d, vdev_id %d is %d",
-			i, adapter->session_id, sinfo->chain_signal_avg[i]);
-
-		if (!rssi_stats_valid && sinfo->chain_signal_avg[i])
-			rssi_stats_valid = true;
-	}
-
-	if (rssi_stats_valid) {
-		sinfo->filled |= HDD_INFO_CHAIN_SIGNAL_AVG;
-		sinfo->filled |= HDD_INFO_SIGNAL_AVG;
-	}
-#endif
+	hdd_wlan_fill_per_chain_rssi_stats(sinfo, adapter);
 
 	MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
 			 TRACE_CODE_HDD_CFG80211_GET_STA,
@@ -5792,16 +5893,21 @@ int wlan_hdd_get_station_stats(struct hdd_adapter *adapter)
 		stats->vdev_summary_stats[0].stats.rx_error_cnt;
 
 	/* save class a stats to legacy location */
-	adapter->hdd_stats.class_a_stat.nss =
+	adapter->hdd_stats.class_a_stat.tx_nss =
 		wlan_vdev_mlme_get_nss(adapter->vdev);
 	adapter->hdd_stats.class_a_stat.tx_rate = stats->tx_rate;
-	adapter->hdd_stats.class_a_stat.tx_rate_flags = stats->tx_rate_flags;
-	adapter->hdd_stats.class_a_stat.mcs_index =
-		sme_get_mcs_idx(stats->tx_rate * 5,
-				stats->tx_rate_flags,
-				&adapter->hdd_stats.class_a_stat.nss,
+	adapter->hdd_stats.class_a_stat.rx_rate = stats->rx_rate;
+	adapter->hdd_stats.class_a_stat.tx_rx_rate_flags = stats->tx_rate_flags;
+	adapter->hdd_stats.class_a_stat.tx_mcs_index =
+		sme_get_mcs_idx(stats->tx_rate * 5, stats->tx_rate_flags,
+				&adapter->hdd_stats.class_a_stat.tx_nss,
+				&mcs_rate_flags);
+	adapter->hdd_stats.class_a_stat.tx_mcs_rate_flags = mcs_rate_flags;
+	adapter->hdd_stats.class_a_stat.rx_mcs_index =
+		sme_get_mcs_idx(stats->rx_rate * 5, stats->tx_rate_flags,
+				&adapter->hdd_stats.class_a_stat.rx_nss,
 				&mcs_rate_flags);
-	adapter->hdd_stats.class_a_stat.mcs_rate_flags = mcs_rate_flags;
+	adapter->hdd_stats.class_a_stat.rx_mcs_rate_flags = mcs_rate_flags;
 
 	/* save per chain rssi to legacy location */
 	qdf_mem_copy(adapter->hdd_stats.per_chain_rssi_stats.rssi,

+ 8 - 4
core/sme/inc/csr_api.h

@@ -1473,14 +1473,18 @@ typedef struct tagCsrSummaryStatsInfo {
 } tCsrSummaryStatsInfo;
 
 typedef struct tagCsrGlobalClassAStatsInfo {
-	uint8_t nss;
+	uint8_t tx_nss;
+	uint8_t rx_nss;
 	uint32_t max_pwr;
 	uint32_t tx_rate;
+	uint32_t rx_rate;
 	/* mcs index for HT20 and HT40 rates */
-	uint32_t mcs_index;
-	uint32_t mcs_rate_flags;
+	uint32_t tx_mcs_index;
+	uint32_t rx_mcs_index;
+	uint32_t tx_mcs_rate_flags;
+	uint32_t rx_mcs_rate_flags;
 	/* to diff between HT20 & HT40 rates;short & long guard interval */
-	uint32_t tx_rate_flags;
+	uint32_t tx_rx_rate_flags;
 
 } tCsrGlobalClassAStatsInfo;
 

+ 21 - 4
core/wma/src/wma_utils.c

@@ -2656,6 +2656,7 @@ static void wma_update_peer_stats(tp_wma_handle wma,
 		if (temp_mask & (1 << eCsrGlobalClassAStats)) {
 			classa_stats = (tCsrGlobalClassAStatsInfo *) stats_buf;
 			WMA_LOGD("peer tx rate:%d", peer_stats->peer_tx_rate);
+			WMA_LOGD("peer rx rate:%d", peer_stats->peer_rx_rate);
 			/* The linkspeed returned by fw is in kbps so convert
 			 * it in to units of 500kbps which is expected by UMAC
 			 */
@@ -2664,16 +2665,32 @@ static void wma_update_peer_stats(tp_wma_handle wma,
 					peer_stats->peer_tx_rate / 500;
 			}
 
-			classa_stats->tx_rate_flags = node->rate_flags;
+			if (peer_stats->peer_rx_rate) {
+				classa_stats->rx_rate =
+					peer_stats->peer_rx_rate / 500;
+			}
+
+			classa_stats->tx_rx_rate_flags = node->rate_flags;
 			if (!(node->rate_flags & TX_RATE_LEGACY)) {
 				nss = node->nss;
-				classa_stats->mcs_index =
+				classa_stats->tx_mcs_index =
 					wma_get_mcs_idx(
 						(peer_stats->peer_tx_rate /
 						100), node->rate_flags,
 						&nss, &mcsRateFlags);
-				classa_stats->nss = nss;
-				classa_stats->mcs_rate_flags = mcsRateFlags;
+				classa_stats->tx_nss = nss;
+				classa_stats->tx_mcs_rate_flags = mcsRateFlags;
+			}
+
+			if (!(node->rate_flags & TX_RATE_LEGACY)) {
+				nss = node->nss;
+				classa_stats->rx_mcs_index =
+					wma_get_mcs_idx(
+						(peer_stats->peer_rx_rate /
+						100), node->rate_flags,
+						&nss, &mcsRateFlags);
+				classa_stats->rx_nss = nss;
+				classa_stats->rx_mcs_rate_flags = mcsRateFlags;
 			}
 			/* FW returns tx power in intervals of 0.5 dBm
 			 * Convert it back to intervals of 1 dBm