qcacmn: Extract and populate peer_extd2 stats from fw

Presently, the driver doesnot extract the peer extended2 stats
(rx_bytes, rx_count, fcs_err) that are received from firmware.

Provide the support to extract and populate the peer extended stats2 at
the driver level.

Change-Id: If1f1bb1ef2d1202581744dd509d0da1da718d8c1
CRs-Fixed: 2397638
diff --git a/os_if/linux/cp_stats/src/wlan_cfg80211_mc_cp_stats.c b/os_if/linux/cp_stats/src/wlan_cfg80211_mc_cp_stats.c
index 3f18ed6..8c20932 100644
--- a/os_if/linux/cp_stats/src/wlan_cfg80211_mc_cp_stats.c
+++ b/os_if/linux/cp_stats/src/wlan_cfg80211_mc_cp_stats.c
@@ -51,6 +51,7 @@
 	qdf_mem_free(stats->cca_stats);
 	qdf_mem_free(stats->vdev_summary_stats);
 	qdf_mem_free(stats->vdev_chain_rssi);
+	qdf_mem_free(stats->peer_adv_stats);
 }
 
 /**
@@ -419,7 +420,7 @@
 {
 	struct stats_event *priv;
 	struct osif_request *request;
-	uint32_t summary_size, rssi_size;
+	uint32_t summary_size, rssi_size, peer_adv_size;
 
 	request = osif_request_get(cookie);
 	if (!request) {
@@ -430,8 +431,10 @@
 	priv = osif_request_priv(request);
 	summary_size = sizeof(*ev->vdev_summary_stats) * ev->num_summary_stats;
 	rssi_size = sizeof(*ev->vdev_chain_rssi) * ev->num_chain_rssi_stats;
+	peer_adv_size = sizeof(*ev->peer_adv_stats) * ev->num_peer_adv_stats;
+
 	if (summary_size == 0 || rssi_size == 0) {
-		cfg80211_err("Invalid stats, summary size %d rssi size %d",
+		cfg80211_err("Invalid stats, summary %d rssi %d",
 			     summary_size, rssi_size);
 		goto station_stats_cb_fail;
 	}
@@ -444,11 +447,21 @@
 	if (!priv->vdev_chain_rssi)
 		goto station_stats_cb_fail;
 
+	if (peer_adv_size) {
+		priv->peer_adv_stats = qdf_mem_malloc(peer_adv_size);
+		if (!priv->peer_adv_stats)
+			goto station_stats_cb_fail;
+
+		qdf_mem_copy(priv->peer_adv_stats, ev->peer_adv_stats,
+			     peer_adv_size);
+	}
+
 	priv->num_summary_stats = ev->num_summary_stats;
 	priv->num_chain_rssi_stats = ev->num_chain_rssi_stats;
 	priv->tx_rate = ev->tx_rate;
 	priv->rx_rate = ev->rx_rate;
 	priv->tx_rate_flags = ev->tx_rate_flags;
+	priv->num_peer_adv_stats = ev->num_peer_adv_stats;
 	qdf_mem_copy(priv->vdev_chain_rssi, ev->vdev_chain_rssi, rssi_size);
 	qdf_mem_copy(priv->vdev_summary_stats, ev->vdev_summary_stats,
 		     summary_size);
@@ -517,7 +530,8 @@
 
 	if (!priv->vdev_summary_stats || !priv->vdev_chain_rssi ||
 	    priv->num_summary_stats == 0 || priv->num_chain_rssi_stats == 0) {
-		cfg80211_err("Invalid stats, summary %d:%pK, rssi %d:%pK",
+		cfg80211_err("Invalid stats");
+		cfg80211_err("summary %d:%pK, rssi %d:%pK",
 			     priv->num_summary_stats, priv->vdev_summary_stats,
 			     priv->num_chain_rssi_stats, priv->vdev_chain_rssi);
 		*errno = -EINVAL;
@@ -533,6 +547,10 @@
 	priv->vdev_summary_stats = NULL;
 	out->vdev_chain_rssi = priv->vdev_chain_rssi;
 	priv->vdev_chain_rssi = NULL;
+	out->num_peer_adv_stats = priv->num_peer_adv_stats;
+	if (priv->peer_adv_stats)
+		out->peer_adv_stats = priv->peer_adv_stats;
+	priv->peer_adv_stats = NULL;
 	osif_request_put(request);
 
 	return out;
@@ -554,5 +572,6 @@
 	qdf_mem_free(stats->cca_stats);
 	qdf_mem_free(stats->vdev_summary_stats);
 	qdf_mem_free(stats->vdev_chain_rssi);
+	qdf_mem_free(stats->peer_adv_stats);
 	qdf_mem_free(stats);
 }
diff --git a/target_if/cp_stats/src/target_if_mc_cp_stats.c b/target_if/cp_stats/src/target_if_mc_cp_stats.c
index ae16d45..f125f83 100644
--- a/target_if/cp_stats/src/target_if_mc_cp_stats.c
+++ b/target_if/cp_stats/src/target_if_mc_cp_stats.c
@@ -41,6 +41,8 @@
 	ev->pdev_stats = NULL;
 	qdf_mem_free(ev->peer_stats);
 	ev->peer_stats = NULL;
+	qdf_mem_free(ev->peer_adv_stats);
+	ev->peer_adv_stats = NULL;
 	qdf_mem_free(ev->cca_stats);
 	ev->cca_stats = NULL;
 	qdf_mem_free(ev->vdev_summary_stats);
@@ -94,7 +96,9 @@
 	QDF_STATUS status;
 	wmi_host_peer_stats peer_stats;
 	bool db2dbm_enabled;
+	struct wmi_host_peer_adv_stats *peer_adv_stats;
 
+	/* Extract peer_stats */
 	ev->num_peer_stats = stats_param->num_peer_stats;
 	if (!ev->num_peer_stats)
 		return QDF_STATUS_SUCCESS;
@@ -109,11 +113,11 @@
 	for (i = 0; i < ev->num_peer_stats; i++) {
 		status = wmi_extract_peer_stats(wmi_hdl, data, i, &peer_stats);
 		if (QDF_IS_STATUS_ERROR(status)) {
-			cp_stats_err("wmi_extract_pdev_stats failed");
+			cp_stats_err("wmi_extract_peer_stats failed");
 			continue;
 		}
 		WMI_MAC_ADDR_TO_CHAR_ARRAY(&peer_stats.peer_macaddr,
-						ev->peer_stats[i].peer_macaddr);
+					   ev->peer_stats[i].peer_macaddr);
 		ev->peer_stats[i].tx_rate = peer_stats.peer_tx_rate;
 		ev->peer_stats[i].rx_rate = peer_stats.peer_rx_rate;
 		if (db2dbm_enabled)
@@ -123,6 +127,41 @@
 							TGT_NOISE_FLOOR_DBM;
 	}
 
+	/* Extract peer_adv_stats */
+	ev->num_peer_adv_stats = stats_param->num_peer_adv_stats;
+	if (!ev->num_peer_adv_stats)
+		return QDF_STATUS_SUCCESS;
+
+	ev->peer_adv_stats = qdf_mem_malloc(sizeof(*ev->peer_adv_stats) *
+					    ev->num_peer_adv_stats);
+	if (!ev->peer_adv_stats)
+		return QDF_STATUS_E_NOMEM;
+
+	peer_adv_stats = qdf_mem_malloc(sizeof(*peer_adv_stats) *
+					ev->num_peer_adv_stats);
+	if (!peer_adv_stats) {
+		qdf_mem_free(ev->peer_adv_stats);
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	status = wmi_extract_peer_adv_stats(wmi_hdl, data, peer_adv_stats);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cp_stats_err("wmi_extract_peer_stats failed");
+		qdf_mem_free(peer_adv_stats);
+		qdf_mem_free(ev->peer_adv_stats);
+		ev->peer_adv_stats = NULL;
+		return QDF_STATUS_SUCCESS;
+	}
+
+	for (i = 0; i < ev->num_peer_adv_stats; i++) {
+		qdf_mem_copy(&ev->peer_adv_stats[i].peer_macaddr,
+			     &peer_adv_stats[i].peer_macaddr,
+			     WLAN_MACADDR_LEN);
+		ev->peer_adv_stats[i].fcs_count = peer_adv_stats[i].fcs_count;
+		ev->peer_adv_stats[i].rx_bytes = peer_adv_stats[i].rx_bytes;
+		ev->peer_adv_stats[i].rx_count = peer_adv_stats[i].rx_count;
+	}
+	qdf_mem_free(peer_adv_stats);
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -280,6 +319,7 @@
 		       stats_param.num_pdev_stats, stats_param.num_vdev_stats,
 		       stats_param.num_peer_stats, stats_param.num_rssi_stats);
 
+	ev->last_event = stats_param.last_event;
 	status = target_if_cp_stats_extract_pdev_stats(wmi_hdl, &stats_param,
 						       ev, data);
 	if (QDF_IS_STATUS_ERROR(status))
@@ -506,6 +546,7 @@
 			WMI_REQUEST_PEER_STAT |
 			WMI_REQUEST_VDEV_STAT |
 			WMI_REQUEST_PDEV_STAT |
+			WMI_REQUEST_PEER_EXTD2_STAT |
 			WMI_REQUEST_RSSI_PER_CHAIN_STAT);
 	}
 
diff --git a/umac/cp_stats/core/src/wlan_cp_stats_defs.h b/umac/cp_stats/core/src/wlan_cp_stats_defs.h
index e37a1be..dcad1b2 100644
--- a/umac/cp_stats/core/src/wlan_cp_stats_defs.h
+++ b/umac/cp_stats/core/src/wlan_cp_stats_defs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -87,12 +87,14 @@
  * struct peer_cp_stats - defines cp stats at peer object
  * @peer_obj: pointer to peer
  * @peer_stats: pointer to ic/mc specific stats
+ * @peer_adv_stats: pointer to peer adv stats
  * @peer_comp_priv_obj[]: component's private object pointers
  * @peer_cp_stats_lock:	lock to protect object
  */
 struct peer_cp_stats {
 	struct wlan_objmgr_peer  *peer_obj;
 	void                     *peer_stats;
+	void                     *peer_adv_stats;
 	void *peer_comp_priv_obj[WLAN_CP_STATS_MAX_COMPONENTS];
 	qdf_spinlock_t peer_cp_stats_lock;
 };
diff --git a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h
index 9556a41..bfca991 100644
--- a/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h
+++ b/umac/cp_stats/dispatcher/inc/wlan_cp_stats_mc_defs.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2012-2019 The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -33,6 +33,9 @@
 
 #define MAX_NUM_CHAINS              2
 
+#define IS_MSB_SET(__num) ((__num) & BIT(31))
+#define IS_LSB_SET(__num) ((__num) & BIT(0))
+
 /**
  * enum stats_req_type - enum indicating bit position of various stats type in
  * request map
@@ -249,6 +252,20 @@
 };
 
 /**
+ * struct peer_adv_mc_cp_stats - peer specific adv stats
+ * @peer_macaddr: mac address
+ * @fcs_count: fcs count
+ * @rx_bytes: rx bytes
+ * @rx_count: rx count
+ */
+struct peer_adv_mc_cp_stats {
+	uint8_t peer_macaddr[WLAN_MACADDR_LEN];
+	uint32_t fcs_count;
+	uint32_t rx_count;
+	uint64_t rx_bytes;
+};
+
+/**
  * struct congestion_stats_event: congestion stats event param
  * @vdev_id: vdev_id of the event
  * @congestion: the congestion percentage
@@ -284,6 +301,8 @@
  * @pdev_stats: if populated array indicating pdev stats (index = pdev_id)
  * @num_peer_stats: num peer stats
  * @peer_stats: if populated array indicating peer stats
+ * @peer_adv_stats: if populated, indicates peer adv (extd2) stats
+ * @num_peer_adv_stats: number of peer adv (extd2) stats
  * @cca_stats: if populated indicates congestion stats
  * @num_summary_stats: number of summary stats
  * @vdev_summary_stats: if populated indicates array of summary stats per vdev
@@ -291,12 +310,16 @@
  * @vdev_chain_rssi: if populated indicates array of chain rssi per vdev
  * @tx_rate: tx rate (kbps)
  * @tx_rate_flags: tx rate flags, (enum tx_rate_info)
+ * @last_event: The LSB indicates if the event is the last event or not and the
+ *              MSB indicates if this feature is supported by FW or not.
  */
 struct stats_event {
 	uint32_t num_pdev_stats;
 	struct pdev_mc_cp_stats *pdev_stats;
 	uint32_t num_peer_stats;
 	struct peer_mc_cp_stats *peer_stats;
+	uint32_t num_peer_adv_stats;
+	struct peer_adv_mc_cp_stats *peer_adv_stats;
 	struct congestion_stats_event *cca_stats;
 	uint32_t num_summary_stats;
 	struct summary_stats_event *vdev_summary_stats;
@@ -305,6 +328,7 @@
 	uint32_t tx_rate;
 	uint32_t rx_rate;
 	enum tx_rate_info tx_rate_flags;
+	uint32_t last_event;
 };
 
 #endif /* CONFIG_MCL */
diff --git a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c
index 5198424..721f707 100644
--- a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c
+++ b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c
@@ -213,6 +213,54 @@
 }
 
 static QDF_STATUS
+tgt_mc_cp_stats_update_peer_adv_stats(struct wlan_objmgr_psoc *psoc,
+				      struct peer_adv_mc_cp_stats
+				      *peer_adv_stats, uint32_t size)
+{
+	uint8_t *peer_mac_addr;
+	struct wlan_objmgr_peer *peer;
+	struct peer_adv_mc_cp_stats *peer_adv_mc_stats;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+	struct peer_cp_stats *peer_cp_stats_priv;
+
+	if (!peer_adv_stats)
+		return QDF_STATUS_E_INVAL;
+
+	peer_mac_addr = peer_adv_stats->peer_macaddr;
+	peer = wlan_objmgr_get_peer_by_mac(psoc, peer_mac_addr,
+					   WLAN_CP_STATS_ID);
+	if (!peer) {
+		cp_stats_err("peer is null");
+		return QDF_STATUS_E_EXISTS;
+	}
+	peer_cp_stats_priv = wlan_cp_stats_get_peer_stats_obj(peer);
+	if (!peer_cp_stats_priv) {
+		cp_stats_err("peer_cp_stats_priv is null");
+		status = QDF_STATUS_E_EXISTS;
+		goto end;
+	}
+	wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv);
+	peer_adv_mc_stats = peer_cp_stats_priv->peer_adv_stats;
+
+	qdf_mem_copy(peer_adv_mc_stats->peer_macaddr,
+		     peer_adv_stats->peer_macaddr,
+		     WLAN_MACADDR_LEN);
+	if (peer_adv_stats->fcs_count)
+		peer_adv_mc_stats->fcs_count = peer_adv_stats->fcs_count;
+	if (peer_adv_stats->rx_bytes)
+		peer_adv_mc_stats->rx_bytes = peer_adv_stats->rx_bytes;
+	if (peer_adv_stats->rx_count)
+		peer_adv_mc_stats->rx_count = peer_adv_stats->rx_count;
+	wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv);
+
+end:
+	if (peer)
+		wlan_objmgr_peer_release_ref(peer, WLAN_CP_STATS_ID);
+
+	return status;
+}
+
+static QDF_STATUS
 tgt_mc_cp_stats_update_peer_stats(struct wlan_objmgr_psoc *psoc,
 				  struct peer_mc_cp_stats *peer_stats)
 {
@@ -273,10 +321,6 @@
 	struct request_info last_req = {0};
 	uint32_t selected;
 
-	if (!ev->peer_stats) {
-		cp_stats_err("no peer stats");
-		return;
-	}
 
 	if (is_station_stats)
 		status = ucfg_mc_cp_stats_get_pending_req(psoc,
@@ -292,6 +336,11 @@
 		return;
 	}
 
+	if (!ev->peer_stats) {
+		cp_stats_debug("no peer stats");
+		goto extd2_stats;
+	}
+
 	selected = ev->num_peer_stats;
 	for (i = 0; i < ev->num_peer_stats; i++) {
 		status = tgt_mc_cp_stats_update_peer_stats(psoc,
@@ -311,10 +360,40 @@
 	/* no matched peer */
 	if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) &&
 	    selected == ev->num_peer_stats) {
-		cp_stats_err("peer not found stats");
+		cp_stats_err("peer not found for stats");
+	}
+
+extd2_stats:
+
+	if (!ev->peer_adv_stats) {
+		cp_stats_err("no peer_extd2 stats");
+		goto complete;
+	}
+	selected = ev->num_peer_adv_stats;
+	for (i = 0; i < ev->num_peer_adv_stats; i++) {
+		status = tgt_mc_cp_stats_update_peer_adv_stats(
+						psoc, &ev->peer_adv_stats[i],
+						ev->num_peer_adv_stats);
+		if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) &&
+		    !qdf_mem_cmp(ev->peer_adv_stats[i].peer_macaddr,
+				 last_req.peer_mac_addr,
+				 WLAN_MACADDR_LEN)) {
+			/* mac is specified, but failed to update the peer */
+			if (QDF_IS_STATUS_ERROR(status))
+				return;
+
+			selected = i;
+		}
+	}
+
+	/* no matched peer */
+	if (!QDF_IS_ADDR_BROADCAST(last_req.peer_mac_addr) &&
+	    selected == ev->num_peer_adv_stats) {
+		cp_stats_err("peer not found for extd stats");
 		return;
 	}
 
+complete:
 	if (is_station_stats)
 		return;
 
@@ -563,6 +642,10 @@
 	info.tx_rate_flags = vdev_mc_stats->tx_rate_flags;
 	wlan_cp_stats_vdev_obj_unlock(vdev_cp_stats_priv);
 
+	info.peer_adv_stats = qdf_mem_malloc(sizeof(*info.peer_adv_stats));
+	if (!info.peer_adv_stats)
+		goto end;
+
 	wlan_cp_stats_peer_obj_lock(peer_cp_stats_priv);
 	peer_mc_stats = peer_cp_stats_priv->peer_stats;
 	/*
@@ -571,6 +654,14 @@
 	 */
 	info.tx_rate = peer_mc_stats->tx_rate / 100;
 	info.rx_rate = peer_mc_stats->rx_rate / 100;
+
+	if (peer_cp_stats_priv->peer_adv_stats) {
+		info.num_peer_adv_stats = 1;
+		qdf_mem_copy(info.peer_adv_stats,
+			     peer_cp_stats_priv->peer_adv_stats,
+			     sizeof(peer_cp_stats_priv->peer_adv_stats));
+	}
+
 	wlan_cp_stats_peer_obj_unlock(peer_cp_stats_priv);
 
 end:
@@ -590,10 +681,14 @@
 				struct stats_event *ev)
 {
 	QDF_STATUS status;
-	bool is_peer_stats;
+	bool is_last_event;
 	struct request_info last_req = {0};
 
-	is_peer_stats = (ev->peer_stats != NULL);
+	if (IS_MSB_SET(ev->last_event))
+		is_last_event = IS_LSB_SET(ev->last_event);
+	else
+		is_last_event = !!ev->peer_stats;
+
 	status = ucfg_mc_cp_stats_get_pending_req(psoc,
 						  TYPE_STATION_STATS,
 						  &last_req);
@@ -611,7 +706,7 @@
 	 * PEER stats are the last stats sent for get_station statistics.
 	 * reset type_map bit for station stats .
 	 */
-	if (is_peer_stats) {
+	if (is_last_event) {
 		tgt_mc_cp_stats_prepare_n_send_raw_station_stats(psoc,
 								 &last_req);
 		ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_STATION_STATS);
diff --git a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c
index 7a77704..def4b8c 100644
--- a/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c
+++ b/umac/cp_stats/dispatcher/src/wlan_cp_stats_mc_ucfg_api.c
@@ -85,11 +85,20 @@
 	if (!peer_cs->peer_stats)
 		return QDF_STATUS_E_NOMEM;
 
+	peer_cs->peer_adv_stats = qdf_mem_malloc(sizeof
+						 (struct peer_adv_mc_cp_stats));
+	if (!peer_cs->peer_adv_stats) {
+		cp_stats_err("malloc failed");
+		qdf_mem_free(peer_cs->peer_stats);
+		return QDF_STATUS_E_NOMEM;
+	}
 	return QDF_STATUS_SUCCESS;
 }
 
 QDF_STATUS wlan_cp_stats_peer_cs_deinit(struct peer_cp_stats *peer_cs)
 {
+	qdf_mem_free(peer_cs->peer_adv_stats);
+	peer_cs->peer_adv_stats = NULL;
 	qdf_mem_free(peer_cs->peer_stats);
 	peer_cs->peer_stats = NULL;
 	return QDF_STATUS_SUCCESS;
@@ -554,6 +563,7 @@
 		return;
 
 	qdf_mem_free(ev->pdev_stats);
+	qdf_mem_free(ev->peer_adv_stats);
 	qdf_mem_free(ev->peer_stats);
 	qdf_mem_free(ev->cca_stats);
 	qdf_mem_free(ev->vdev_summary_stats);