Browse Source

qcacld-3.0: Send get_station and ll_stats req in a single command

Currently ll_stats and get_station requests are sent back to back
from user space. The firmware time difference between these two commands
results in extra sleep and wakeup for Q6.

To avoid this extra Q6 sleep and wake, send both requests in a single
command from the driver if the firmware supports that operation. Cache
the get_station results in the driver until user space request reach
to the driver.

Change-Id: Ic7c6232558b9411106bf3fb88ec8d9b0b28b045d
CRs-Fixed: 2778476
Bapiraju Alla 4 years ago
parent
commit
5191713c4b

+ 1 - 0
Kbuild

@@ -2554,6 +2554,7 @@ cppflags-$(CONFIG_FEATURE_WLAN_LPHB) += -DFEATURE_WLAN_LPHB
 cppflags-$(CONFIG_QCA_SUPPORT_TX_THROTTLE) += -DQCA_SUPPORT_TX_THROTTLE
 cppflags-$(CONFIG_WMI_INTERFACE_EVENT_LOGGING) += -DWMI_INTERFACE_EVENT_LOGGING
 cppflags-$(CONFIG_WLAN_FEATURE_LINK_LAYER_STATS) += -DWLAN_FEATURE_LINK_LAYER_STATS
+cppflags-$(CONFIG_FEATURE_CLUB_LL_STATS_AND_GET_STATION) += -DFEATURE_CLUB_LL_STATS_AND_GET_STATION
 cppflags-$(CONFIG_WLAN_FEATURE_MIB_STATS) += -DWLAN_FEATURE_MIB_STATS
 cppflags-$(CONFIG_FEATURE_WLAN_EXTSCAN) += -DFEATURE_WLAN_EXTSCAN
 cppflags-$(CONFIG_160MHZ_SUPPORT) += -DCONFIG_160MHZ_SUPPORT

+ 1 - 0
configs/default_defconfig

@@ -946,6 +946,7 @@ CONFIG_FEATURE_WLAN_LPHB := y
 CONFIG_QCA_SUPPORT_TX_THROTTLE := y
 CONFIG_WMI_INTERFACE_EVENT_LOGGING := y
 CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y
+CONFIG_FEATURE_CLUB_LL_STATS_AND_GET_STATION := y
 CONFIG_FEATURE_WLAN_EXTSCAN := n
 CONFIG_WMI_BCN_OFFLOAD := y
 CONFIG_160MHZ_SUPPORT := y

+ 59 - 0
core/hdd/inc/hdd_config.h

@@ -1536,6 +1536,64 @@ struct dhcp_server {
 #define CFG_WLAN_STA_PERIODIC_STATS
 #endif /* WLAN_FEATURE_PERIODIC_STA_STATS */
 
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+/*
+ * <ini>
+ * club_get_sta_in_ll_stats_req - Flag used to club ll_stats and get_station
+ *                                requests in the driver
+ *
+ * @Min: 0
+ * @Max: 1
+ * Default: 1
+ *
+ * This ini param is used to enable/disable the feature for clubbing ll stats
+ * and get station requests.
+ *
+ * Supported Feature: STA
+ *
+ * Usage: External
+ *
+ * </ini>
+ */
+#define CFG_CLUB_LL_STA_AND_GET_STATION  CFG_INI_BOOL( \
+			"club_get_sta_in_ll_stats_req", \
+			1, \
+			"Club ll_stats and get station requests")
+
+/*
+ * <ini>
+ * sta_stats_cache_expiry_time - Expiry time for cached station stats
+ *
+ * @Min: 0
+ * @Max: 5000
+ * Default: 200
+ *
+ * This ini is used as duration in milliseconds for which cached station stats
+ * are valid. Driver sends the cached information as response, if it gets the
+ * get_station request with in this duration. Otherwise driver sends new
+ * request to the firmware to get the updated stats.
+ *
+ * Supported Feature: STA
+ *
+ * Usage: External
+ *
+ * </ini>
+ */
+#define CFG_STA_STATS_CACHE_EXPIRY  CFG_INI_UINT( \
+			"sta_stats_cache_expiry_time", \
+			0, \
+			5000, \
+			200, \
+			CFG_VALUE_OR_DEFAULT, \
+			"Station stats cache expiry")
+
+#define CFG_WLAN_CLUB_GET_STA_IN_LL_STA_REQ \
+	 CFG(CFG_CLUB_LL_STA_AND_GET_STATION) \
+	 CFG(CFG_STA_STATS_CACHE_EXPIRY)
+#else
+#define CFG_WLAN_CLUB_GET_STA_IN_LL_STA_REQ
+#endif /* FEATURE_CLUB_LL_STATS_AND_GET_STATION */
+
 /**
  * enum host_log_level - Debug verbose level imposed by user
  * @HOST_LOG_LEVEL_NONE: no trace will be logged.
@@ -1652,6 +1710,7 @@ enum host_log_level {
 	CFG_ENABLE_QMI_STATS_ALL \
 	CFG_VC_MODE_BITMAP_ALL \
 	CFG_WLAN_AUTO_SHUTDOWN_ALL \
+	CFG_WLAN_CLUB_GET_STA_IN_LL_STA_REQ \
 	CFG_WLAN_LOGGING_SUPPORT_ALL \
 	CFG_WLAN_STA_PERIODIC_STATS \
 	CFG(CFG_ACTION_OUI_CCKM_1X1) \

+ 4 - 0
core/hdd/inc/wlan_hdd_cfg.h

@@ -257,6 +257,10 @@ struct hdd_config {
 	uint32_t periodic_stats_timer_duration;
 #endif /* WLAN_FEATURE_PERIODIC_STA_STATS */
 	uint8_t nb_commands_interval;
+
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+	uint32_t sta_stats_cache_expiry_time;
+#endif
 };
 
 /**

+ 8 - 0
core/hdd/inc/wlan_hdd_main.h

@@ -701,6 +701,11 @@ struct hdd_stats {
 	struct hdd_eapol_stats_s hdd_eapol_stats;
 	struct hdd_dhcp_stats_s hdd_dhcp_stats;
 	struct pmf_bcn_protect_stats bcn_protect_stats;
+
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+	uint32_t sta_stats_cached_timestamp;
+	bool is_ll_stats_req_in_progress;
+#endif
 };
 
 /**
@@ -2072,6 +2077,9 @@ struct hdd_context {
 	qdf_workqueue_t *adapter_ops_wq;
 	struct hdd_adapter_ops_history adapter_ops_history;
 	bool ll_stats_per_chan_rx_tx_time;
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+	bool is_get_station_clubbed_in_ll_stats_req;
+#endif
 #ifdef FEATURE_WPSS_THERMAL_MITIGATION
 	bool multi_client_thermal_mitigation;
 #endif

+ 51 - 0
core/hdd/src/wlan_hdd_main.c

@@ -1596,6 +1596,52 @@ static inline void hdd_update_roam_offload(struct hdd_context *hdd_ctx,
 }
 #endif
 
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+static void
+hdd_club_ll_stats_in_get_sta_cfg_update(struct hdd_config *config,
+					struct wlan_objmgr_psoc *psoc)
+{
+	config->sta_stats_cache_expiry_time =
+			cfg_get(psoc, CFG_STA_STATS_CACHE_EXPIRY);
+}
+
+static void
+hdd_update_feature_cfg_club_get_sta_in_ll_stats_req(
+					struct hdd_context *hdd_ctx,
+					struct wma_tgt_services *cfg)
+{
+	hdd_ctx->is_get_station_clubbed_in_ll_stats_req =
+				cfg->is_get_station_clubbed_in_ll_stats_req &&
+				cfg_get(hdd_ctx->psoc,
+					CFG_CLUB_LL_STA_AND_GET_STATION);
+}
+
+static void
+hdd_init_get_sta_in_ll_stats_config(struct hdd_adapter *adapter)
+{
+	adapter->hdd_stats.is_ll_stats_req_in_progress = false;
+	adapter->hdd_stats.sta_stats_cached_timestamp = 0;
+}
+#else
+static void
+hdd_club_ll_stats_in_get_sta_cfg_update(struct hdd_config *config,
+					struct wlan_objmgr_psoc *psoc)
+{
+}
+
+static void
+hdd_update_feature_cfg_club_get_sta_in_ll_stats_req(
+					struct hdd_context *hdd_ctx,
+					struct wma_tgt_services *cfg)
+{
+}
+
+static void
+hdd_init_get_sta_in_ll_stats_config(struct hdd_adapter *adapter)
+{
+}
+#endif /* FEATURE_CLUB_LL_STATS_AND_GET_STATION */
+
 static void hdd_update_tgt_services(struct hdd_context *hdd_ctx,
 				    struct wma_tgt_services *cfg)
 {
@@ -1672,6 +1718,8 @@ static void hdd_update_tgt_services(struct hdd_context *hdd_ctx,
 	hdd_ctx->roam_ch_from_fw_supported = cfg->is_roam_scan_ch_to_host;
 	hdd_ctx->ll_stats_per_chan_rx_tx_time =
 					cfg->ll_stats_per_chan_rx_tx_time;
+
+	hdd_update_feature_cfg_club_get_sta_in_ll_stats_req(hdd_ctx, cfg);
 }
 
 /**
@@ -5136,6 +5184,8 @@ hdd_alloc_station_adapter(struct hdd_context *hdd_ctx, tSirMacAddr mac_addr,
 	adapter->start_time = qdf_system_ticks();
 	adapter->last_time = adapter->start_time;
 
+	hdd_init_get_sta_in_ll_stats_config(adapter);
+
 	return adapter;
 
 free_net_dev:
@@ -11997,6 +12047,7 @@ static void hdd_cfg_params_init(struct hdd_context *hdd_ctx)
 	hdd_dp_cfg_update(psoc, hdd_ctx);
 	hdd_sar_cfg_update(config, psoc);
 	hdd_init_qmi_stats(config, psoc);
+	hdd_club_ll_stats_in_get_sta_cfg_update(config, psoc);
 }
 
 struct hdd_context *hdd_context_create(struct device *dev)

+ 313 - 106
core/hdd/src/wlan_hdd_stats.c

@@ -180,6 +180,8 @@ static int rssi_mcs_tbl[][12] = {
 	{-76, -73, -71, -68, -64, -60, -59, -58, -53, -51, -46, -42}   /* 80 */
 };
 
+static bool get_station_fw_request_needed = true;
+
 #ifdef WLAN_FEATURE_LINK_LAYER_STATS
 
 /**
@@ -1567,6 +1569,271 @@ static void wlan_hdd_dealloc_ll_stats(void *priv)
 	qdf_list_destroy(&ll_stats_priv->ll_stats_q);
 }
 
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+static void
+hdd_update_station_stats_cached_timestamp(struct hdd_adapter *adapter)
+{
+	adapter->hdd_stats.sta_stats_cached_timestamp =
+				qdf_system_ticks_to_msecs(qdf_system_ticks());
+}
+#else
+static void
+hdd_update_station_stats_cached_timestamp(struct hdd_adapter *adapter)
+{
+}
+#endif /* FEATURE_CLUB_LL_STATS_AND_GET_STATION */
+
+/*
+ * copy_station_stats_to_adapter() - Copy station stats to adapter
+ * @adapter: Pointer to the adapter
+ * @stats: Pointer to the station stats event
+ *
+ * Return: 0 if success, non-zero for failure
+ */
+static int copy_station_stats_to_adapter(struct hdd_adapter *adapter,
+					 struct stats_event *stats)
+{
+	int ret = 0;
+	struct wlan_mlme_nss_chains *dynamic_cfg;
+	uint32_t tx_nss, rx_nss;
+	struct wlan_objmgr_vdev *vdev;
+
+	vdev = hdd_objmgr_get_vdev(adapter);
+	if (!vdev)
+		return -EINVAL;
+
+	/* save summary stats to legacy location */
+	qdf_mem_copy(adapter->hdd_stats.summary_stat.retry_cnt,
+		     stats->vdev_summary_stats[0].stats.retry_cnt,
+		     sizeof(adapter->hdd_stats.summary_stat.retry_cnt));
+	qdf_mem_copy(
+		adapter->hdd_stats.summary_stat.multiple_retry_cnt,
+		stats->vdev_summary_stats[0].stats.multiple_retry_cnt,
+		sizeof(adapter->hdd_stats.summary_stat.multiple_retry_cnt));
+	qdf_mem_copy(adapter->hdd_stats.summary_stat.tx_frm_cnt,
+		     stats->vdev_summary_stats[0].stats.tx_frm_cnt,
+		     sizeof(adapter->hdd_stats.summary_stat.tx_frm_cnt));
+	qdf_mem_copy(adapter->hdd_stats.summary_stat.fail_cnt,
+		     stats->vdev_summary_stats[0].stats.fail_cnt,
+		     sizeof(adapter->hdd_stats.summary_stat.fail_cnt));
+	adapter->hdd_stats.summary_stat.snr =
+			stats->vdev_summary_stats[0].stats.snr;
+	adapter->hdd_stats.summary_stat.rssi =
+			stats->vdev_summary_stats[0].stats.rssi;
+	adapter->hdd_stats.summary_stat.rx_frm_cnt =
+			stats->vdev_summary_stats[0].stats.rx_frm_cnt;
+	adapter->hdd_stats.summary_stat.frm_dup_cnt =
+			stats->vdev_summary_stats[0].stats.frm_dup_cnt;
+	adapter->hdd_stats.summary_stat.rts_fail_cnt =
+			stats->vdev_summary_stats[0].stats.rts_fail_cnt;
+	adapter->hdd_stats.summary_stat.ack_fail_cnt =
+			stats->vdev_summary_stats[0].stats.ack_fail_cnt;
+	adapter->hdd_stats.summary_stat.rts_succ_cnt =
+			stats->vdev_summary_stats[0].stats.rts_succ_cnt;
+	adapter->hdd_stats.summary_stat.rx_discard_cnt =
+			stats->vdev_summary_stats[0].stats.rx_discard_cnt;
+	adapter->hdd_stats.summary_stat.rx_error_cnt =
+			stats->vdev_summary_stats[0].stats.rx_error_cnt;
+	adapter->hdd_stats.peer_stats.rx_count =
+			stats->peer_adv_stats->rx_count;
+	adapter->hdd_stats.peer_stats.rx_bytes =
+			stats->peer_adv_stats->rx_bytes;
+	adapter->hdd_stats.peer_stats.fcs_count =
+			stats->peer_adv_stats->fcs_count;
+
+	dynamic_cfg = mlme_get_dynamic_vdev_config(vdev);
+	if (!dynamic_cfg) {
+		hdd_err("nss chain dynamic config NULL");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	switch (hdd_conn_get_connected_band(&adapter->session.station)) {
+	case BAND_2G:
+		tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_2GHZ];
+		rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_2GHZ];
+		break;
+	case BAND_5G:
+		tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_5GHZ];
+		rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_5GHZ];
+		break;
+	default:
+		tx_nss = wlan_vdev_mlme_get_nss(vdev);
+		rx_nss = wlan_vdev_mlme_get_nss(vdev);
+	}
+
+	/* Intersection of self and AP's NSS capability */
+	if (tx_nss > wlan_vdev_mlme_get_nss(vdev))
+		tx_nss = wlan_vdev_mlme_get_nss(vdev);
+
+	if (rx_nss > wlan_vdev_mlme_get_nss(vdev))
+		rx_nss = wlan_vdev_mlme_get_nss(vdev);
+
+	/* save class a stats to legacy location */
+	adapter->hdd_stats.class_a_stat.tx_nss = tx_nss;
+	adapter->hdd_stats.class_a_stat.rx_nss = rx_nss;
+	adapter->hdd_stats.class_a_stat.tx_rate = stats->tx_rate;
+	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, stats->tx_rate_flags,
+				&adapter->hdd_stats.class_a_stat.tx_nss,
+				&adapter->hdd_stats.class_a_stat.tx_dcm,
+				&adapter->hdd_stats.class_a_stat.tx_gi,
+				&adapter->hdd_stats.class_a_stat.
+				tx_mcs_rate_flags);
+
+	adapter->hdd_stats.class_a_stat.rx_mcs_index =
+		sme_get_mcs_idx(stats->rx_rate, stats->tx_rate_flags,
+				&adapter->hdd_stats.class_a_stat.rx_nss,
+				&adapter->hdd_stats.class_a_stat.rx_dcm,
+				&adapter->hdd_stats.class_a_stat.rx_gi,
+				&adapter->hdd_stats.class_a_stat.
+				rx_mcs_rate_flags);
+
+	/* save per chain rssi to legacy location */
+	qdf_mem_copy(adapter->hdd_stats.per_chain_rssi_stats.rssi,
+		     stats->vdev_chain_rssi[0].chain_rssi,
+		     sizeof(stats->vdev_chain_rssi[0].chain_rssi));
+	adapter->hdd_stats.bcn_protect_stats = stats->bcn_protect_stats;
+out:
+	hdd_objmgr_put_vdev(vdev);
+	return ret;
+}
+
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+/**
+ * cache_station_stats_cb() - cache_station_stats_cb callback function
+ * @ev: station stats buffer
+ * @cookie: cookie that contains the address of the adapter corresponding to
+ *          the request
+ *
+ * Return: None
+ */
+static void cache_station_stats_cb(struct stats_event *ev, void *cookie)
+{
+	struct hdd_adapter *adapter = cookie;
+	struct hdd_context *hdd_ctx = adapter->hdd_ctx;
+	uint8_t vdev_id = adapter->vdev_id;
+
+	hdd_for_each_adapter_dev_held(hdd_ctx, adapter) {
+		if (adapter->vdev_id != vdev_id) {
+			dev_put(adapter->dev);
+			continue;
+		}
+		copy_station_stats_to_adapter(adapter, ev);
+		/* dev_put has to be done here */
+		dev_put(adapter->dev);
+		break;
+	}
+}
+
+static void
+wlan_hdd_set_station_stats_request_pending(struct hdd_adapter *adapter)
+{
+	struct wlan_objmgr_peer *peer;
+	struct request_info info = {0};
+	struct wlan_objmgr_vdev *vdev;
+
+	if (!adapter->hdd_ctx->is_get_station_clubbed_in_ll_stats_req)
+		return;
+
+	vdev = hdd_objmgr_get_vdev(adapter);
+	if (!vdev)
+		return;
+
+	if (adapter->hdd_stats.is_ll_stats_req_in_progress) {
+		hdd_err("Previous ll_stats request is in progress");
+		goto get_station_stats_fail;
+	}
+
+	adapter->hdd_stats.is_ll_stats_req_in_progress = true;
+
+	info.cookie = adapter;
+	info.u.get_station_stats_cb = cache_station_stats_cb;
+	info.vdev_id = adapter->vdev_id;
+	info.pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev));
+	peer = wlan_objmgr_vdev_try_get_bsspeer(vdev, WLAN_OSIF_ID);
+	if (!peer) {
+		osif_err("peer is null");
+		goto get_station_stats_fail;
+	}
+
+	qdf_mem_copy(info.peer_mac_addr, peer->macaddr, QDF_MAC_ADDR_SIZE);
+
+	wlan_objmgr_peer_release_ref(peer, WLAN_OSIF_ID);
+
+	ucfg_mc_cp_stats_set_pending_req(wlan_vdev_get_psoc(vdev),
+					 TYPE_STATION_STATS, &info);
+
+get_station_stats_fail:
+	hdd_objmgr_put_vdev(vdev);
+}
+
+static void
+wlan_hdd_reset_station_stats_request_pending(struct wlan_objmgr_psoc *psoc,
+					     struct hdd_adapter *adapter)
+{
+	QDF_STATUS status;
+	struct request_info last_req = {0};
+	bool pending = false;
+
+	if (!adapter->hdd_ctx->is_get_station_clubbed_in_ll_stats_req)
+		return;
+
+	adapter->hdd_stats.is_ll_stats_req_in_progress = false;
+
+	status = ucfg_mc_cp_stats_get_pending_req(psoc, TYPE_STATION_STATS,
+						  &last_req);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("ucfg_mc_cp_stats_get_pending_req failed");
+		return;
+	}
+
+	ucfg_mc_cp_stats_reset_pending_req(psoc, TYPE_STATION_STATS,
+					   &last_req, &pending);
+}
+
+static QDF_STATUS wlan_hdd_stats_request_needed(struct hdd_adapter *adapter)
+{
+	if (adapter->device_mode != QDF_STA_MODE)
+		return QDF_STATUS_SUCCESS;
+
+	if (!adapter->hdd_ctx->config) {
+		hdd_err("Invalid hdd config");
+		return QDF_STATUS_E_INVAL;
+	}
+	if (adapter->hdd_ctx->is_get_station_clubbed_in_ll_stats_req) {
+		uint32_t stats_cached_duration;
+
+		stats_cached_duration =
+				qdf_system_ticks_to_msecs(qdf_system_ticks()) -
+				adapter->hdd_stats.sta_stats_cached_timestamp;
+		if (stats_cached_duration <=
+			adapter->hdd_ctx->config->sta_stats_cache_expiry_time)
+			return QDF_STATUS_E_ALREADY;
+	}
+	return QDF_STATUS_SUCCESS;
+}
+
+#else
+static void
+wlan_hdd_set_station_stats_request_pending(struct hdd_adapter *adapter)
+{
+}
+
+static void
+wlan_hdd_reset_station_stats_request_pending(struct wlan_objmgr_psoc *psoc,
+					     struct hdd_adapter *adapter)
+{
+}
+
+static QDF_STATUS wlan_hdd_stats_request_needed(struct hdd_adapter *adapter)
+{
+	return QDF_STATUS_SUCCESS;
+}
+#endif /* FEATURE_CLUB_LL_STATS_AND_GET_STATION */
+
 static int wlan_hdd_send_ll_stats_req(struct hdd_adapter *adapter,
 				      tSirLLStatsGetReq *req)
 {
@@ -1584,11 +1851,15 @@ static int wlan_hdd_send_ll_stats_req(struct hdd_adapter *adapter,
 		.dealloc = wlan_hdd_dealloc_ll_stats,
 	};
 
-	hdd_enter();
+	hdd_enter_dev(adapter->dev);
+
+	wlan_hdd_set_station_stats_request_pending(adapter);
 
 	request = osif_request_alloc(&params);
 	if (!request) {
 		hdd_err("Request Allocation Failure");
+		wlan_hdd_reset_station_stats_request_pending(hdd_ctx->psoc,
+							     adapter);
 		return -ENOMEM;
 	}
 
@@ -1601,11 +1872,12 @@ static int wlan_hdd_send_ll_stats_req(struct hdd_adapter *adapter,
 	qdf_spinlock_create(&priv->ll_stats_lock);
 	qdf_list_create(&priv->ll_stats_q, HDD_LINK_STATS_MAX);
 
-	if (QDF_STATUS_SUCCESS !=
-			sme_ll_stats_get_req(hdd_ctx->mac_handle, req,
-					     cookie)) {
+	status = sme_ll_stats_get_req(hdd_ctx->mac_handle, req, cookie);
+	if (QDF_IS_STATUS_ERROR(status)) {
 		hdd_err("sme_ll_stats_get_req Failed");
-		ret = -EINVAL;
+		ret = qdf_status_to_os_return(status);
+		wlan_hdd_reset_station_stats_request_pending(hdd_ctx->psoc,
+							     adapter);
 		goto exit;
 	}
 	ret = osif_request_wait_for_response(request);
@@ -1616,6 +1888,10 @@ static int wlan_hdd_send_ll_stats_req(struct hdd_adapter *adapter,
 		priv->request_bitmap = 0;
 		qdf_spin_unlock(&priv->ll_stats_lock);
 		ret = -ETIMEDOUT;
+		wlan_hdd_reset_station_stats_request_pending(hdd_ctx->psoc,
+							     adapter);
+	} else {
+		hdd_update_station_stats_cached_timestamp(adapter);
 	}
 	qdf_spin_lock(&priv->ll_stats_lock);
 	status = qdf_list_remove_front(&priv->ll_stats_q, &ll_node);
@@ -1643,9 +1919,10 @@ int wlan_hdd_ll_stats_get(struct hdd_adapter *adapter, uint32_t req_id,
 {
 	int errno;
 	tSirLLStatsGetReq get_req;
-	struct hdd_station_ctx *hddstactx = WLAN_HDD_GET_STATION_CTX_PTR(adapter);
+	struct hdd_station_ctx *hddstactx =
+					WLAN_HDD_GET_STATION_CTX_PTR(adapter);
 
-	hdd_enter();
+	hdd_enter_dev(adapter->dev);
 
 	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
 		hdd_warn("Command not allowed in FTM mode");
@@ -3239,6 +3516,12 @@ int wlan_hdd_cfg80211_ll_stats_ext_set_param(struct wiphy *wiphy,
 
 	return errno;
 }
+
+#else
+static QDF_STATUS wlan_hdd_stats_request_needed(struct hdd_adapter *adapter)
+{
+	return QDF_STATUS_SUCCESS;
+}
 #endif /* WLAN_FEATURE_LINK_LAYER_STATS */
 
 #ifdef WLAN_FEATURE_STATS_EXT
@@ -5256,8 +5539,10 @@ static int _wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
 					  struct station_info *sinfo)
 {
 	struct hdd_context *hdd_ctx = wiphy_priv(wiphy);
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
 	qdf_device_t qdf_ctx = cds_get_context(QDF_MODULE_ID_QDF_DEVICE);
 	int errno;
+	QDF_STATUS status;
 
 	errno = wlan_hdd_validate_context(hdd_ctx);
 	if (errno)
@@ -5266,13 +5551,26 @@ static int _wlan_hdd_cfg80211_get_station(struct wiphy *wiphy,
 	if (!qdf_ctx)
 		return -EINVAL;
 
-	errno = wlan_hdd_qmi_get_sync_resume(hdd_ctx, qdf_ctx->dev);
-	if (errno)
-		return errno;
+	status = wlan_hdd_stats_request_needed(adapter);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		if (status == QDF_STATUS_E_ALREADY)
+			get_station_fw_request_needed = false;
+		else
+			return -EINVAL;
+	}
+
+	if (get_station_fw_request_needed) {
+		errno = wlan_hdd_qmi_get_sync_resume(hdd_ctx, qdf_ctx->dev);
+		if (errno)
+			return errno;
+	}
 
 	errno = __wlan_hdd_cfg80211_get_station(wiphy, dev, mac, sinfo);
 
-	wlan_hdd_qmi_put_suspend(hdd_ctx, qdf_ctx->dev);
+	if (get_station_fw_request_needed)
+		wlan_hdd_qmi_put_suspend(hdd_ctx, qdf_ctx->dev);
+
+	get_station_fw_request_needed = true;
 
 	return errno;
 }
@@ -6133,10 +6431,11 @@ int wlan_hdd_get_station_stats(struct hdd_adapter *adapter)
 {
 	int ret = 0;
 	struct stats_event *stats;
-	struct wlan_mlme_nss_chains *dynamic_cfg;
-	uint32_t tx_nss, rx_nss;
 	struct wlan_objmgr_vdev *vdev;
 
+	if (!get_station_fw_request_needed)
+		return 0;
+
 	vdev = hdd_objmgr_get_vdev(adapter);
 	if (!vdev)
 		return -EINVAL;
@@ -6147,99 +6446,7 @@ int wlan_hdd_get_station_stats(struct hdd_adapter *adapter)
 		goto out;
 	}
 
-	/* save summary stats to legacy location */
-	qdf_mem_copy(adapter->hdd_stats.summary_stat.retry_cnt,
-		stats->vdev_summary_stats[0].stats.retry_cnt,
-		sizeof(adapter->hdd_stats.summary_stat.retry_cnt));
-	qdf_mem_copy(adapter->hdd_stats.summary_stat.multiple_retry_cnt,
-		stats->vdev_summary_stats[0].stats.multiple_retry_cnt,
-		sizeof(adapter->hdd_stats.summary_stat.multiple_retry_cnt));
-	qdf_mem_copy(adapter->hdd_stats.summary_stat.tx_frm_cnt,
-		stats->vdev_summary_stats[0].stats.tx_frm_cnt,
-		sizeof(adapter->hdd_stats.summary_stat.tx_frm_cnt));
-	qdf_mem_copy(adapter->hdd_stats.summary_stat.fail_cnt,
-		stats->vdev_summary_stats[0].stats.fail_cnt,
-		sizeof(adapter->hdd_stats.summary_stat.fail_cnt));
-	adapter->hdd_stats.summary_stat.snr =
-		stats->vdev_summary_stats[0].stats.snr;
-	adapter->hdd_stats.summary_stat.rssi =
-		stats->vdev_summary_stats[0].stats.rssi;
-	adapter->hdd_stats.summary_stat.rx_frm_cnt =
-		stats->vdev_summary_stats[0].stats.rx_frm_cnt;
-	adapter->hdd_stats.summary_stat.frm_dup_cnt =
-		stats->vdev_summary_stats[0].stats.frm_dup_cnt;
-	adapter->hdd_stats.summary_stat.rts_fail_cnt =
-		stats->vdev_summary_stats[0].stats.rts_fail_cnt;
-	adapter->hdd_stats.summary_stat.ack_fail_cnt =
-		stats->vdev_summary_stats[0].stats.ack_fail_cnt;
-	adapter->hdd_stats.summary_stat.rts_succ_cnt =
-		stats->vdev_summary_stats[0].stats.rts_succ_cnt;
-	adapter->hdd_stats.summary_stat.rx_discard_cnt =
-		stats->vdev_summary_stats[0].stats.rx_discard_cnt;
-	adapter->hdd_stats.summary_stat.rx_error_cnt =
-		stats->vdev_summary_stats[0].stats.rx_error_cnt;
-	adapter->hdd_stats.peer_stats.rx_count =
-		stats->peer_adv_stats->rx_count;
-	adapter->hdd_stats.peer_stats.rx_bytes =
-		stats->peer_adv_stats->rx_bytes;
-	adapter->hdd_stats.peer_stats.fcs_count =
-		stats->peer_adv_stats->fcs_count;
-
-	dynamic_cfg = mlme_get_dynamic_vdev_config(vdev);
-	if (!dynamic_cfg) {
-		hdd_err("nss chain dynamic config NULL");
-		wlan_cfg80211_mc_cp_stats_free_stats_event(stats);
-		ret = -EINVAL;
-		goto out;
-	}
-
-	switch (hdd_conn_get_connected_band(&adapter->session.station)) {
-	case BAND_2G:
-		tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_2GHZ];
-		rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_2GHZ];
-		break;
-	case BAND_5G:
-		tx_nss = dynamic_cfg->tx_nss[NSS_CHAINS_BAND_5GHZ];
-		rx_nss = dynamic_cfg->rx_nss[NSS_CHAINS_BAND_5GHZ];
-		break;
-	default:
-		tx_nss = wlan_vdev_mlme_get_nss(vdev);
-		rx_nss = wlan_vdev_mlme_get_nss(vdev);
-	}
-	/* Intersection of self and AP's NSS capability */
-	if (tx_nss > wlan_vdev_mlme_get_nss(vdev))
-		tx_nss = wlan_vdev_mlme_get_nss(vdev);
-
-	if (rx_nss > wlan_vdev_mlme_get_nss(vdev))
-		rx_nss = wlan_vdev_mlme_get_nss(vdev);
-
-	/* save class a stats to legacy location */
-	adapter->hdd_stats.class_a_stat.tx_nss = tx_nss;
-	adapter->hdd_stats.class_a_stat.rx_nss = rx_nss;
-	adapter->hdd_stats.class_a_stat.tx_rate = stats->tx_rate;
-	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, stats->tx_rate_flags,
-				&adapter->hdd_stats.class_a_stat.tx_nss,
-				&adapter->hdd_stats.class_a_stat.tx_dcm,
-				&adapter->hdd_stats.class_a_stat.tx_gi,
-				&adapter->hdd_stats.class_a_stat.
-					tx_mcs_rate_flags);
-
-	adapter->hdd_stats.class_a_stat.rx_mcs_index =
-		sme_get_mcs_idx(stats->rx_rate, stats->tx_rate_flags,
-				&adapter->hdd_stats.class_a_stat.rx_nss,
-				&adapter->hdd_stats.class_a_stat.rx_dcm,
-				&adapter->hdd_stats.class_a_stat.rx_gi,
-				&adapter->hdd_stats.class_a_stat.
-					rx_mcs_rate_flags);
-
-	/* save per chain rssi to legacy location */
-	qdf_mem_copy(adapter->hdd_stats.per_chain_rssi_stats.rssi,
-		     stats->vdev_chain_rssi[0].chain_rssi,
-		     sizeof(stats->vdev_chain_rssi[0].chain_rssi));
-	adapter->hdd_stats.bcn_protect_stats = stats->bcn_protect_stats;
+	copy_station_stats_to_adapter(adapter, stats);
 	wlan_cfg80211_mc_cp_stats_free_stats_event(stats);
 
 out:

+ 4 - 0
core/hdd/src/wlan_hdd_stats.h

@@ -34,8 +34,12 @@
 
 #define DATA_RATE_11AC_MCS_MASK    0x03
 
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
 /* LL stats get request time out value */
+#define WLAN_WAIT_TIME_LL_STATS 2000
+#else
 #define WLAN_WAIT_TIME_LL_STATS 800
+#endif
 
 #define WLAN_HDD_TGT_NOISE_FLOOR_DBM     (-96)
 

+ 5 - 0
core/wma/inc/wma_tgt_cfg.h

@@ -45,6 +45,8 @@
  * @bcn_reception_stats: Beacon Reception stats capability
  * @is_roam_scan_ch_to_host: Get roam scan channels from fw supported
  * @ll_stats_per_chan_rx_tx_time: Per channel tx and rx time support in ll stats
+ * @is_get_station_clubbed_in_ll_stats_req: Get station req support within ll
+ *                                          stats req
  */
 struct wma_tgt_services {
 	uint32_t sta_power_save;
@@ -79,6 +81,9 @@ struct wma_tgt_services {
 	bool bcn_reception_stats;
 	bool is_roam_scan_ch_to_host;
 	bool ll_stats_per_chan_rx_tx_time;
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+	bool is_get_station_clubbed_in_ll_stats_req;
+#endif
 };
 
 /**

+ 19 - 0
core/wma/src/wma_main.c

@@ -2822,6 +2822,23 @@ static inline void wma_trace_init(void)
 }
 #endif
 
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+static void wma_get_service_cap_club_get_sta_in_ll_stats_req(
+					struct wmi_unified *wmi_handle,
+					struct wma_tgt_services *cfg)
+{
+	cfg->is_get_station_clubbed_in_ll_stats_req =
+		wmi_service_enabled(wmi_handle,
+				    wmi_service_get_station_in_ll_stats_req);
+}
+#else
+static void wma_get_service_cap_club_get_sta_in_ll_stats_req(
+					struct wmi_unified *wmi_handle,
+					struct wma_tgt_services *cfg)
+{
+}
+#endif /* FEATURE_CLUB_LL_STATS_AND_GET_STATION */
+
 /**
  * wma_open() - Allocate wma context and initialize it.
  * @cds_context:  cds context
@@ -4647,6 +4664,8 @@ static inline void wma_update_target_services(struct wmi_unified *wmi_handle,
 	cfg->ll_stats_per_chan_rx_tx_time =
 		wmi_service_enabled(wmi_handle,
 				    wmi_service_ll_stats_per_chan_rx_tx_time);
+
+	wma_get_service_cap_club_get_sta_in_ll_stats_req(wmi_handle, cfg);
 }
 
 /**

+ 31 - 7
core/wma/src/wma_utils.c

@@ -2341,27 +2341,27 @@ void wma_register_ll_stats_event_handler(tp_wma_handle wma_handle)
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 				wmi_iface_link_stats_event_id,
 				wma_unified_link_iface_stats_event_handler,
-				WMA_RX_SERIALIZER_CTX);
+				WMA_RX_WORK_CTX);
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 				wmi_peer_link_stats_event_id,
 				wma_unified_link_peer_stats_event_handler,
-				WMA_RX_SERIALIZER_CTX);
+				WMA_RX_WORK_CTX);
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 				wmi_radio_link_stats_link,
 				wma_unified_link_radio_stats_event_handler,
-				WMA_RX_SERIALIZER_CTX);
+				WMA_RX_WORK_CTX);
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 			wmi_radio_tx_power_level_stats_event_id,
 			wma_unified_radio_tx_power_level_stats_event_handler,
-			WMA_RX_SERIALIZER_CTX);
+			WMA_RX_WORK_CTX);
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 					   wmi_peer_sta_ps_statechg_event_id,
 					   wma_peer_ps_evt_handler,
-					   WMA_RX_SERIALIZER_CTX);
+					   WMA_RX_WORK_CTX);
 	wmi_unified_register_event_handler(wma_handle->wmi_handle,
 					   wmi_report_stats_event_id,
 					   wma_ll_stats_evt_handler,
-					   WMA_RX_SERIALIZER_CTX);
+					   WMA_RX_WORK_CTX);
 
 }
 
@@ -2440,6 +2440,30 @@ QDF_STATUS wma_process_ll_stats_set_req(tp_wma_handle wma,
 	return QDF_STATUS_SUCCESS;
 }
 
+#ifdef FEATURE_CLUB_LL_STATS_AND_GET_STATION
+static QDF_STATUS
+wma_send_ll_stats_get_cmd(tp_wma_handle wma_handle,
+			  struct ll_stats_get_params *cmd)
+{
+	if (!(cfg_get(wma_handle->psoc, CFG_CLUB_LL_STA_AND_GET_STATION) &&
+	      wmi_service_enabled(wma_handle->wmi_handle,
+				  wmi_service_get_station_in_ll_stats_req)))
+		return wmi_unified_process_ll_stats_get_cmd(
+						wma_handle->wmi_handle, cmd);
+
+	return wmi_process_unified_ll_stats_get_sta_cmd(
+						wma_handle->wmi_handle, cmd);
+}
+#else
+static QDF_STATUS
+wma_send_ll_stats_get_cmd(tp_wma_handle wma_handle,
+			  struct ll_stats_get_params *cmd)
+{
+	return wmi_unified_process_ll_stats_get_cmd(wma_handle->wmi_handle,
+						    cmd);
+}
+#endif
+
 QDF_STATUS wma_process_ll_stats_get_req(tp_wma_handle wma,
 				 const tpSirLLStatsGetReq getReq)
 {
@@ -2473,7 +2497,7 @@ QDF_STATUS wma_process_ll_stats_get_req(tp_wma_handle wma,
 		return QDF_STATUS_E_FAILURE;
 	}
 	qdf_mem_copy(cmd.peer_macaddr.bytes, addr, QDF_MAC_ADDR_SIZE);
-	ret = wmi_unified_process_ll_stats_get_cmd(wma->wmi_handle, &cmd);
+	ret = wma_send_ll_stats_get_cmd(wma, &cmd);
 	if (ret) {
 		wma_err("Failed to send get link stats request");
 		return QDF_STATUS_E_FAILURE;