Răsfoiți Sursa

qcacld-3.0: Add support to handle RRM sta stats req

Add support to handle RRM Station statistics request (type == 7)
from AP. Based on the received group id, collect the corresponding
stats and send the RRM station statistics response to AP.

Send Report with incapable/Refused bit in below cases:
a.) Meas duration > threshold which is 10 seconds
b.) Groupid apart from 0, 1 or 10.
c.) Failure to start Timer.
d.) Response not received from FW for previous request.
e.) Another RRM STA STATS Request in progress.
f.) Another CP stats request in progress.

Change-Id: If93e9c51363daf9704a14df5822c8f7bfbd4b216
CRs-Fixed: 3583973
Sheenam Monga 1 an în urmă
părinte
comite
7aa3e49d6d

+ 2 - 2
components/cp_stats/dispatcher/src/wlan_cp_stats_mc_tgt_api.c

@@ -672,8 +672,8 @@ tgt_mc_infra_cp_stats_extract_twt_stats(struct wlan_objmgr_psoc *psoc,
 					struct infra_cp_stats_event *ev)
 {
 	QDF_STATUS status;
-	get_infra_cp_stats_cb resp_cb;
-	void *context;
+	get_infra_cp_stats_cb resp_cb = NULL;
+	void *context = NULL;
 
 	status = wlan_cp_stats_infra_cp_get_context(psoc, &resp_cb, &context);
 	if (QDF_IS_STATUS_ERROR(status)) {

+ 2 - 0
core/mac/inc/ani_global.h

@@ -285,6 +285,8 @@ typedef struct sLimTimers {
 	/* SAE authentication related timer */
 	TX_TIMER sae_auth_timer;
 
+	/* RRM sta stats response related timer */
+	TX_TIMER rrm_sta_stats_resp_timer;
 /* ********************TIMER SECTION ENDS************************************************** */
 /* ALL THE FIELDS BELOW THIS CAN BE ZEROED OUT in lim_initialize */
 /* **************************************************************************************** */

+ 97 - 0
core/mac/inc/sir_mac_prot_def.h

@@ -1363,6 +1363,102 @@ struct chan_load_report {
 	uint8_t chan_load;
 };
 
+/**
+ * sta_statistics_group_id - RRM STA STATISTICS TYPE related Refer IEEE
+ * P802.11-REVme/D2.1, January 2023, Table 9-144
+ * @STA_STAT_GROUP_ID_COUNTER_STATS: group id for counter stats
+ * @STA_STAT_GROUP_ID_MAC_STATS: group id for mac stats
+ * @STA_STAT_GROUP_ID_QOS_STATS: group id for qos stats
+ * @STA_STAT_GROUP_ID_DELAY_STATS: group id delay stats
+ */
+enum sta_statistics_group_id {
+	STA_STAT_GROUP_ID_COUNTER_STATS = 0,
+	STA_STAT_GROUP_ID_MAC_STATS = 1,
+	STA_STAT_GROUP_ID_QOS_STATS = 2,
+	STA_STAT_GROUP_ID_DELAY_STATS = 10,
+};
+
+/**
+ * counter_stats - structure to hold stats of group id 0
+ * @transmitted_fragment_count: transmitted fragment count
+ * @group_transmitted_frame_count: group transmitted frame count
+ * @failed_count: failed count
+ * @group_received_frame_count: group received frame count
+ * @fcs_error_count: face error count
+ * @transmitted_frame_count: transmitted frame count
+ * @received_fragment_count: received fragment count
+ */
+struct counter_stats {
+	uint32_t transmitted_fragment_count;
+	uint32_t group_transmitted_frame_count;
+	uint32_t failed_count;
+	uint32_t group_received_frame_count;
+	uint32_t fcs_error_count;
+	uint32_t transmitted_frame_count;
+	uint32_t received_fragment_count;
+};
+
+/**
+ * mac_stats - struct to hold group id 1 stats
+ * @retry_count: retry count
+ * @multiple_retry_count: multiple retry count
+ * @frame_duplicate_count: frame duplicate count
+ * @rts_success_count: rts success count
+ * @rts_failure_count: rts failure count
+ * @ack_failure_count: ack failure count
+ */
+struct mac_stats {
+	uint32_t retry_count;
+	uint32_t multiple_retry_count;
+	uint32_t frame_duplicate_count;
+	uint32_t rts_success_count;
+	uint32_t rts_failure_count;
+	uint32_t ack_failure_count;
+};
+
+/**
+ * struct access_delay_stats - struct for group id 10 stats
+ * @ap_average_access_delay: ap average access delay
+ * @average_access_delay_besteffort: access delay best effort
+ * @average_access_delay_background: average access delay background
+ * @average_access_delay_video: average access delay video
+ * @average_access_delay_voice: average access delay voice
+ * station_count: station count
+ * channel_utilization: channel utilization
+ */
+struct access_delay_stats {
+	uint8_t ap_average_access_delay;
+	uint8_t average_access_delay_besteffort;
+	uint8_t average_access_delay_background;
+	uint8_t average_access_delay_video;
+	uint8_t average_access_delay_voice;
+	uint16_t station_count;
+	uint8_t channel_utilization;
+};
+
+/**
+ * union stats_group_data - stats data for provided group id
+ * @counter stats - stats for group id 0
+ * @mac_stats - stats for group id 1
+ */
+union stats_group_data {
+	struct counter_stats counter_stats;
+	struct mac_stats mac_stats;
+	struct access_delay_stats access_delay_stats;
+};
+
+/**
+ * struct statistics_report - To store sta statistics report
+ * @meas_duration: measurement duration
+ * @group id: stats group id
+ * @group stats: stats data
+ */
+struct statistics_report {
+	uint8_t meas_duration;
+	uint8_t group_id;
+	union stats_group_data group_stats;
+};
+
 typedef struct sSirMacRadioMeasureReport {
 	uint8_t token;
 	uint8_t refused;
@@ -1371,6 +1467,7 @@ typedef struct sSirMacRadioMeasureReport {
 	union {
 		tSirMacBeaconReport beaconReport;
 		struct chan_load_report channel_load_report;
+		struct statistics_report statistics_report;
 	} report;
 
 } tSirMacRadioMeasureReport, *tpSirMacRadioMeasureReport;

+ 14 - 1
core/mac/src/include/parser_api.h

@@ -823,7 +823,7 @@ populate_dot11f_ext_supp_rates(struct mac_context *mac,
  * @pBeaconReport: Pointer to the Beacon Report structure
  * @is_last_frame: is the current report last or more reports to follow
  *
- * Return: Ret Status
+ * Return: QDF Status
  */
 QDF_STATUS
 populate_dot11f_beacon_report(struct mac_context *mac,
@@ -844,6 +844,19 @@ populate_dot11f_chan_load_report(struct mac_context *mac,
 				 tDot11fIEMeasurementReport *dot11f,
 				 struct chan_load_report *channel_load_report);
 
+/**
+ * populate_dot11f_rrm_sta_stats_report() - Populate RRM STA STATS Report IE
+ * @mac: Pointer to the global MAC context
+ * @pdot11f: Pointer to the measurement report structure
+ * @statistics_report: Pointer to the RRM STA STATS Report structure
+ *
+ * Return: QDF Status
+ */
+QDF_STATUS
+populate_dot11f_rrm_sta_stats_report(
+		struct mac_context *mac, tDot11fIEMeasurementReport *pdot11f,
+		struct statistics_report *statistics_report);
+
 /**
  * \brief Populate a tDot11fIEExtSuppRates
  *

+ 1 - 1
core/mac/src/include/sir_params.h

@@ -709,7 +709,7 @@ enum halmsgtype {
 #define SIR_LIM_WPS_OVERLAP_TIMEOUT      (SIR_LIM_TIMEOUT_MSG_START + 0x1D)
 #define SIR_LIM_FT_PREAUTH_RSP_TIMEOUT   (SIR_LIM_TIMEOUT_MSG_START + 0x1E)
 
-/* currently unused                     (SIR_LIM_TIMEOUT_MSG_START + 0x24) */
+#define SIR_LIM_RRM_STA_STATS_RSP_TIMEOUT    (SIR_LIM_TIMEOUT_MSG_START + 0x24)
 /* currently unused                     (SIR_LIM_TIMEOUT_MSG_START + 0x25) */
 
 #define SIR_LIM_DISASSOC_ACK_TIMEOUT       (SIR_LIM_TIMEOUT_MSG_START + 0x26)

+ 40 - 0
core/mac/src/pe/include/rrm_api.h

@@ -133,4 +133,44 @@ QDF_STATUS rrm_reject_req(tpSirMacRadioMeasureReport *radiomes_report,
 			  uint8_t measurement_type);
 
 void lim_update_rrm_capability(struct mac_context *mac_ctx);
+
+#ifdef WLAN_SUPPORT_INFRA_CTRL_PATH_STATS
+/**
+ * rrm_send_sta_stats_req - Send RRM STA STATS request
+ * @mac: mac context
+ * @session: pe session
+ * @peer_mac: peer mac
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+rrm_send_sta_stats_req(struct mac_context *mac,
+		       struct pe_session *session,
+		       tSirMacAddr peer_mac);
+#else
+static inline QDF_STATUS
+rrm_send_sta_stats_req(struct mac_context *mac,
+		       struct pe_session *session,
+		       tSirMacAddr peer_mac)
+{
+	return QDF_STATUS_E_NOSUPPORT;
+}
+#endif
+
+/**
+ * rrm_process_rrm_sta_stats_request_failure: send RRM STA Stats report with
+ * failure
+ * @mac: mac context
+ * @pe_session: pe session
+ * @peer: peer mac
+ * @status: failure status
+ * @index: index of report
+ *
+ * Return: void
+ */
+void
+rrm_process_rrm_sta_stats_request_failure(struct mac_context *mac,
+					  struct pe_session *pe_session,
+					  tSirMacAddr peer,
+					  tRrmRetStatus status, uint8_t index);
 #endif

+ 17 - 0
core/mac/src/pe/include/rrm_global.h

@@ -259,6 +259,22 @@ typedef struct sRRMCaps {
 	uint8_t reserved:4;
 } tRRMCaps, *tpRRMCaps;
 
+/**
+ * struct rrm_sta_stats - RRM sta stats structure
+ * @rrm_report: rrm_report
+ * @peer: peer address
+ * @index: current req index
+ * @rrm_sta_stats_res_count: sta stats response count
+ * @vdev_id: vdev_id
+ */
+struct rrm_sta_stats {
+	tSirMacRadioMeasureReport rrm_report;
+	tSirMacAddr peer;
+	uint8_t index;
+	uint8_t rrm_sta_stats_res_count;
+	uint8_t vdev_id;
+};
+
 typedef struct sRrmPEContext {
 	uint8_t rrmEnable;
 	/*
@@ -279,6 +295,7 @@ typedef struct sRrmPEContext {
 	tpRRMReq pCurrentReq[MAX_MEASUREMENT_REQUEST];
 	uint32_t beacon_rpt_chan_list[MAX_NUM_CHANNELS];
 	uint8_t beacon_rpt_chan_num;
+	struct rrm_sta_stats rrm_sta_stats;
 } tRrmPEContext, *tpRrmPEContext;
 
 /* 2008 11k spec reference: 18.4.8.5 RCPI Measurement */

+ 1 - 0
core/mac/src/pe/lim/lim_process_message_queue.c

@@ -1855,6 +1855,7 @@ static void lim_process_messages(struct mac_context *mac_ctx,
 	case SIR_LIM_DISASSOC_ACK_TIMEOUT:
 	case SIR_LIM_AUTH_RETRY_TIMEOUT:
 	case SIR_LIM_AUTH_SAE_TIMEOUT:
+	case SIR_LIM_RRM_STA_STATS_RSP_TIMEOUT:
 		/* These timeout messages are handled by MLM sub module */
 		lim_process_mlm_req_messages(mac_ctx, msg);
 		break;

+ 75 - 0
core/mac/src/pe/lim/lim_process_mlm_req_messages.c

@@ -43,12 +43,15 @@
 #include "wlan_mlme_public_struct.h"
 #include "../../core/src/vdev_mgr_ops.h"
 #include "wlan_pmo_ucfg_api.h"
+#include "wlan_cp_stats_utils_api.h"
 #include "wlan_objmgr_vdev_obj.h"
 #include <wlan_cm_api.h>
 #include <lim_mlo.h>
 #include "wlan_mlo_mgr_peer.h"
 #include <son_api.h>
 #include "wifi_pos_pasn_api.h"
+#include "rrm_api.h"
+#include "../../core/src/wlan_cp_stats_obj_mgr_handler.h"
 
 static void lim_process_mlm_auth_req(struct mac_context *, uint32_t *);
 static void lim_process_mlm_assoc_req(struct mac_context *, uint32_t *);
@@ -96,6 +99,75 @@ static void lim_fill_status_code(uint8_t frame_type,
 	}
 }
 
+void lim_process_rrm_sta_stats_rsp_timeout(struct mac_context *mac)
+{
+	struct pe_session *session;
+	tSirMacRadioMeasureReport rrm_report;
+	QDF_STATUS status;
+	uint8_t index;
+	tpRRMReq pcurrent_req = NULL;
+	tRrmRetStatus rrm_status;
+
+	session = pe_find_session_by_session_id(mac,
+		mac->lim.lim_timers.rrm_sta_stats_resp_timer.sessionId);
+	if (!session) {
+		pe_err("Session does not exist for given session id %d",
+		       mac->lim.lim_timers.rrm_sta_stats_resp_timer.sessionId);
+		rrm_cleanup(mac, mac->rrm.rrmPEContext.rrm_sta_stats.index);
+		return;
+	}
+
+	pe_warn("STA STATS RSP timeout vdev_id %d", session->vdev_id);
+	index = mac->rrm.rrmPEContext.rrm_sta_stats.index;
+	pcurrent_req = mac->rrm.rrmPEContext.pCurrentReq[index];
+	if (!pcurrent_req) {
+		pe_err("Current request is NULL for index %d", index);
+		qdf_mem_zero(&mac->rrm.rrmPEContext.rrm_sta_stats,
+			     sizeof(mac->rrm.rrmPEContext.rrm_sta_stats));
+		return;
+	}
+
+	if (!mac->rrm.rrmPEContext.rrm_sta_stats.rrm_sta_stats_res_count) {
+		pe_err("response not received for previous req");
+		rrm_status = eRRM_INCAPABLE;
+		goto err;
+	}
+
+	rrm_report = mac->rrm.rrmPEContext.rrm_sta_stats.rrm_report;
+	switch (rrm_report.report.statistics_report.group_id) {
+	/*
+	 * For Counter stats and Mac stats some stats will be received
+	 * via FW and some via DP. So, same handling is required for both
+	 * cases.
+	 */
+	case STA_STAT_GROUP_ID_COUNTER_STATS:
+	case STA_STAT_GROUP_ID_MAC_STATS:
+		status = wlan_cp_stats_infra_cp_deregister_resp_cb(mac->psoc);
+		if (QDF_IS_STATUS_ERROR(status))
+			pe_err("failed to deregister callback %d", status);
+
+		status =
+			rrm_send_sta_stats_req(
+				mac, session,
+				mac->rrm.rrmPEContext.rrm_sta_stats.peer);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			pe_err("fail to send stats req");
+			rrm_status = eRRM_FAILURE;
+			goto err;
+		}
+		break;
+	case STA_STAT_GROUP_ID_DELAY_STATS:
+		/* TOdo: fetch from scan ie */
+		break;
+	}
+	return;
+err:
+	rrm_process_rrm_sta_stats_request_failure(
+		mac, session, mac->rrm.rrmPEContext.rrm_sta_stats.peer,
+		rrm_status, mac->rrm.rrmPEContext.rrm_sta_stats.index);
+	rrm_cleanup(mac, mac->rrm.rrmPEContext.rrm_sta_stats.index);
+}
+
 void lim_process_sae_auth_timeout(struct mac_context *mac_ctx)
 {
 	struct pe_session *session;
@@ -194,6 +266,9 @@ void lim_process_mlm_req_messages(struct mac_context *mac_ctx,
 	case SIR_LIM_AUTH_SAE_TIMEOUT:
 		lim_process_sae_auth_timeout(mac_ctx);
 		break;
+	case SIR_LIM_RRM_STA_STATS_RSP_TIMEOUT:
+		lim_process_rrm_sta_stats_rsp_timeout(mac_ctx);
+		break;
 	case LIM_MLM_TSPEC_REQ:
 	default:
 		break;

+ 10 - 0
core/mac/src/pe/lim/lim_send_management_frames.c

@@ -5686,6 +5686,16 @@ lim_send_radio_measure_report_action_frame(struct mac_context *mac,
 				pRRMReport[i].refused;
 			frm->MeasurementReport[i].present = 1;
 			break;
+		case SIR_MAC_RRM_STA_STATISTICS_TYPE:
+			populate_dot11f_rrm_sta_stats_report(
+				mac, &frm->MeasurementReport[i],
+				&pRRMReport[i].report.statistics_report);
+			frm->MeasurementReport[i].incapable =
+				pRRMReport[i].incapable;
+			frm->MeasurementReport[i].refused =
+				pRRMReport[i].refused;
+			frm->MeasurementReport[i].present = 1;
+			break;
 		default:
 			frm->MeasurementReport[i].incapable =
 				pRRMReport[i].incapable;

+ 32 - 0
core/mac/src/pe/lim/lim_timer_utils.c

@@ -49,6 +49,12 @@
  */
 #define LIM_AUTH_SAE_TIMER_MS 5000
 
+/*
+ * STA stats resp timer of 10secs. This is required for duration of RRM
+ * STA STATS report response from report request.
+ */
+#define LIM_RRM_STA_STATS_RSP_TIMER_MS 10000
+
 static bool lim_create_non_ap_timers(struct mac_context *mac)
 {
 	uint32_t cfgValue;
@@ -235,10 +241,21 @@ uint32_t lim_create_timers(struct mac_context *mac)
 		goto err_timer;
 	}
 
+	if ((tx_timer_create(mac,
+			     &mac->lim.lim_timers.rrm_sta_stats_resp_timer,
+			     "STA STATS RSP timer",
+			     lim_timer_handler,
+			     SIR_LIM_RRM_STA_STATS_RSP_TIMEOUT,
+			     SYS_MS_TO_TICKS(LIM_RRM_STA_STATS_RSP_TIMER_MS), 0,
+			     TX_NO_ACTIVATE)) != TX_SUCCESS) {
+		pe_err("could not create STA STATS RSP Timer");
+		goto err_timer;
+	}
 	return TX_SUCCESS;
 
 err_timer:
 	lim_delete_timers_host_roam(mac);
+	tx_timer_delete(&mac->lim.lim_timers.rrm_sta_stats_resp_timer);
 	tx_timer_delete(&mac->lim.lim_timers.gLimDeauthAckTimer);
 	tx_timer_delete(&mac->lim.lim_timers.gLimDisassocAckTimer);
 	tx_timer_delete(&mac->lim.lim_timers.gLimUpdateOlbcCacheTimer);
@@ -677,6 +694,21 @@ void lim_deactivate_and_change_timer(struct mac_context *mac, uint32_t timerId)
 			pe_err("unable to change SAE auth timer");
 
 		break;
+	case eLIM_RRM_STA_STATS_RSP_TIMER:
+		if (tx_timer_deactivate
+		   (&mac->lim.lim_timers.rrm_sta_stats_resp_timer)
+		    != TX_SUCCESS)
+			pe_err("Unable to deactivate STA STATS RSP timer");
+
+		/* Change timer to reactivate it in future */
+		val = SYS_MS_TO_TICKS(LIM_RRM_STA_STATS_RSP_TIMER_MS);
+
+		if (tx_timer_change(
+			&mac->lim.lim_timers.rrm_sta_stats_resp_timer,
+			val, 0) != TX_SUCCESS)
+			pe_err("unable to change STA STATS RSP timer");
+
+		break;
 
 	default:
 		/* Invalid timerId. Log error */

+ 3 - 2
core/mac/src/pe/lim/lim_timer_utils.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2011-2014, 2016-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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
@@ -55,7 +55,8 @@ enum limtimertype {
 	eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER,
 	eLIM_INSERT_SINGLESHOT_NOA_TIMER,
 	eLIM_AUTH_RETRY_TIMER,
-	eLIM_AUTH_SAE_TIMER
+	eLIM_AUTH_SAE_TIMER,
+	eLIM_RRM_STA_STATS_RSP_TIMER
 };
 
 #define LIM_DISASSOC_DEAUTH_ACK_TIMEOUT         500

+ 2 - 1
core/mac/src/pe/lim/lim_trace.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2013-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. 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
@@ -64,6 +64,7 @@ static uint8_t *__lim_trace_get_timer_string(uint16_t timerId)
 		CASE_RETURN_STRING(eLIM_DEAUTH_ACK_TIMER);
 		CASE_RETURN_STRING(eLIM_PERIODIC_JOIN_PROBE_REQ_TIMER);
 		CASE_RETURN_STRING(eLIM_AUTH_RETRY_TIMER);
+		CASE_RETURN_STRING(eLIM_RRM_STA_STATS_RSP_TIMER);
 	default:
 		return "UNKNOWN";
 		break;

+ 9 - 0
core/mac/src/pe/lim/lim_types.h

@@ -1624,6 +1624,15 @@ void lim_process_assoc_failure_timeout(struct mac_context *mac_ctx,
  */
 void lim_process_sae_auth_timeout(struct mac_context *mac_ctx);
 
+/**
+ * lim_process_rrm_sta_stats_rsp_timeout() - This function is called to process
+ * sta stats response timeout
+ * @mac_ctx: Pointer to Global MAC structure
+ *
+ * @Return: None
+ */
+void lim_process_rrm_sta_stats_rsp_timeout(struct mac_context *mac_ctx);
+
 /**
  * lim_send_frame() - API to send frame
  * @mac_ctx Pointer to Global MAC structure

+ 7 - 1
core/mac/src/pe/lim/lim_utils.c

@@ -496,8 +496,13 @@ void lim_deactivate_timers(struct mac_context *mac_ctx)
 		/* Cleanup as if SAE auth timer expired */
 		lim_timer_handler(mac_ctx, SIR_LIM_AUTH_SAE_TIMEOUT);
 	}
-
 	tx_timer_deactivate(&lim_timer->sae_auth_timer);
+
+	if (tx_timer_running(&lim_timer->rrm_sta_stats_resp_timer)) {
+		pe_err("sta stats resp timer running call the timeout API");
+		lim_timer_handler(mac_ctx, SIR_LIM_RRM_STA_STATS_RSP_TIMEOUT);
+	}
+	tx_timer_deactivate(&lim_timer->rrm_sta_stats_resp_timer);
 }
 
 void lim_deactivate_timers_for_vdev(struct mac_context *mac_ctx,
@@ -663,6 +668,7 @@ void lim_cleanup_mlm(struct mac_context *mac_ctx)
 		tx_timer_delete(&lim_timer->gLimDeauthAckTimer);
 
 		tx_timer_delete(&lim_timer->sae_auth_timer);
+		tx_timer_delete(&lim_timer->rrm_sta_stats_resp_timer);
 
 		mac_ctx->lim.gLimTimersCreated = 0;
 	}

+ 525 - 2
core/mac/src/pe/rrm/rrm_api.c

@@ -45,6 +45,15 @@
 #include "rrm_api.h"
 #include "wlan_lmac_if_def.h"
 #include "wlan_reg_services_api.h"
+#include "wlan_cp_stats_utils_api.h"
+#include "../../core/src/wlan_cp_stats_obj_mgr_handler.h"
+#include "../../core/src/wlan_cp_stats_defs.h"
+#include "cdp_txrx_host_stats.h"
+
+#define MAX_CTRL_STAT_VDEV_ENTRIES 1
+#define MAX_CTRL_STAT_MAC_ADDR_ENTRIES 1
+#define MAX_RMM_STA_STATS_REQUESTED 2
+#define MAX_MEAS_DURATION_FOR_STA_STATS 10
 
 /* Max passive scan dwell for wide band rrm scan, in milliseconds */
 #define RRM_SCAN_MAX_DWELL_TIME 110
@@ -611,6 +620,506 @@ wlan_diag_log_beacon_rpt_req_event(uint8_t token, uint8_t mode,
 #endif
 
 #define ABS(x)      ((x < 0) ? -x : x)
+
+#ifdef WLAN_SUPPORT_INFRA_CTRL_PATH_STATS
+/**
+ * rrm_update_mac_cp_stats: update stats of rrm structure of mac
+ * @ev: cp stats event
+ * @mac_ctx: mac context
+ *
+ * Return: None
+ */
+static inline void
+rrm_update_mac_cp_stats(struct infra_cp_stats_event *ev,
+			struct mac_context *mac_ctx)
+{
+	tpSirMacRadioMeasureReport rrm_report;
+	struct counter_stats *counter_stats;
+	struct group_id_0 ev_counter_stats;
+	struct mac_stats *mac_stats;
+	struct group_id_1 ev_mac_stats;
+
+	rrm_report = &mac_ctx->rrm.rrmPEContext.rrm_sta_stats.rrm_report;
+
+	counter_stats =
+		&rrm_report->report.statistics_report.group_stats.counter_stats;
+	mac_stats =
+		&rrm_report->report.statistics_report.group_stats.mac_stats;
+
+	ev_counter_stats = ev->sta_stats->group.counter_stats;
+	ev_mac_stats = ev->sta_stats->group.mac_stats;
+
+	switch (rrm_report->report.statistics_report.group_id) {
+	/*
+	 * Assign count diff in mac rrm sta stats report.
+	 * For first event callback stats will be same as
+	 * send by FW because memset is done for mac rrm sta
+	 * stats before sending rrm sta stats request to FW and
+	 * for second request stats will be the diff of stats send
+	 * by FW and previous stats.
+	 */
+	case STA_STAT_GROUP_ID_COUNTER_STATS:
+		counter_stats->group_transmitted_frame_count =
+			ev_counter_stats.group_transmitted_frame_count -
+			counter_stats->group_transmitted_frame_count;
+		counter_stats->failed_count =
+			ev_counter_stats.failed_count -
+			counter_stats->failed_count;
+		counter_stats->group_received_frame_count =
+			ev_counter_stats.group_received_frame_count -
+			counter_stats->group_received_frame_count;
+		counter_stats->fcs_error_count =
+			ev_counter_stats.fcs_error_count -
+			counter_stats->fcs_error_count;
+		counter_stats->transmitted_frame_count =
+			ev_counter_stats.transmitted_frame_count -
+			counter_stats->transmitted_frame_count;
+		break;
+	case STA_STAT_GROUP_ID_MAC_STATS:
+		mac_stats->rts_success_count =
+			ev_mac_stats.rts_success_count -
+			mac_stats->rts_success_count;
+		mac_stats->rts_failure_count =
+			ev_mac_stats.rts_failure_count -
+			mac_stats->rts_failure_count;
+		mac_stats->ack_failure_count =
+			ev_mac_stats.ack_failure_count -
+			mac_stats->ack_failure_count;
+		break;
+	default:
+		pe_debug("group id not supported");
+	}
+	pe_nofl_debug("counter stats: group frame count ( tx %d rx %d ) failed_count %d fcs_error %d tx frame count %d mac stats: rts success count %d rts fail count %d ack fail count %d",
+		      counter_stats->group_transmitted_frame_count,
+		      counter_stats->group_received_frame_count,
+		      counter_stats->failed_count,
+		      counter_stats->fcs_error_count,
+		      counter_stats->transmitted_frame_count,
+		      mac_stats->rts_success_count,
+		      mac_stats->rts_failure_count,
+		      mac_stats->ack_failure_count);
+}
+
+/**
+ * rmm_sta_stats_response_cb: RRM sta stats response callback
+ * @ev: cp stats event
+ * @cookie: NULL
+ *
+ * Return: None
+ */
+static inline
+void rmm_sta_stats_response_cb(struct infra_cp_stats_event *ev, void *cookie)
+{
+	struct mac_context *mac;
+	struct pe_session *session;
+	uint8_t vdev_id, index;
+	QDF_STATUS status;
+	tpRRMReq pcurrent_req;
+	tSirMacRadioMeasureReport *rrm_report;
+
+	mac = cds_get_context(QDF_MODULE_ID_PE);
+	if (!mac)
+		return;
+	index = mac->rrm.rrmPEContext.rrm_sta_stats.index;
+
+	/* Deregister callback registered in request */
+	status = wlan_cp_stats_infra_cp_deregister_resp_cb(mac->psoc);
+	if (QDF_IS_STATUS_ERROR(status))
+		pe_err("failed to deregister callback %d", status);
+
+	pcurrent_req = mac->rrm.rrmPEContext.pCurrentReq[index];
+	if (!pcurrent_req) {
+		pe_err("Current request is NULL");
+		qdf_mem_zero(&mac->rrm.rrmPEContext.rrm_sta_stats,
+			     sizeof(mac->rrm.rrmPEContext.rrm_sta_stats));
+		return;
+	}
+
+	vdev_id = mac->rrm.rrmPEContext.rrm_sta_stats.vdev_id;
+	session = pe_find_session_by_vdev_id(mac, vdev_id);
+	if (!session) {
+		pe_err("Session does not exist for given vdev id %d", vdev_id);
+		rrm_cleanup(mac, mac->rrm.rrmPEContext.rrm_sta_stats.index);
+		return;
+	}
+
+	rrm_report = &mac->rrm.rrmPEContext.rrm_sta_stats.rrm_report;
+	rrm_update_mac_cp_stats(ev, mac);
+
+	/* Update resp counter for every response to find second resp*/
+	mac->rrm.rrmPEContext.rrm_sta_stats.rrm_sta_stats_res_count++;
+	/* Send current stats if meas duration is 0. */
+	if (mac->rrm.rrmPEContext.rrm_sta_stats.rrm_sta_stats_res_count ==
+	    MAX_RMM_STA_STATS_REQUESTED ||
+	   !rrm_report->report.statistics_report.meas_duration) {
+		lim_send_radio_measure_report_action_frame(
+			mac, pcurrent_req->dialog_token, 1, true,
+			&mac->rrm.rrmPEContext.rrm_sta_stats.rrm_report,
+			mac->rrm.rrmPEContext.rrm_sta_stats.peer, session);
+
+		rrm_cleanup(mac, index);
+	}
+}
+
+/**
+ * rrm_update_vdev_stats: Update RRM stats from DP
+ * @rrm_report: RRM Measurement Report
+ * @vdev_id: vdev_id
+ *
+ * Return: QDF STATUS
+ */
+static QDF_STATUS
+rrm_update_vdev_stats(tpSirMacRadioMeasureReport rrm_report, uint8_t vdev_id)
+{
+	struct cdp_vdev_stats *stats;
+	QDF_STATUS status;
+	struct counter_stats *counter_stats;
+	struct mac_stats *mac_stats;
+
+	counter_stats =
+		&rrm_report->report.statistics_report.group_stats.counter_stats;
+	mac_stats =
+		&rrm_report->report.statistics_report.group_stats.mac_stats;
+
+	stats = qdf_mem_malloc(sizeof(*stats));
+	if (!stats)
+		return QDF_STATUS_E_NOMEM;
+
+	status =
+		cdp_host_get_vdev_stats(cds_get_context(QDF_MODULE_ID_SOC),
+					vdev_id, stats, true);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		pe_err("Failed to get stats %d", status);
+		qdf_mem_free(stats);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	pe_nofl_debug("counter stats count: fragment (tx: %d rx: %d) mac stats count: retry : %d multiple retry: %d frame duplicate %d",
+		      stats->tx.fragment_count, stats->rx.fragment_count,
+		      stats->tx.retry_count, stats->tx.multiple_retry_count,
+		      stats->rx.duplicate_count);
+
+	switch (rrm_report->report.statistics_report.group_id) {
+	case STA_STAT_GROUP_ID_COUNTER_STATS:
+		counter_stats->transmitted_fragment_count =
+			stats->tx.fragment_count -
+				counter_stats->transmitted_fragment_count;
+		counter_stats->received_fragment_count =
+			stats->rx.fragment_count -
+				counter_stats->received_fragment_count;
+		break;
+	case STA_STAT_GROUP_ID_MAC_STATS:
+		mac_stats->retry_count =
+			stats->tx.retry_count - mac_stats->retry_count;
+		mac_stats->multiple_retry_count =
+			stats->tx.multiple_retry_count -
+				mac_stats->multiple_retry_count;
+		mac_stats->frame_duplicate_count =
+			stats->rx.duplicate_count -
+				mac_stats->frame_duplicate_count;
+		break;
+	}
+	qdf_mem_free(stats);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+rrm_send_sta_stats_req(struct mac_context *mac,
+		       struct pe_session *session,
+		       tSirMacAddr peer_mac)
+{
+	struct infra_cp_stats_cmd_info info = {0};
+	get_infra_cp_stats_cb resp_cb = NULL;
+	void *context;
+	QDF_STATUS status;
+	tpSirMacRadioMeasureReport report;
+
+	status = wlan_cp_stats_infra_cp_get_context(mac->psoc, &resp_cb,
+						    &context);
+	if (resp_cb) {
+		pe_err("another request already in progress");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	info.request_cookie = NULL;
+	info.stats_id = TYPE_REQ_CTRL_PATH_RRM_STA_STAT;
+	info.action = ACTION_REQ_CTRL_PATH_STAT_GET;
+	info.infra_cp_stats_resp_cb = rmm_sta_stats_response_cb;
+	info.num_pdev_ids = 0;
+	info.num_vdev_ids = MAX_CTRL_STAT_VDEV_ENTRIES;
+	info.vdev_id[0] = wlan_vdev_get_id(session->vdev);
+	info.num_mac_addr_list = MAX_CTRL_STAT_MAC_ADDR_ENTRIES;
+	info.num_pdev_ids = 0;
+	/*
+	 * FW doesn't send vdev id in response path.
+	 * So, vdev id will be used to get session
+	 * in callback.
+	 */
+	mac->rrm.rrmPEContext.rrm_sta_stats.vdev_id =
+					wlan_vdev_get_id(session->vdev);
+	qdf_mem_copy(&info.peer_mac_addr[0], peer_mac, QDF_MAC_ADDR_SIZE);
+	report = &mac->rrm.rrmPEContext.rrm_sta_stats.rrm_report;
+
+	status = rrm_update_vdev_stats(report, wlan_vdev_get_id(session->vdev));
+	if (QDF_IS_STATUS_ERROR(status)) {
+		pe_err("Failed to register resp callback: %d", status);
+		return status;
+	}
+
+	status =  wlan_cp_stats_infra_cp_register_resp_cb(mac->psoc, &info);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		pe_err("Failed to register resp callback: %d", status);
+		return status;
+	}
+
+	status = wlan_cp_stats_send_infra_cp_req(mac->psoc, &info);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		pe_err("Failed to send stats request status: %d", status);
+		goto get_stats_fail;
+	}
+
+	return QDF_STATUS_SUCCESS;
+
+get_stats_fail:
+	status = wlan_cp_stats_infra_cp_deregister_resp_cb(mac->psoc);
+	if (QDF_IS_STATUS_ERROR(status))
+		pe_err("failed to deregister callback %d", status);
+	return status;
+}
+#endif
+
+/**
+ * rrm_process_sta_stats_report_req: Process RRM sta stats request
+ * @mac: mac context
+ * @pCurrentReq: Current RRM request
+ * @pStaStatsReq: RRM Measurement Request
+ * @pe_session: pe session
+ *
+ * Return: rrm status
+ */
+static tRrmRetStatus
+rrm_process_sta_stats_report_req(struct mac_context *mac,
+				 tpRRMReq pCurrentReq,
+				 tDot11fIEMeasurementRequest *sta_stats_req,
+				 struct pe_session *pe_session)
+{
+	QDF_STATUS status;
+	uint8_t meas_duration = 1;
+	struct rrm_sta_stats *rrm_sta_statistics;
+
+	if (sta_stats_req->measurement_request.sta_stats.meas_duration >
+	    MAX_MEAS_DURATION_FOR_STA_STATS) {
+		pe_err("Dropping req measurement duration > threshold %d",
+		       sta_stats_req->measurement_request.sta_stats.meas_duration);
+		return eRRM_INCAPABLE;
+	}
+
+	if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)
+	    sta_stats_req->measurement_request.sta_stats.peer_mac_addr)) {
+		pe_err("Dropping req: broadcast address not supported");
+		return eRRM_INCAPABLE;
+	}
+
+	rrm_sta_statistics = &mac->rrm.rrmPEContext.rrm_sta_stats;
+	if (sta_stats_req->measurement_request.sta_stats.meas_duration)
+		meas_duration =
+		sta_stats_req->measurement_request.sta_stats.meas_duration;
+
+	rrm_sta_statistics->rrm_report.token = pCurrentReq->token;
+	rrm_sta_statistics->rrm_report.type = pCurrentReq->type;
+	rrm_sta_statistics->rrm_report.refused = 0;
+	rrm_sta_statistics->rrm_report.incapable = 0;
+	rrm_sta_statistics->rrm_report.report.statistics_report.group_id =
+	sta_stats_req->measurement_request.sta_stats.group_identity;
+	rrm_sta_statistics->rrm_report.report.statistics_report.meas_duration
+		= sta_stats_req->measurement_request.sta_stats.meas_duration;
+
+	switch  (sta_stats_req->measurement_request.sta_stats.group_identity) {
+	case STA_STAT_GROUP_ID_COUNTER_STATS:
+	case STA_STAT_GROUP_ID_MAC_STATS:
+		status =
+		rrm_send_sta_stats_req(
+		mac, pe_session,
+		sta_stats_req->measurement_request.sta_stats.peer_mac_addr);
+		if (!QDF_IS_STATUS_SUCCESS(status))
+			return eRRM_REFUSED;
+		mac->lim.lim_timers.rrm_sta_stats_resp_timer.sessionId =
+							pe_session->peSessionId;
+		/*
+		 * Start timer of 1 sec even if meas duration is 0.
+		 * To get stats from FW.
+		 */
+		tx_timer_change(&mac->lim.lim_timers.rrm_sta_stats_resp_timer,
+				SYS_MS_TO_TICKS(meas_duration * 1000), 0);
+		/* Activate sta stats resp timer */
+		if (tx_timer_activate(
+		    &mac->lim.lim_timers.rrm_sta_stats_resp_timer) !=
+		    TX_SUCCESS) {
+			pe_err("failed to start sta stats timer");
+			return eRRM_REFUSED;
+		}
+		break;
+	case STA_STAT_GROUP_ID_DELAY_STATS:
+		/* TODO: update access delay from scan IE */
+	default:
+		pe_nofl_err("group id %d not supported",
+		sta_stats_req->measurement_request.sta_stats.group_identity);
+		return eRRM_INCAPABLE;
+	}
+	return eRRM_SUCCESS;
+}
+
+void
+rrm_process_rrm_sta_stats_request_failure(struct mac_context *mac,
+					  struct pe_session *pe_session,
+					  tSirMacAddr peer,
+					  tRrmRetStatus status, uint8_t index)
+{
+	tpSirMacRadioMeasureReport p_report = NULL;
+	tpRRMReq p_current_req = mac->rrm.rrmPEContext.pCurrentReq[index];
+
+	if (!p_current_req) {
+		pe_err("Current request is NULL");
+		return;
+	}
+
+	p_report = qdf_mem_malloc(sizeof(tSirMacRadioMeasureReport));
+	if (!p_report)
+		return;
+	p_report->token = p_current_req->token;
+	p_report->type = SIR_MAC_RRM_STA_STATISTICS_TYPE;
+
+	pe_debug("Measurement index:%d status %d token %d", index, status,
+		 p_report->token);
+
+	switch (status) {
+	case eRRM_FAILURE: /* fallthrough */
+	case eRRM_REFUSED:
+		p_report->refused = 1;
+		break;
+	case eRRM_INCAPABLE:
+		p_report->incapable = 1;
+		break;
+	default:
+		pe_err("Invalid RRM status, failed to send report");
+		qdf_mem_free(p_report);
+		return;
+	}
+
+	lim_send_radio_measure_report_action_frame(mac,
+						   p_current_req->dialog_token,
+						   1, true,
+						   p_report, peer,
+						   pe_session);
+
+	qdf_mem_free(p_report);
+}
+
+/**
+ * rrm_check_other_sta_sats_req_in_progress: To check if any other sta stats req
+ * in progress
+ * @rrm_req: measurement request
+ *
+ * To avoid any conflict between multiple sta stats request at time of callback
+ * response from FW handle one sta stats request at a time.
+ *
+ * Return: true/false
+ */
+static bool
+rrm_check_other_sta_sats_req_in_progress(
+		tDot11fRadioMeasurementRequest *rrm_req)
+{
+	uint8_t count = 0, i = 0;
+
+	for (i = 0; i < MAX_MEASUREMENT_REQUEST; i++) {
+		if (rrm_req->MeasurementRequest[i].measurement_type ==
+		    SIR_MAC_RRM_STA_STATISTICS_TYPE)
+			count++;
+		if (count > 1)
+			return true;
+	}
+	return false;
+}
+
+/**
+ * rrm_process_sta_stats_req: Process RRM sta stats request
+ * @mac: mac context
+ * @session_entry: pe session
+ * @radiomes_report: measurement report
+ * @rrm_req: measurement request
+ * @num_report: no of reports
+ * @index: index of request
+ *
+ * Return: QDF_STATUS
+ */
+static
+QDF_STATUS rrm_process_sta_stats_req(
+			struct mac_context *mac, tSirMacAddr peer,
+			struct pe_session *session_entry,
+			tpSirMacRadioMeasureReport *radiomes_report,
+			tDot11fRadioMeasurementRequest *rrm_req,
+			uint8_t *num_report, int index)
+{
+	tRrmRetStatus rrm_status = eRRM_SUCCESS;
+	tpRRMReq curr_req;
+	QDF_STATUS status = QDF_STATUS_SUCCESS;
+
+	if (index  >= MAX_MEASUREMENT_REQUEST) {
+		status = rrm_reject_req(radiomes_report,
+					rrm_req, num_report, index,
+					rrm_req->MeasurementRequest[0].
+					measurement_type);
+		return status;
+	}
+
+	if (rrm_check_other_sta_sats_req_in_progress(rrm_req)) {
+		pe_debug("another sta stats request already in progress");
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	curr_req = qdf_mem_malloc(sizeof(*curr_req));
+	if (!curr_req) {
+		mac->rrm.rrmPEContext.pCurrentReq[index] = NULL;
+		qdf_mem_zero(&mac->rrm.rrmPEContext.rrm_sta_stats,
+			     sizeof(mac->rrm.rrmPEContext.rrm_sta_stats));
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	curr_req->dialog_token = rrm_req->DialogToken.token;
+	curr_req->token =
+		rrm_req->MeasurementRequest[index].measurement_token;
+	curr_req->measurement_idx = index;
+	curr_req->type = rrm_req->MeasurementRequest[index].measurement_type;
+
+
+	qdf_mem_set(&mac->rrm.rrmPEContext.rrm_sta_stats,
+		    sizeof(mac->rrm.rrmPEContext.rrm_sta_stats), 0);
+
+	mac->rrm.rrmPEContext.rrm_sta_stats.index = index;
+	mac->rrm.rrmPEContext.pCurrentReq[index] = curr_req;
+	mac->rrm.rrmPEContext.num_active_request++;
+	qdf_mem_copy(mac->rrm.rrmPEContext.rrm_sta_stats.peer,
+		     peer,
+		     QDF_MAC_ADDR_SIZE);
+
+	pe_debug("Processing sta stats Report req %d num_active_req:%d",
+		 index, mac->rrm.rrmPEContext.num_active_request);
+
+	rrm_status = rrm_process_sta_stats_report_req(mac, curr_req,
+		&rrm_req->MeasurementRequest[index], session_entry);
+	if (eRRM_SUCCESS != rrm_status)
+		goto failure;
+
+	return QDF_STATUS_SUCCESS;
+failure:
+	rrm_process_rrm_sta_stats_request_failure(
+			mac, session_entry, peer, rrm_status, index);
+
+	rrm_cleanup(mac, index);
+	return QDF_STATUS_E_FAILURE;
+}
+
 /* -------------------------------------------------------------------- */
 /**
  * rrm_get_max_meas_duration() - calculate max measurement duration for a
@@ -1849,6 +2358,12 @@ rrm_process_radio_measurement_request(struct mac_context *mac_ctx,
 			if (QDF_IS_STATUS_ERROR(status))
 				return status;
 			break;
+		case SIR_MAC_RRM_STA_STATISTICS_TYPE:
+		     status = rrm_process_sta_stats_req(mac_ctx, peer,
+							session_entry, &report,
+							rrm_req, &num_report,
+							i);
+			break;
 		case SIR_MAC_RRM_LCI_TYPE:
 		case SIR_MAC_RRM_LOCATION_CIVIC_TYPE:
 		case SIR_MAC_RRM_FINE_TIME_MEAS_TYPE:
@@ -1958,17 +2473,25 @@ void rrm_cleanup(struct mac_context *mac, uint8_t idx)
 {
 	tpRRMReq cur_rrm_req = NULL;
 
-	mac->rrm.rrmPEContext.num_active_request--;
+	if (mac->rrm.rrmPEContext.num_active_request)
+		mac->rrm.rrmPEContext.num_active_request--;
+
 	cur_rrm_req = mac->rrm.rrmPEContext.pCurrentReq[idx];
 	if (!cur_rrm_req)
 		return;
-
 	if (cur_rrm_req->request.Beacon.reqIes.num) {
 		qdf_mem_free(cur_rrm_req->request.Beacon.reqIes.pElementIds);
 		cur_rrm_req->request.Beacon.reqIes.pElementIds = NULL;
 		cur_rrm_req->request.Beacon.reqIes.num = 0;
 	}
 
+	if (cur_rrm_req->type == SIR_MAC_RRM_STA_STATISTICS_TYPE) {
+		pe_debug("deactivate rrm sta stats timer");
+		lim_deactivate_and_change_timer(mac,
+						eLIM_RRM_STA_STATS_RSP_TIMER);
+		qdf_mem_zero(&mac->rrm.rrmPEContext.rrm_sta_stats,
+			     sizeof(mac->rrm.rrmPEContext.rrm_sta_stats));
+	}
 	qdf_mem_free(cur_rrm_req);
 	mac->rrm.rrmPEContext.pCurrentReq[idx] = NULL;
 

+ 1 - 0
core/mac/src/sys/legacy/src/utils/src/mac_trace.c

@@ -588,6 +588,7 @@ uint8_t *mac_trace_get_lim_msg_string(uint16_t lim_msg)
 		CASE_RETURN_STRING(SIR_LIM_PERIODIC_JOIN_PROBE_REQ_TIMEOUT);
 		CASE_RETURN_STRING(SIR_LIM_AUTH_RETRY_TIMEOUT);
 		CASE_RETURN_STRING(SIR_LIM_AUTH_SAE_TIMEOUT);
+		CASE_RETURN_STRING(SIR_LIM_RRM_STA_STATS_RSP_TIMEOUT);
 		CASE_RETURN_STRING(SIR_LIM_MSG_TYPES_END);
 		CASE_RETURN_STRING(LIM_MLM_SCAN_REQ);
 		CASE_RETURN_STRING(LIM_MLM_SCAN_CNF);

+ 9 - 0
core/mac/src/sys/legacy/src/utils/src/parser_api.c

@@ -7086,6 +7086,15 @@ populate_dot11f_chan_load_report(struct mac_context *mac,
 		 dot11f->report.channel_load_report.chan_load);
 }
 
+QDF_STATUS
+populate_dot11f_rrm_sta_stats_report(
+		struct mac_context *mac, tDot11fIEMeasurementReport *pdot11f,
+		struct statistics_report *statistics_report)
+{
+	/* TODO: populate measurement report */
+	return QDF_STATUS_SUCCESS;
+}
+
 QDF_STATUS
 populate_dot11f_beacon_report(struct mac_context *mac,
 			      tDot11fIEMeasurementReport *pDot11f,