qcacld-3.0: Add support to get per chain rssi stats

Propagation from qcacld-2.0 to qcalcd-3.0.

Add support to get per chain rssi from fw via
already existing WMI command WMI_REQUEST_STATS_CMDID
and then store it in the station_info structure defined
by cfg80211.

Change-Id: Id50c96dd322b5ca4db34cf2df901730d8b103251
CRs-Fixed: 1005367
diff --git a/core/hdd/inc/wlan_hdd_main.h b/core/hdd/inc/wlan_hdd_main.h
index 4063495..d2cda7c 100644
--- a/core/hdd/inc/wlan_hdd_main.h
+++ b/core/hdd/inc/wlan_hdd_main.h
@@ -401,6 +401,7 @@
 	tCsrGlobalClassCStatsInfo ClassC_stat;
 	tCsrGlobalClassDStatsInfo ClassD_stat;
 	tCsrPerStaStatsInfo perStaStats;
+	struct csr_per_chain_rssi_stats_info  per_chain_rssi_stats;
 	hdd_tx_rx_stats_t hddTxRxStats;
 #ifdef WLAN_FEATURE_11W
 	hdd_pmf_stats_t hddPmfStats;
diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c
index 991b36c..8f1c09f 100644
--- a/core/hdd/src/wlan_hdd_cfg80211.c
+++ b/core/hdd/src/wlan_hdd_cfg80211.c
@@ -14031,6 +14031,7 @@
 	.mgmt_tx_cancel_wait = wlan_hdd_cfg80211_mgmt_tx_cancel_wait,
 	.set_default_mgmt_key = wlan_hdd_set_default_mgmt_key,
 	.set_txq_params = wlan_hdd_set_txq_params,
+	.dump_station = wlan_hdd_cfg80211_dump_station,
 	.get_station = wlan_hdd_cfg80211_get_station,
 	.set_power_mgmt = wlan_hdd_cfg80211_set_power_mgmt,
 	.del_station = wlan_hdd_cfg80211_del_station,
diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c
index ff7a538..600545f 100644
--- a/core/hdd/src/wlan_hdd_stats.c
+++ b/core/hdd/src/wlan_hdd_stats.c
@@ -1650,6 +1650,9 @@
 	int status, 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 eDataRate11ACMaxMcs vhtMaxMcs;
@@ -2128,6 +2131,35 @@
 			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] =
+			   pAdapter->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_info("RSSI for chain %d, vdev_id %d is %d",
+			i, pAdapter->sessionId, sinfo->chain_signal_avg[i]);
+
+		if (!rssi_stats_valid && sinfo->chain_signal_avg[i])
+			rssi_stats_valid = true;
+	}
+
+	if (rssi_stats_valid) {
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)) && !defined(WITH_BACKPORTS)
+		sinfo->filled |= STATION_INFO_CHAIN_SIGNAL_AVG;
+		sinfo->filled |= STATION_INFO_SIGNAL_AVG;
+#else
+		sinfo->filled |= BIT(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
+		sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
+#endif
+	}
+#endif
+
+
 	MTRACE(qdf_trace(QDF_MODULE_ID_HDD,
 			 TRACE_CODE_HDD_CFG80211_GET_STA,
 			 pAdapter->sessionId, maxRate));
@@ -2164,6 +2196,54 @@
 }
 
 /**
+ * __wlan_hdd_cfg80211_dump_station() - dump station statistics
+ * @wiphy: Pointer to wiphy
+ * @dev: Pointer to network device
+ * @idx: variable to determine whether to get stats or not
+ * @mac: Pointer to mac
+ * @sinfo: Pointer to station info
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+static int __wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy,
+				struct net_device *dev,
+				int idx, u8 *mac,
+				struct station_info *sinfo)
+{
+	hdd_context_t *hdd_ctx = (hdd_context_t *) wiphy_priv(wiphy);
+
+	hdd_debug("%s: idx %d", __func__, idx);
+	if (idx != 0)
+		return -ENOENT;
+	qdf_mem_copy(mac, hdd_ctx->config->intfMacAddr[0].bytes,
+				QDF_MAC_ADDR_SIZE);
+	return __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo);
+}
+
+/**
+ * wlan_hdd_cfg80211_dump_station() - dump station statistics
+ * @wiphy: Pointer to wiphy
+ * @dev: Pointer to network device
+ * @idx: variable to determine whether to get stats or not
+ * @mac: Pointer to mac
+ * @sinfo: Pointer to station info
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+int wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy,
+				struct net_device *dev,
+				int idx, u8 *mac,
+				struct station_info *sinfo)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_dump_station(wiphy, dev, idx, mac, sinfo);
+	cds_ssr_unprotect(__func__);
+	return ret;
+}
+
+/**
  * hdd_get_stats() - Function to retrieve interface statistics
  * @dev: pointer to network device
  *
diff --git a/core/hdd/src/wlan_hdd_stats.h b/core/hdd/src/wlan_hdd_stats.h
index c33031f..a7f4e12 100644
--- a/core/hdd/src/wlan_hdd_stats.h
+++ b/core/hdd/src/wlan_hdd_stats.h
@@ -40,6 +40,8 @@
 /* LL stats get request time out value */
 #define WLAN_WAIT_TIME_LL_STATS 800
 
+#define WLAN_HDD_TGT_NOISE_FLOOR_DBM     (-96)
+
 /**
  * struct index_vht_data_rate_type - vht data rate type
  * @beacon_rate_index: Beacon rate index
@@ -190,6 +192,21 @@
 				  struct station_info *sinfo);
 #endif
 
+/**
+ * wlan_hdd_cfg80211_dump_station() - dump station statistics
+ * @wiphy: Pointer to wiphy
+ * @dev: Pointer to network device
+ * @idx: variable to determine whether to get stats or not
+ * @mac: Pointer to mac
+ * @sinfo: Pointer to station info
+ *
+ * Return: 0 for success, non-zero for failure
+ */
+int wlan_hdd_cfg80211_dump_station(struct wiphy *wiphy,
+				struct net_device *dev,
+				int idx, u8 *mac,
+				struct station_info *sinfo);
+
 struct net_device_stats *hdd_get_stats(struct net_device *dev);
 
 int wlan_hdd_cfg80211_dump_survey(struct wiphy *wiphy,
diff --git a/core/hdd/src/wlan_hdd_wext.c b/core/hdd/src/wlan_hdd_wext.c
index 8d176a9..6f6051d 100644
--- a/core/hdd/src/wlan_hdd_wext.c
+++ b/core/hdd/src/wlan_hdd_wext.c
@@ -3717,6 +3717,7 @@
 	struct statsContext *pStatsContext;
 	tCsrSummaryStatsInfo *pSummaryStats;
 	tCsrGlobalClassAStatsInfo *pClassAStats;
+	struct csr_per_chain_rssi_stats_info *per_chain_rssi_stats;
 	hdd_adapter_t *pAdapter;
 
 	if (ioctl_debug) {
@@ -3739,6 +3740,8 @@
 
 	pSummaryStats = (tCsrSummaryStatsInfo *) pStats;
 	pClassAStats = (tCsrGlobalClassAStatsInfo *) (pSummaryStats + 1);
+	per_chain_rssi_stats = (struct csr_per_chain_rssi_stats_info *)
+				(pClassAStats + 1);
 	pStatsContext = pContext;
 	pAdapter = pStatsContext->pAdapter;
 	if ((NULL == pAdapter) ||
@@ -3764,6 +3767,7 @@
 	/* copy over the stats. do so as a struct copy */
 	pAdapter->hdd_stats.summary_stat = *pSummaryStats;
 	pAdapter->hdd_stats.ClassA_stat = *pClassAStats;
+	pAdapter->hdd_stats.per_chain_rssi_stats = *per_chain_rssi_stats;
 
 	/* notify the caller */
 	complete(&pStatsContext->completion);
@@ -3799,7 +3803,8 @@
 	hstatus = sme_get_statistics(WLAN_HDD_GET_HAL_CTX(pAdapter),
 				     eCSR_HDD,
 				     SME_SUMMARY_STATS |
-					SME_GLOBAL_CLASSA_STATS,
+				     SME_GLOBAL_CLASSA_STATS |
+				     SME_PER_CHAIN_RSSI_STATS,
 				     hdd_get_station_statistics_cb,
 				     0, /* not periodic */
 				     false, /* non-cached results */
diff --git a/core/mac/inc/sir_api.h b/core/mac/inc/sir_api.h
index c1fc594..de87474 100644
--- a/core/mac/inc/sir_api.h
+++ b/core/mac/inc/sir_api.h
@@ -109,6 +109,8 @@
 
 #define MAX_POWER_DBG_ARGS_SUPPORTED 8
 
+#define NUM_CHAINS_MAX  2
+
 /**
  * enum sir_conn_update_reason: Reason for conc connection update
  * @SIR_UPDATE_REASON_SET_OPER_CHAN: Set probable operating channel
diff --git a/core/sme/inc/csr_api.h b/core/sme/inc/csr_api.h
index d032de0..4fc6567 100644
--- a/core/sme/inc/csr_api.h
+++ b/core/sme/inc/csr_api.h
@@ -1522,6 +1522,16 @@
 	uint32_t tx_mpdu_in_ampdu_cnt;
 } tCsrPerStaStatsInfo;
 
+/**
+ * struct csr_per_chain_rssi_stats_info - stores chain rssi
+ * @rssi: array containing rssi for all chains
+ * @peer_mac_addr: peer mac address
+ */
+struct csr_per_chain_rssi_stats_info {
+	int8_t rssi[NUM_CHAINS_MAX];
+	tSirMacAddr peer_mac_addr;
+};
+
 typedef struct tagCsrRoamSetKey {
 	eCsrEncryptionType encType;
 	tAniKeyDirection keyDirection;  /* Tx, Rx or Tx-and-Rx */
diff --git a/core/sme/inc/csr_internal.h b/core/sme/inc/csr_internal.h
index 57df53f..8fe191b 100644
--- a/core/sme/inc/csr_internal.h
+++ b/core/sme/inc/csr_internal.h
@@ -240,6 +240,7 @@
 	eCsrGlobalClassCStats,
 	eCsrGlobalClassDStats,
 	eCsrPerStaStats,
+	csr_per_chain_rssi_stats,
 	eCsrMaxStats
 } eCsrRoamStatsClassTypes;
 
@@ -1023,6 +1024,7 @@
 	tCsrGlobalClassCStatsInfo classCStatsInfo;
 	tCsrGlobalClassDStatsInfo classDStatsInfo;
 	tCsrPerStaStatsInfo perStaStatsInfo[CSR_MAX_STA];
+	struct csr_per_chain_rssi_stats_info  per_chain_rssi_stats;
 	tDblLinkList statsClientReqList;
 	tDblLinkList peStatsReqList;
 	tCsrTlStatsReqInfo tlStatsReqInfo;
diff --git a/core/sme/inc/sme_api.h b/core/sme/inc/sme_api.h
index db91ca7..2514084 100644
--- a/core/sme/inc/sme_api.h
+++ b/core/sme/inc/sme_api.h
@@ -60,6 +60,7 @@
 #define SME_GLOBAL_CLASSC_STATS   8
 #define SME_GLOBAL_CLASSD_STATS  16
 #define SME_PER_STA_STATS        32
+#define SME_PER_CHAIN_RSSI_STATS 64
 
 #define SME_SESSION_ID_ANY        50
 
diff --git a/core/sme/src/csr/csr_api_roam.c b/core/sme/src/csr/csr_api_roam.c
index c804e5a..0086e0d 100644
--- a/core/sme/src/csr/csr_api_roam.c
+++ b/core/sme/src/csr/csr_api_roam.c
@@ -15986,6 +15986,14 @@
 		*stats += sizeof(tCsrPerStaStatsInfo);
 		*length -= sizeof(tCsrPerStaStatsInfo);
 		break;
+	case csr_per_chain_rssi_stats:
+		sms_log(mac, LOG2,
+			FL("csrRoamStatsRspProcessor:Per Chain RSSI stats"));
+		qdf_mem_copy((uint8_t *)&mac->roam.per_chain_rssi_stats,
+			*stats, sizeof(struct csr_per_chain_rssi_stats_info));
+		*stats += sizeof(struct csr_per_chain_rssi_stats_info);
+		*length -= sizeof(struct csr_per_chain_rssi_stats_info);
+		break;
 	default:
 		sms_log(mac, LOGW, FL("unknown stats type"));
 		break;
@@ -17754,6 +17762,14 @@
 					     sizeof(tCsrPerStaStatsInfo));
 				pStats += sizeof(tCsrPerStaStatsInfo);
 				break;
+			case csr_per_chain_rssi_stats:
+				sms_log(pMac, LOG2, FL("Per Chain RSSI stats"));
+				qdf_mem_copy(pStats,
+				  (uint8_t *)&pMac->roam.per_chain_rssi_stats,
+				  sizeof(struct csr_per_chain_rssi_stats_info));
+				pStats += sizeof(
+					struct csr_per_chain_rssi_stats_info);
+				break;
 			default:
 				sms_log(pMac, LOGE,
 					FL("Unknown stats type and counter %d"),
diff --git a/core/wma/inc/wma_internal.h b/core/wma/inc/wma_internal.h
index b5fc9f4..dadac75 100644
--- a/core/wma/inc/wma_internal.h
+++ b/core/wma/inc/wma_internal.h
@@ -55,12 +55,14 @@
 #define FW_PDEV_STATS_SET 0x1
 #define FW_VDEV_STATS_SET 0x2
 #define FW_PEER_STATS_SET 0x4
-#define FW_STATS_SET 0x7
+#define FW_RSSI_PER_CHAIN_STATS_SET 0x8
+#define FW_STATS_SET 0xf
 
 /*AR9888/AR6320  noise floor approx value
  * similar to the mentioned the WMA
  */
 #define WMA_TGT_NOISE_FLOOR_DBM (-96)
+#define WMA_TGT_RSSI_INVALID      96
 
 /*
  * Make sure that link monitor and keep alive
diff --git a/core/wma/src/wma_utils.c b/core/wma/src/wma_utils.c
index b8f3570..aa18f51 100644
--- a/core/wma/src/wma_utils.c
+++ b/core/wma/src/wma_utils.c
@@ -1298,11 +1298,6 @@
 				 node->rate_flags, node->nss,
 				 classa_stats->max_pwr);
 		}
-
-		if (node->fw_stats_set & FW_STATS_SET) {
-			WMA_LOGD("<--STATS RSP VDEV_ID:%d", vdev_id);
-			wma_post_stats(wma, node);
-		}
 	}
 }
 
@@ -1332,6 +1327,107 @@
 }
 
 /**
+ * wma_update_per_chain_rssi_stats() - to store per chain rssi stats
+ * @wma: wma handle
+ * @rssi_stats: rssi stats
+ * @rssi_per_chain_stats: buffer where rssi stats to be stored
+ *
+ * This function stores per chain rssi stats received from fw for all vdevs for
+ * which the stats were requested into a csr stats structure.
+ *
+ * Return: void
+ */
+static void wma_update_per_chain_rssi_stats(tp_wma_handle wma,
+		wmi_rssi_stats *rssi_stats,
+		struct csr_per_chain_rssi_stats_info *rssi_per_chain_stats)
+{
+	int i;
+	int8_t bcn_snr, dat_snr;
+
+	for (i = 0; i < NUM_CHAINS_MAX; i++) {
+		bcn_snr = rssi_stats->rssi_avg_beacon[i];
+		dat_snr = rssi_stats->rssi_avg_data[i];
+		WMA_LOGD("chain %d beacon snr %d data snr %d",
+			i, bcn_snr, dat_snr);
+		if (dat_snr != WMA_TGT_INVALID_SNR)
+			rssi_per_chain_stats->rssi[i] = dat_snr;
+		else if (bcn_snr != WMA_TGT_INVALID_SNR)
+			rssi_per_chain_stats->rssi[i] = bcn_snr;
+		else
+			/*
+			 * Firmware sends invalid snr till it sees
+			 * Beacon/Data after connection since after
+			 * vdev up fw resets the snr to invalid.
+			 * In this duartion Host will return an invalid rssi
+			 * value.
+			 */
+			rssi_per_chain_stats->rssi[i] = WMA_TGT_RSSI_INVALID;
+
+		/*
+		 * Get the absolute rssi value from the current rssi value the
+		 * sinr value is hardcoded into 0 in the qcacld-new/CORE stack
+		 */
+		rssi_per_chain_stats->rssi[i] += WMA_TGT_NOISE_FLOOR_DBM;
+		WMI_MAC_ADDR_TO_CHAR_ARRAY(&(rssi_stats->peer_macaddr),
+			rssi_per_chain_stats->peer_mac_addr);
+	}
+}
+
+/**
+ * wma_update_rssi_stats() - to update rssi stats for all vdevs
+ *         for which the stats were requested.
+ * @wma: wma handle
+ * @rssi_stats: rssi stats
+ *
+ * This function updates the rssi stats for all vdevs for which
+ * the stats were requested.
+ *
+ * Return: void
+ */
+static void wma_update_rssi_stats(tp_wma_handle wma,
+			wmi_rssi_stats *rssi_stats)
+{
+	tAniGetPEStatsRsp *stats_rsp_params;
+	struct csr_per_chain_rssi_stats_info *rssi_per_chain_stats = NULL;
+	struct wma_txrx_node *node;
+	uint8_t *stats_buf;
+	uint32_t temp_mask;
+	uint8_t vdev_id;
+
+	vdev_id = rssi_stats->vdev_id;
+	node = &wma->interfaces[vdev_id];
+	if (node->stats_rsp) {
+		node->fw_stats_set |=  FW_RSSI_PER_CHAIN_STATS_SET;
+		WMA_LOGD("<-- FW RSSI PER CHAIN STATS received for vdevId:%d",
+				vdev_id);
+		stats_rsp_params = (tAniGetPEStatsRsp *) node->stats_rsp;
+		stats_buf = (uint8_t *) (stats_rsp_params + 1);
+		temp_mask = stats_rsp_params->statsMask;
+
+		if (temp_mask & (1 << eCsrSummaryStats))
+			stats_buf += sizeof(tCsrSummaryStatsInfo);
+		if (temp_mask & (1 << eCsrGlobalClassAStats))
+			stats_buf += sizeof(tCsrGlobalClassAStatsInfo);
+		if (temp_mask & (1 << eCsrGlobalClassBStats))
+			stats_buf += sizeof(tCsrGlobalClassBStatsInfo);
+		if (temp_mask & (1 << eCsrGlobalClassCStats))
+			stats_buf += sizeof(tCsrGlobalClassCStatsInfo);
+		if (temp_mask & (1 << eCsrGlobalClassDStats))
+			stats_buf += sizeof(tCsrGlobalClassDStatsInfo);
+		if (temp_mask & (1 << eCsrPerStaStats))
+			stats_buf += sizeof(tCsrPerStaStatsInfo);
+
+		if (temp_mask & (1 << csr_per_chain_rssi_stats)) {
+			rssi_per_chain_stats =
+			     (struct csr_per_chain_rssi_stats_info *)stats_buf;
+			wma_update_per_chain_rssi_stats(wma, rssi_stats,
+					rssi_per_chain_stats);
+		}
+	}
+}
+
+
+/**
  * wma_link_status_event_handler() - link status event handler
  * @handle: wma handle
  * @cmd_param_info: data from event
@@ -1412,9 +1508,11 @@
 	wmi_pdev_stats *pdev_stats;
 	wmi_vdev_stats *vdev_stats;
 	wmi_peer_stats *peer_stats;
+	wmi_rssi_stats *rssi_stats;
+	wmi_per_chain_rssi_stats *rssi_event;
+	struct wma_txrx_node *node;
 	uint8_t i, *temp;
 
-
 	param_buf = (WMI_UPDATE_STATS_EVENTID_param_tlvs *) cmd_param_info;
 	if (!param_buf) {
 		WMA_LOGA("%s: Invalid stats event", __func__);
@@ -1450,6 +1548,34 @@
 		}
 	}
 
+	rssi_event = (wmi_per_chain_rssi_stats *) param_buf->chain_stats;
+	if (rssi_event) {
+		if ((rssi_event->tlv_header & 0xFFFF0000) >> 16 ==
+				WMITLV_TAG_STRUC_wmi_per_chain_rssi_stats) {
+			WMA_LOGD("%s: num_rssi_stats %u", __func__,
+				rssi_event->num_per_chain_rssi_stats);
+			if (rssi_event->num_per_chain_rssi_stats > 0) {
+				temp = (uint8_t *) rssi_event;
+				temp += sizeof(*rssi_event);
+				for (i = 0;
+				     i < rssi_event->num_per_chain_rssi_stats;
+				     i++) {
+					rssi_stats = (wmi_rssi_stats *)temp;
+					wma_update_rssi_stats(wma, rssi_stats);
+					temp += sizeof(wmi_rssi_stats);
+				}
+			}
+		}
+	}
+
+	for (i = 0; i < wma->max_bssid; i++) {
+		node = &wma->interfaces[i];
+		if (node->fw_stats_set & FW_PEER_STATS_SET) {
+			WMA_LOGD("<--STATS RSP VDEV_ID:%d", i);
+			wma_post_stats(wma, node);
+		}
+	}
+
 	WMA_LOGI("%s: Exit", __func__);
 	return 0;
 }
@@ -1880,6 +2006,10 @@
 			case eCsrPerStaStats:
 				len += sizeof(tCsrPerStaStatsInfo);
 				break;
+			case csr_per_chain_rssi_stats:
+				len +=
+				   sizeof(struct csr_per_chain_rssi_stats_info);
+				break;
 			}
 		}