Selaa lähdekoodia

Merge 06aa01c3bcdec335409c6fb8061fe7f66d3a5dbc on remote branch

Change-Id: Ifd29901aeb7e552ba1c171edc745f0da6ed1e48a
Linux Build Service Account 1 vuosi sitten
vanhempi
sitoutus
1a4a097d59
40 muutettua tiedostoa jossa 1399 lisäystä ja 389 poistoa
  1. 8 21
      components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c
  2. 57 8
      components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c
  3. 4 1
      components/mlme/core/src/wlan_mlme_main.c
  4. 4 3
      components/mlme/dispatcher/inc/cfg_mlme_sta.h
  5. 23 8
      components/pmo/core/inc/wlan_pmo_main.h
  6. 10 11
      components/pmo/core/src/wlan_pmo_main.c
  7. 42 38
      components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h
  8. 23 6
      components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h
  9. 20 6
      components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_api.h
  10. 8 3
      components/pmo/dispatcher/src/wlan_pmo_obj_mgmt_api.c
  11. 49 1
      components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c
  12. 0 1
      components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.h
  13. 8 1
      components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h
  14. 2 1
      components/umac/mlme/mlo_mgr/src/wlan_t2lm_api.c
  15. 15 0
      core/hdd/inc/wlan_hdd_main.h
  16. 208 113
      core/hdd/src/wlan_hdd_cfg80211.c
  17. 1 9
      core/hdd/src/wlan_hdd_driver_ops.c
  18. 4 25
      core/hdd/src/wlan_hdd_hostapd.c
  19. 101 0
      core/hdd/src/wlan_hdd_ioctl.c
  20. 126 15
      core/hdd/src/wlan_hdd_main.c
  21. 22 4
      core/hdd/src/wlan_hdd_mlo.c
  22. 22 1
      core/hdd/src/wlan_hdd_p2p.c
  23. 8 0
      core/hdd/src/wlan_hdd_power.c
  24. 3 1
      core/hdd/src/wlan_hdd_sar_limits.c
  25. 3 0
      core/hdd/src/wlan_hdd_stats.c
  26. 3 2
      core/hdd/src/wlan_hdd_tx_power.c
  27. 3 3
      core/mac/inc/qwlan_version.h
  28. 5 2
      core/mac/src/pe/lim/lim_process_action_frame.c
  29. 17 3
      core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c
  30. 2 0
      core/mac/src/pe/lim/lim_process_sme_req_messages.c
  31. 22 3
      core/mac/src/pe/lim/lim_send_sme_rsp_messages.c
  32. 1 3
      core/mac/src/pe/lim/lim_utils.c
  33. 7 5
      core/sme/inc/sme_api.h
  34. 2 2
      core/sme/inc/sme_internal.h
  35. 6 4
      core/sme/src/common/sme_api.c
  36. 60 6
      core/wma/inc/wma.h
  37. 17 8
      core/wma/src/wma_dev_if.c
  38. 382 58
      core/wma/src/wma_features.c
  39. 78 9
      core/wma/src/wma_main.c
  40. 23 4
      core/wma/src/wma_mgmt.c

+ 8 - 21
components/cmn_services/policy_mgr/src/wlan_policy_mgr_action.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -1871,8 +1871,7 @@ bool policy_mgr_is_sap_restart_required_after_sta_disconnect(
 	QDF_STATUS status;
 	uint32_t sta_gc_present = 0;
 	qdf_freq_t user_config_freq = 0;
-	tQDF_MCC_TO_SCC_SWITCH_MODE cc_mode =
-				policy_mgr_get_mcc_to_scc_switch_mode(psoc);
+	enum reg_wifi_band user_band, op_band;
 
 	if (intf_ch_freq)
 		*intf_ch_freq = 0;
@@ -1966,26 +1965,14 @@ bool policy_mgr_is_sap_restart_required_after_sta_disconnect(
 		/*
 		 * STA got disconnected & SAP has previously moved to 2.4 GHz
 		 * due to concurrency, then move SAP back to user configured
-		 * frequency.
-		 * if SCC to MCC switch mode is
-		 * QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL, then don't move
-		 * SAP to user configured frequency whenever standalone SAP is
-		 * currently not on the user configured frequency.
-		 * Else move the SAP only when SAP is on 2.4 GHz band and user
-		 * configured frequency is on any other bands.
-		 * And for QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL, if GO
-		 * is on 5/6 GHz, SAP is not allowed to move back to 5/6 GHz.
-		 * If GO is not present on 5/6 GHz, SAP need to moved to
-		 * user configured frequency.
+		 * frequency if the user configured band is better than
+		 * the current operating band.
 		 */
+		op_band = wlan_reg_freq_to_band(op_ch_freq_list[i]);
+		user_band = wlan_reg_freq_to_band(user_config_freq);
+
 		if (!sta_gc_present && user_config_freq &&
-		    cc_mode == QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL) {
-			policy_mgr_debug("Don't move sap to user configured freq: %d",
-					 user_config_freq);
-			break;
-		} else if (!sta_gc_present && user_config_freq &&
-			   WLAN_REG_IS_24GHZ_CH_FREQ(op_ch_freq_list[i]) &&
-			   !WLAN_REG_IS_24GHZ_CH_FREQ(user_config_freq)) {
+		    op_band < user_band) {
 			curr_sap_freq = op_ch_freq_list[i];
 			policy_mgr_debug("Move sap to user configured freq: %d",
 					 user_config_freq);

+ 57 - 8
components/cmn_services/policy_mgr/src/wlan_policy_mgr_get_set_utils.c

@@ -11961,6 +11961,49 @@ bool policy_mgr_is_sap_go_on_2g(struct wlan_objmgr_psoc *psoc)
 	return ret;
 }
 
+static inline bool
+policy_mgr_is_chan_eligible_for_sap(struct policy_mgr_psoc_priv_obj *pm_ctx,
+				    uint8_t vdev_id, qdf_freq_t freq)
+{
+	struct wlan_objmgr_vdev *vdev;
+	enum channel_state ch_state;
+	enum reg_6g_ap_type sta_connected_pwr_type;
+	uint32_t ap_power_type_6g = 0;
+	bool is_eligible = false;
+
+	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(pm_ctx->psoc, vdev_id,
+						    WLAN_POLICY_MGR_ID);
+	if (!vdev)
+		return false;
+
+	ch_state = wlan_reg_get_channel_state_for_pwrmode(pm_ctx->pdev,
+							  freq,
+							  REG_CURRENT_PWR_MODE);
+	sta_connected_pwr_type = mlme_get_best_6g_power_type(vdev);
+	wlan_reg_get_cur_6g_ap_pwr_type(pm_ctx->pdev, &ap_power_type_6g);
+
+	/*
+	 * If the SAP user configured frequency is 6 GHz,
+	 * move the SAP to STA SCC in 6 GHz only if:
+	 * a) The channel is PSC
+	 * b) The channel supports AP in VLP power type
+	 * c) The DUT is configured to operate SAP in VLP only
+	 * d) The STA is connected to the 6 GHz AP in
+	 *    either VLP or LPI.
+	 *    - If the STA is in LPI, then lim_update_tx_power()
+	 *	would move the STA to VLP.
+	 */
+	if (WLAN_REG_IS_6GHZ_PSC_CHAN_FREQ(freq) &&
+	    ap_power_type_6g == REG_VERY_LOW_POWER_AP &&
+	    ch_state == CHANNEL_STATE_ENABLE &&
+	    (sta_connected_pwr_type == REG_VERY_LOW_POWER_AP ||
+	     sta_connected_pwr_type == REG_INDOOR_AP))
+		is_eligible = true;
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_POLICY_MGR_ID);
+	return is_eligible;
+}
+
 bool policy_mgr_is_restart_sap_required(struct wlan_objmgr_psoc *psoc,
 					uint8_t vdev_id,
 					qdf_freq_t freq,
@@ -12138,14 +12181,20 @@ bool policy_mgr_is_restart_sap_required(struct wlan_objmgr_psoc *psoc,
 		}
 
 		if (scc_mode == QDF_MCC_TO_SCC_SWITCH_WITH_FAVORITE_CHANNEL &&
-		    connection[i].freq == freq &&
-		    WLAN_REG_IS_24GHZ_CH_FREQ(freq) &&
-		    !num_5_or_6_conn &&
-		    user_config_freq &&
-		    !WLAN_REG_IS_24GHZ_CH_FREQ(user_config_freq)) {
-			policy_mgr_debug("SAP move to user configure %d from %d",
-					 user_config_freq, freq);
-			restart_required = true;
+		    WLAN_REG_IS_24GHZ_CH_FREQ(freq) && user_config_freq) {
+			if (connection[i].freq == freq && !num_5_or_6_conn &&
+			    !WLAN_REG_IS_24GHZ_CH_FREQ(user_config_freq)) {
+				policy_mgr_debug("SAP move to user configure %d from %d",
+						 user_config_freq, freq);
+				restart_required = true;
+			} else if (connection[i].freq != freq &&
+				   WLAN_REG_IS_6GHZ_CHAN_FREQ(user_config_freq) &&
+				   policy_mgr_is_chan_eligible_for_sap(pm_ctx,
+								       connection[i].vdev_id,
+								       connection[i].freq)) {
+				policy_mgr_debug("Move SAP to STA 6 GHz channel");
+				restart_required = true;
+			}
 		}
 	}
 

+ 4 - 1
components/mlme/core/src/wlan_mlme_main.c

@@ -2583,7 +2583,10 @@ static void mlme_init_sta_mlo_cfg(struct wlan_objmgr_psoc *psoc,
 	sta->mlo_same_link_mld_address =
 		cfg_default(CFG_MLO_SAME_LINK_MLD_ADDR);
 	sta->mlo_5gl_5gh_mlsr =
-		cfg_default(CFG_MLO_MLO_5GL_5GH_MLSR);
+		cfg_get(psoc, CFG_MLO_MLO_5GL_5GH_MLSR);
+
+	mlme_debug("mlo_support_link_num: %d, mlo_support_link_band: 0x%x",
+		   sta->mlo_support_link_num, sta->mlo_support_link_band);
 }
 
 static bool

+ 4 - 3
components/mlme/dispatcher/inc/cfg_mlme_sta.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2024 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 above
@@ -761,7 +761,7 @@
  * </cfg>
  */
 
-#define CFG_MLO_MLO_5GL_5GH_MLSR CFG_BOOL( \
+#define CFG_MLO_MLO_5GL_5GH_MLSR CFG_INI_BOOL( \
 		"mlo_5gl_5gh_mlsr",\
 		0, \
 		"enable 5GL+5GH MLSR")
@@ -797,5 +797,6 @@
 	CFG_MLO_SUPPORT_LINK_BAND_CFG \
 	CFG_MLO_PREFER_PERCENTAGE_CFG \
 	CFG_MLO_SAME_LINK_MLD_ADDR_CFG \
-	CFG_EHT_DISABLE_PUNCT_IN_US_LPI_CFG
+	CFG_EHT_DISABLE_PUNCT_IN_US_LPI_CFG \
+	CFG_MLO_MLO_5GL_5GH_MLSR_CFG
 #endif /* CFG_MLME_STA_H__ */

+ 23 - 8
components/pmo/core/inc/wlan_pmo_main.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2017-2019 The Linux Foundation. All rights reserved.
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023-2024 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
@@ -386,21 +386,36 @@ pmo_intersect_packet_filter(struct pmo_psoc_priv_obj *psoc_ctx)
 }
 
 /*
- * pmo_enable_ssr_on_page_fault: Enable/disable ssr on pagefault
- * @psoc: objmgr psoc
+ * pmo_host_action_on_page_fault() - Returns action host will take on page fault
+ * @psoc: PSOC object manager pointer.
  *
- * Return: True if SSR is enabled on pagefault
+ * Returns: Host action on page fault event
  */
-bool pmo_enable_ssr_on_page_fault(struct wlan_objmgr_psoc *psoc);
+enum pmo_page_fault_action
+pmo_host_action_on_page_fault(struct wlan_objmgr_psoc *psoc);
+
+#define pmo_is_host_pagefault_action(_psoc, _action) \
+		(pmo_host_action_on_page_fault(_psoc) == (_action))
+
+static inline bool pmo_no_op_on_page_fault(struct wlan_objmgr_psoc *psoc)
+{
+	return pmo_is_host_pagefault_action(psoc, PMO_PF_HOST_ACTION_NO_OP);
+}
+
+static inline bool pmo_enable_ssr_on_page_fault(struct wlan_objmgr_psoc *psoc)
+{
+	return pmo_is_host_pagefault_action(psoc, PMO_PF_HOST_ACTION_TRIGGER_SSR);
+}
 
 /*
- * pmo_get_max_pagefault_wakeups_for_ssr: get pagefault wakeups for ssr
+ * pmo_get_min_pagefault_wakeups_for_action() - get pagefault wakeups for host
+ * to initiate action
  * @psoc: objmgr psoc
  *
- * Return: SSR interval for pagefault
+ * Return: Min wakeups interval for host action on pagefault
  */
 uint8_t
-pmo_get_max_pagefault_wakeups_for_ssr(struct wlan_objmgr_psoc *psoc);
+pmo_get_min_pagefault_wakeups_for_action(struct wlan_objmgr_psoc *psoc);
 
 /*
  * pmo_get_interval_for_pagefault_wakeup_counts: get ssr interval for pagefault

+ 10 - 11
components/pmo/core/src/wlan_pmo_main.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021,2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021,2023-2024 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
@@ -295,12 +295,10 @@ static void wlan_pmo_init_cfg(struct wlan_objmgr_psoc *psoc,
 			cfg_get(psoc, CFG_DISCONNECT_SAP_TDLS_IN_WOW);
 	wlan_pmo_get_icmp_offload_enable_cfg(psoc, psoc_cfg);
 
-	psoc_cfg->enable_ssr_on_page_fault =
+	psoc_cfg->host_pf_action = cfg_get(psoc, CFG_HOST_ACTION_ON_PAGEFAULT);
+	psoc_cfg->min_pagefault_wakeups_for_action =
 				cfg_get(psoc,
-					CFG_ENABLE_SSR_ON_PAGEFAULT);
-	psoc_cfg->max_pagefault_wakeups_for_ssr =
-				cfg_get(psoc,
-					CFG_MAX_PAGEFAULT_WAKEUPS_FOR_SSR);
+					CFG_MIN_PAGEFAULT_WAKEUPS_FOR_ACTION);
 	psoc_cfg->interval_for_pagefault_wakeup_counts =
 			cfg_get(psoc,
 				CFG_INTERVAL_FOR_PAGEFAULT_WAKEUP_COUNT);
@@ -503,24 +501,25 @@ uint8_t pmo_core_psoc_get_txrx_handle(struct wlan_objmgr_psoc *psoc)
 	return txrx_pdev_id;
 }
 
-bool pmo_enable_ssr_on_page_fault(struct wlan_objmgr_psoc *psoc)
+enum pmo_page_fault_action
+pmo_host_action_on_page_fault(struct wlan_objmgr_psoc *psoc)
 {
 	struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc);
 
 	if (!pmo_psoc_ctx)
-		return 0;
+		return PMO_PF_HOST_ACTION_NO_OP;
 
-	return pmo_psoc_ctx->psoc_cfg.enable_ssr_on_page_fault;
+	return pmo_psoc_ctx->psoc_cfg.host_pf_action;
 }
 
-uint8_t pmo_get_max_pagefault_wakeups_for_ssr(struct wlan_objmgr_psoc *psoc)
+uint8_t pmo_get_min_pagefault_wakeups_for_action(struct wlan_objmgr_psoc *psoc)
 {
 	struct pmo_psoc_priv_obj *pmo_psoc_ctx = pmo_psoc_get_priv(psoc);
 
 	if (!pmo_psoc_ctx)
 		return 0;
 
-	return pmo_psoc_ctx->psoc_cfg.max_pagefault_wakeups_for_ssr;
+	return pmo_psoc_ctx->psoc_cfg.min_pagefault_wakeups_for_action;
 }
 
 uint32_t

+ 42 - 38
components/pmo/dispatcher/inc/wlan_pmo_common_cfg.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -636,14 +636,14 @@
 
 /*
  * <ini>
- * enable_ssr_on_page_fault - Enable SSR on pagefault
- * @Min: 0
- * @Max: 1
- * @Default: 0
- *
- * This INI is used to enable/disable SSR when host is woken up with the reason
- * as pagefault.
- * For ex: If enable_ssr_on_page_fault = 1, max_pagefault_wakeups_for_ssr = 30,
+ * action_on_page_fault - Host action on page fault wakeup event
+ * @Min: PMO_PF_HOST_ACTION_NO_OP
+ * @Max: PMO_PF_HOST_ACTION_MAX - 1
+ * @Default: PMO_PF_HOST_ACTION_NO_OP
+ *
+ * This INI is used to determine host behavior on WOW_REASON_PAGE_FAULT wakeup
+ * event.
+ * For ex: If action is to trigger SSR, min_pagefault_wakeups_for_action = 30,
  * interval_for_pagefault_wakeup_counts = 180000 (3 mins) and
  * ssr_frequency_on_pagefault = 3600000 (1hr), in this case host will trigger
  * the SSR if it receives 30 wakeups because of pagefaults in 3 mins, host will
@@ -653,24 +653,28 @@
  *
  * </ini>
  */
-#define CFG_ENABLE_SSR_ON_PAGEFAULT CFG_INI_BOOL( \
-		"enable_ssr_on_page_fault", \
-		0, \
-		"Enable SSR on pagefault")
+#define CFG_HOST_ACTION_ON_PAGEFAULT CFG_INI_UINT( \
+		"action_on_page_fault", \
+		PMO_PF_HOST_ACTION_NO_OP, \
+		PMO_PF_HOST_ACTION_MAX - 1, \
+		PMO_PF_HOST_ACTION_NO_OP, \
+		CFG_VALUE_OR_DEFAULT, \
+		"Host action on FW pagefault event")
 
 /*
  * <ini>
- * max_pagefault_wakeups_for_ssr - Max number of pagefaults wakeups to trigger
- * SSR
- * @Min: 1
+ * min_pagefault_wakeups_for_action - Min number of pagefaults wakeups to
+ * initiate host action.
+ * @Min: 2
  * @Max: 255
  * @Default: 30
  *
- * This ini is used to trigger SSR if fw wakes up host for
- * max_pagefault_wakeups_for_ssr number of times in
- * interval_for_pagefault_wakeup_counts interval. SSR is triggered only once
- * in ssr_frequency_on_pagefault interval.
- * For ex: If enable_ssr_on_page_fault = 1, max_pagefault_wakeups_for_ssr = 30,
+ * This ini is used to get the count of max pagefault wakeups to reach before
+ * host takes action.
+ * If the count is reached within the wakeup time interval host will either
+ * trigger SSR (within the limits of SSR trigger freq) or may notify APPS or
+ * ignore if no action is set.
+ * For ex: If SSR on pagefault = 1, min_pagefault_wakeups_for_action = 30,
  * interval_for_pagefault_wakeup_counts = 180000 (3 mins) and
  * ssr_frequency_on_pagefault = 3600000 (1hr), in this case host will trigger
  * the SSR if it receives 30 wakeups because of pagefaults in 3 mins, host will
@@ -678,9 +682,9 @@
  * trigger next SSR for next 1 hr even if it receives 30 wakeups from fw because
  * of pagefaults. This 1 hr time is getting monitored from last SSR.
  */
-#define CFG_MAX_PAGEFAULT_WAKEUPS_FOR_SSR CFG_INI_UINT( \
-		"max_pagefault_wakeups_for_ssr", \
-		1, \
+#define CFG_MIN_PAGEFAULT_WAKEUPS_FOR_ACTION CFG_INI_UINT( \
+		"min_pagefault_wakeups_for_action", \
+		2, \
 		255, \
 		30, \
 		CFG_VALUE_OR_DEFAULT, \
@@ -689,16 +693,16 @@
 /*
  * <ini>
  * interval_for_pagefault_wakeup_counts - Time in ms in which
- * max_pagefault_wakeups_for_ssr needs to be monitored
- * @Min: 60000
- * @Max: 300000
+ * min_pagefault_wakeups_for_action needs to be monitored
+ * @Min: 60000 (1min)
+ * @Max: 18000000 (5hrs)
  * @Default: 180000 (3 mins)
  *
- * This ini define time in ms in which max_pagefault_wakeups_for_ssr needs to be
- * Monitored. If in interval_for_pagefault_wakeup_counts ms,
- * max_pagefault_wakeups_for_ssr is reached host will trigger the SSR.
+ * This ini define time in ms in which min_pagefault_wakeups_for_host action
+ * needs to be monitored. If in interval_for_pagefault_wakeup_counts ms,
+ * min_pagefault_wakeups_for_action is reached host will trigger the action.
  * SSR is triggered only once in ssr_frequency_on_pagefault interval.
- * For ex: If enable_ssr_on_page_fault = 1, max_pagefault_wakeups_for_ssr = 30,
+ * For ex: If SSR on pagefault = 1, min_pagefault_wakeups_for_action = 30,
  * interval_for_pagefault_wakeup_counts = 180000 (3 mins) and
  * ssr_frequency_on_pagefault = 3600000 (1hr), in this case host will trigger
  * the SSR if it receives 30 wakeups because of pagefaults in 3 mins, host will
@@ -709,10 +713,10 @@
 #define CFG_INTERVAL_FOR_PAGEFAULT_WAKEUP_COUNT CFG_INI_UINT( \
 	"interval_for_pagefault_wakeup_counts", \
 	60000, \
-	300000, \
+	18000000, \
 	180000, \
 	CFG_VALUE_OR_DEFAULT, \
-	"Interval in which max_pagefault_wakeups_for_ssr needs to be monitored")
+	"Interval in which min_pagefault_wakeups_for_ssr needs to be monitored")
 
 /*
  * <ini>
@@ -723,11 +727,11 @@
  * @Default: 3600000 (1 hr)
  *
  * This ini define time in ms in which next SSR needs to be triggered if
- * max_pagefault_wakeups_for_ssr is reached in
+ * min_pagefault_wakeups_for_action is reached in
  * interval_for_pagefault_wakeup_counts time.
  * INIs max_pagefault_wakeups_for_ssr, interval_for_pagefault_wakeup_counts and
  * ssr_frequency_on_pagefault needs to be considered together.
- * For ex: If enable_ssr_on_page_fault = 1, max_pagefault_wakeups_for_ssr = 30,
+ * For ex: If enable_ssr_on_page_fault = 1, min_pagefault_wakeups_for_ssr = 30,
  * interval_for_pagefault_wakeup_counts = 180000 (3 mins) and
  * ssr_frequency_on_pagefault = 3600000 (1hr), in this case host will trigger
  * the SSR if it receives 30 wakeups because of pagefaults in 3 mins, host will
@@ -741,7 +745,7 @@
 	7200000, \
 	3600000, \
 	CFG_VALUE_OR_DEFAULT, \
-	"Interval in which max_pagefault_wakeups_for_ssr needs to be monitored")
+	"Interval in which min_pagefault_wakeups_for_ssr needs to be monitored")
 
 /*
  * <ini>
@@ -790,8 +794,8 @@
 	CFG(CFG_DISCONNECT_SAP_TDLS_IN_WOW) \
 	CFG(CFG_IGMP_VERSION_SUPPORT) \
 	CFG(CFG_ENABLE_ICMP_OFFLOAD) \
-	CFG(CFG_ENABLE_SSR_ON_PAGEFAULT) \
-	CFG(CFG_MAX_PAGEFAULT_WAKEUPS_FOR_SSR) \
+	CFG(CFG_HOST_ACTION_ON_PAGEFAULT) \
+	CFG(CFG_MIN_PAGEFAULT_WAKEUPS_FOR_ACTION) \
 	CFG(CFG_INTERVAL_FOR_PAGEFAULT_WAKEUP_COUNT) \
 	CFG(CFG_SSR_FREQUENCY_ON_PAGEFAULT)
 

+ 23 - 6
components/pmo/dispatcher/inc/wlan_pmo_common_public_struct.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2017-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -331,6 +331,23 @@ struct pmo_icmp_offload {
 };
 #endif
 
+/*
+ * enum pmo_page_fault_action - Host action on FW page fault
+ * @PMO_PF_HOST_ACTION_NO_OP: Host will ignore PF wakeup event.
+ * @PMO_PF_HOST_ACTION_TRIGGER_SSR: Host will trigger SSR on PF threshold.
+ * @PMO_PF_HOST_ACTION_NOTIFY_APPS: Host will notify APPS on PF threshold.
+ *
+ * @PMO_PF_HOST_ACTION_MAX: Reserved and invalid value
+ */
+enum pmo_page_fault_action {
+	PMO_PF_HOST_ACTION_NO_OP = 0,
+	PMO_PF_HOST_ACTION_TRIGGER_SSR = 1,
+	PMO_PF_HOST_ACTION_NOTIFY_APPS = 2,
+
+	/* Keep it last */
+	PMO_PF_HOST_ACTION_MAX,
+};
+
 /**
  * struct pmo_psoc_cfg - user configuration required for pmo
  * @ptrn_match_enable_all_vdev: true when pattern match is enable for all vdev
@@ -402,9 +419,9 @@ struct pmo_icmp_offload {
  * @disconnect_sap_tdls_in_wow: sap/p2p_go disconnect or teardown tdls link
  * @is_icmp_offload_enable: true if icmp offload is supported
  *	for psoc else false
- * @enable_ssr_on_page_fault: Enable ssr on pagefault
- * @max_pagefault_wakeups_for_ssr: Maximum number of pagefaults after which host
- * needs to trigger SSR
+ * @host_pf_action: Action to take on page fault
+ * @min_pagefault_wakeups_for_action: Min number of pagefaults after which
+ * host needs to start act upon.
  * @interval_for_pagefault_wakeup_counts: Time in ms in which max pagefault
  * wakeups needs to be monitored.
  * @ssr_frequency_on_pagefault: Time in ms in which SSR needs to be triggered
@@ -490,8 +507,8 @@ struct pmo_psoc_cfg {
 #ifdef WLAN_FEATURE_ICMP_OFFLOAD
 	bool is_icmp_offload_enable;
 #endif
-	bool enable_ssr_on_page_fault;
-	uint8_t max_pagefault_wakeups_for_ssr;
+	enum pmo_page_fault_action host_pf_action;
+	uint8_t min_pagefault_wakeups_for_action;
 	uint32_t interval_for_pagefault_wakeup_counts;
 	uint32_t ssr_frequency_on_pagefault;
 };

+ 20 - 6
components/pmo/dispatcher/inc/wlan_pmo_obj_mgmt_api.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2017-2019, 2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2024 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
@@ -294,6 +294,14 @@ wlan_pmo_get_sap_mode_bus_suspend(struct wlan_objmgr_psoc *psoc);
 bool
 wlan_pmo_get_go_mode_bus_suspend(struct wlan_objmgr_psoc *psoc);
 
+/*
+ * wlan_pmo_no_op_on_page_fault() - Whether to ignore page fault wakeups
+ * @psoc: PSOC object manager
+ *
+ * Return: true if host has to ignore page fault wakeup events else false.
+ */
+bool wlan_pmo_no_op_on_page_fault(struct wlan_objmgr_psoc *psoc);
+
 /*
  * wlan_pmo_enable_ssr_on_page_fault: Enable/disable ssr on pagefault
  * @psoc: objmgr psoc
@@ -303,13 +311,14 @@ wlan_pmo_get_go_mode_bus_suspend(struct wlan_objmgr_psoc *psoc);
 bool wlan_pmo_enable_ssr_on_page_fault(struct wlan_objmgr_psoc *psoc);
 
 /*
- * wlan_pmo_get_max_pagefault_wakeups_for_ssr: get max pagefault wakeups for ssr
+ * wlan_pmo_get_min_pagefault_wakeups_for_action() - get min pagefault wakeups
+ * for host to initiate action
  * @psoc: objmgr psoc
  *
- * Return: Max pagefault wakeups for SSR
+ * Return: Min pagefault wakeups for action
  */
 uint8_t
-wlan_pmo_get_max_pagefault_wakeups_for_ssr(struct wlan_objmgr_psoc *psoc);
+wlan_pmo_get_min_pagefault_wakeups_for_action(struct wlan_objmgr_psoc *psoc);
 
 /*
  * wlan_pmo_get_interval_for_pagefault_wakeup_counts: get ssr interval for
@@ -508,14 +517,19 @@ wlan_pmo_get_go_mode_bus_suspend(struct wlan_objmgr_psoc *psoc)
 	return false;
 }
 
+static inline bool wlan_pmo_no_op_on_page_fault(struct wlan_objmgr_psoc *psoc)
+{
+	return true;
+}
+
 static inline bool
 wlan_pmo_enable_ssr_on_page_fault(struct wlan_objmgr_psoc *psoc)
 {
-	return 0;
+	return false;
 }
 
 static inline uint8_t
-wlan_pmo_get_max_pagefault_wakeups_for_ssr(struct wlan_objmgr_psoc *psoc)
+wlan_pmo_get_min_pagefault_wakeups_for_action(struct wlan_objmgr_psoc *psoc)
 {
 	return 0;
 }

+ 8 - 3
components/pmo/dispatcher/src/wlan_pmo_obj_mgmt_api.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2023-2024 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
@@ -893,15 +893,20 @@ wlan_pmo_get_go_mode_bus_suspend(struct wlan_objmgr_psoc *psoc)
 	return pmo_psoc_ctx->psoc_cfg.is_bus_suspend_enabled_in_go_mode;
 }
 
+bool wlan_pmo_no_op_on_page_fault(struct wlan_objmgr_psoc *psoc)
+{
+	return pmo_no_op_on_page_fault(psoc);
+}
+
 bool wlan_pmo_enable_ssr_on_page_fault(struct wlan_objmgr_psoc *psoc)
 {
 	return pmo_enable_ssr_on_page_fault(psoc);
 }
 
 uint8_t
-wlan_pmo_get_max_pagefault_wakeups_for_ssr(struct wlan_objmgr_psoc *psoc)
+wlan_pmo_get_min_pagefault_wakeups_for_action(struct wlan_objmgr_psoc *psoc)
 {
-	return pmo_get_max_pagefault_wakeups_for_ssr(psoc);
+	return pmo_get_min_pagefault_wakeups_for_action(psoc);
 }
 
 uint32_t

+ 49 - 1
components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.c

@@ -6488,6 +6488,33 @@ cm_populate_roam_success_mlo_param(struct wlan_objmgr_psoc *psoc,
 }
 #endif
 
+/**
+ * cm_roam_cancel_event() - Send roam cancelled diag event
+ * @vdev_id: Vdev id
+ * @reason: Roam failure reason code
+ * @fw_timestamp: Firmware timestamp
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS
+cm_roam_cancel_event(uint8_t vdev_id, enum wlan_roam_failure_reason_code reason,
+		     uint64_t fw_timestamp)
+{
+	WLAN_HOST_DIAG_EVENT_DEF(wlan_diag_event, struct wlan_diag_roam_result);
+
+	qdf_mem_zero(&wlan_diag_event, sizeof(wlan_diag_event));
+
+	populate_diag_cmn(&wlan_diag_event.diag_cmn, vdev_id, fw_timestamp,
+			  NULL);
+
+	wlan_diag_event.version = DIAG_ROAM_RESULT_VERSION;
+	wlan_diag_event.roam_fail_reason = reason;
+
+	WLAN_HOST_DIAG_EVENT_REPORT(&wlan_diag_event, EVENT_WLAN_ROAM_CANCEL);
+
+	return QDF_STATUS_SUCCESS;
+}
+
 void cm_roam_result_info_event(struct wlan_objmgr_psoc *psoc,
 			       struct wmi_roam_trigger_info *trigger,
 			       struct wmi_roam_result *res,
@@ -6496,6 +6523,7 @@ void cm_roam_result_info_event(struct wlan_objmgr_psoc *psoc,
 {
 	uint8_t i;
 	struct qdf_mac_addr bssid = {0};
+	enum wlan_roam_failure_reason_code roam_cancel_reason;
 	bool roam_abort = (res->fail_reason == ROAM_FAIL_REASON_SYNC ||
 			   res->fail_reason == ROAM_FAIL_REASON_DISCONNECT ||
 			   res->fail_reason == ROAM_FAIL_REASON_HOST ||
@@ -6505,10 +6533,31 @@ void cm_roam_result_info_event(struct wlan_objmgr_psoc *psoc,
 				ROAM_FAIL_REASON_UNABLE_TO_START_ROAM_HO);
 	bool is_full_scan = (scan_data->present &&
 			scan_data->type == WLAN_ROAM_SCAN_TYPE_FULL_SCAN);
+	bool is_roam_cancel =
+		(res->fail_reason == ROAM_FAIL_REASON_SCAN_CANCEL);
 
 	WLAN_HOST_DIAG_EVENT_DEF(wlan_diag_event,
 				 struct wlan_diag_roam_result);
 
+	if (is_roam_cancel) {
+		if (res->roam_abort_reason ==
+		    WMI_ROAM_SCAN_CANCEL_IDLE_SCREEN_ON) {
+			roam_cancel_reason = ROAM_FAIL_REASON_SCREEN_ACTIVITY;
+		} else if (res->roam_abort_reason ==
+			 WMI_ROAM_SCAN_CANCEL_OTHER_PRIORITY_ROAM_SCAN) {
+			roam_cancel_reason =
+				ROAM_FAIL_REASON_OTHER_PRIORITY_ROAM_SCAN;
+		} else {
+			mlme_debug("vdev:%d Unsupported abort reason:%d",
+				   vdev_id, res->roam_abort_reason);
+			return;
+		}
+
+		cm_roam_cancel_event(vdev_id, roam_cancel_reason,
+				     res->timestamp);
+		return;
+	}
+
 	qdf_mem_zero(&wlan_diag_event, sizeof(wlan_diag_event));
 
 	populate_diag_cmn(&wlan_diag_event.diag_cmn, vdev_id,
@@ -6582,7 +6631,6 @@ void cm_roam_result_info_event(struct wlan_objmgr_psoc *psoc,
 				       trigger->trigger_reason,
 				       wlan_diag_event.is_roam_successful,
 				       is_full_scan, res->fail_reason);
-
 }
 
 #endif

+ 0 - 1
components/umac/mlme/connection_mgr/core/src/wlan_cm_roam_offload.h

@@ -81,7 +81,6 @@ void cm_roam_result_info_event(struct wlan_objmgr_psoc *psoc,
 			       struct wmi_roam_result *res,
 			       struct wmi_roam_scan_data *scan_data,
 			       uint8_t vdev_id);
-
 #elif defined(WLAN_FEATURE_CONNECTIVITY_LOGGING) && \
     defined(WLAN_FEATURE_ROAM_OFFLOAD)
 /**

+ 8 - 1
components/umac/mlme/connection_mgr/dispatcher/inc/wlan_cm_roam_public_struct.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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 above
@@ -421,6 +421,10 @@ enum roam_fail_params {
  * found after final BMISS.
  * @ROAM_FAIL_REASON_CURR_AP_STILL_OK: Background scan was abort, but
  * current network condition is fine.
+ * @ROAM_FAIL_REASON_SCAN_CANCEL: Roam fail reason, scan cancelled
+ * @ROAM_FAIL_REASON_SCREEN_ACTIVITY: Roam fail reason screen activity happened
+ * @ROAM_FAIL_REASON_OTHER_PRIORITY_ROAM_SCAN: Roam fail due to other priority
+ * roam scan started.
  * @ROAM_FAIL_REASON_UNKNOWN: Default reason
  */
 enum wlan_roam_failure_reason_code {
@@ -461,6 +465,9 @@ enum wlan_roam_failure_reason_code {
 	ROAM_FAIL_REASON_NO_AP_FOUND_AND_FINAL_BMISS_SENT,
 	ROAM_FAIL_REASON_NO_CAND_AP_FOUND_AND_FINAL_BMISS_SENT,
 	ROAM_FAIL_REASON_CURR_AP_STILL_OK,
+	ROAM_FAIL_REASON_SCAN_CANCEL,
+	ROAM_FAIL_REASON_SCREEN_ACTIVITY,
+	ROAM_FAIL_REASON_OTHER_PRIORITY_ROAM_SCAN,
 	ROAM_FAIL_REASON_UNKNOWN = 255,
 };
 

+ 2 - 1
components/umac/mlme/mlo_mgr/src/wlan_t2lm_api.c

@@ -504,7 +504,8 @@ wlan_t2lm_validate_candidate(struct cnx_mgr *cm_ctx,
 	}
 
 	status = wlan_mlo_parse_bcn_prbresp_t2lm_ie(&t2lm_ctx,
-						    scan_entry->ie_list.t2lm[0]);
+					util_scan_entry_t2lm(scan_entry),
+					util_scan_entry_t2lm_len(scan_entry));
 	if (QDF_IS_STATUS_ERROR(status))
 		goto end;
 

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

@@ -3718,6 +3718,21 @@ void wlan_hdd_stop_sap(struct hdd_adapter *ap_adapter);
  */
 void wlan_hdd_start_sap(struct wlan_hdd_link_info *link_info, bool reinit);
 
+/**
+ * wlan_hdd_set_sap_beacon_protection() - this function will set beacon
+ * protection for SAP.
+ * @hdd_ctx: pointer to HDD context
+ * @link_info: Link info pointer
+ * @beacon: pointer to beacon data structure
+ *
+ * This function will enable beacon protection and cache the value in vdev
+ * priv object.
+ *
+ * Return: None
+ */
+void wlan_hdd_set_sap_beacon_protection(struct hdd_context *hdd_ctx,
+					struct wlan_hdd_link_info *link_info,
+					struct hdd_beacon_data *beacon);
 #ifdef QCA_CONFIG_SMP
 int wlan_hdd_get_cpu(void);
 #else

+ 208 - 113
core/hdd/src/wlan_hdd_cfg80211.c

@@ -712,7 +712,7 @@ static const struct ieee80211_txrx_stypes
 	},
 	[NL80211_IFTYPE_NAN] = {
 		.tx = 0xffff,
-		.rx = BIT(SIR_MAC_MGMT_AUTH),
+		.rx = BIT(SIR_MAC_MGMT_AUTH) | BIT(SIR_MAC_MGMT_ACTION),
 	},
 };
 
@@ -2259,6 +2259,10 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
 	},
 
 	FEATURE_TX_LATENCY_STATS_EVENTS
+	[QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT_INDEX] = {
+		.vendor_id = QCA_NL80211_VENDOR_ID,
+		.subcmd = QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT,
+	},
 };
 
 /**
@@ -3671,16 +3675,17 @@ static bool wlan_hdd_is_sap_acs_ch_width_320(struct sap_config *sap_config)
 	return sap_config->acs_cfg.ch_width == CH_WIDTH_320MHZ;
 }
 
-static void wlan_hdd_set_chandef(struct wlan_objmgr_vdev *vdev,
-				 struct cfg80211_chan_def *chandef)
+static void wlan_hdd_set_chandef_for_11be(struct cfg80211_chan_def *chandef,
+					  struct wlan_channel *chan_info)
 {
-	if (vdev->vdev_mlme.des_chan->ch_width != CH_WIDTH_320MHZ)
+	if (chan_info->ch_width != CH_WIDTH_320MHZ)
 		return;
 
 	chandef->width = NL80211_CHAN_WIDTH_320;
 	/* Set center_freq1 to center frequency of complete 320MHz */
-	chandef->center_freq1 = vdev->vdev_mlme.des_chan->ch_cfreq2;
+	chandef->center_freq1 = chan_info->ch_cfreq2;
 }
+
 #else /* !WLAN_FEATURE_11BE */
 static inline
 void wlan_hdd_set_sap_acs_ch_width_320(struct sap_config *sap_config)
@@ -3693,8 +3698,9 @@ bool wlan_hdd_is_sap_acs_ch_width_320(struct sap_config *sap_config)
 	return false;
 }
 
-static inline void wlan_hdd_set_chandef(struct wlan_objmgr_vdev *vdev,
-					struct cfg80211_chan_def *chandef)
+static inline void
+wlan_hdd_set_chandef_for_11be(struct cfg80211_chan_def *chandef,
+			      struct wlan_channel *chan_info)
 {
 }
 #endif /* WLAN_FEATURE_11BE */
@@ -28597,107 +28603,11 @@ bool hdd_is_legacy_connection(struct wlan_hdd_link_info *link_info)
 		return false;
 }
 
-static int __wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy,
-					   struct wireless_dev *wdev,
-					   struct cfg80211_chan_def *chandef,
-					   int link_id)
+static void
+wlan_hdd_update_chandef(struct cfg80211_chan_def *chandef,
+			enum phy_ch_width ch_width, uint32_t ch_cfreq2,
+			bool is_legacy_phymode)
 {
-	struct net_device *dev = wdev->netdev;
-	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
-	struct hdd_context *hdd_ctx;
-	bool is_legacy_phymode = false;
-	struct wlan_objmgr_vdev *vdev;
-	uint32_t chan_freq;
-	struct wlan_hdd_link_info *link_info;
-	uint8_t vdev_id;
-	enum phy_ch_width ch_width;
-	enum wlan_phymode peer_phymode;
-	struct hdd_station_ctx *sta_ctx = NULL;
-	struct ch_params ch_params = {0};
-
-	hdd_enter_dev(wdev->netdev);
-
-	if (hdd_validate_adapter(adapter))
-		return -EINVAL;
-
-	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
-	if (wlan_hdd_validate_context(hdd_ctx))
-		return -EINVAL;
-
-	if ((adapter->device_mode == QDF_STA_MODE) ||
-	    (adapter->device_mode == QDF_P2P_CLIENT_MODE)) {
-
-		if (!hdd_cm_is_vdev_associated(adapter->deflink))
-			return -EINVAL;
-
-		sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink);
-		if (sta_ctx->conn_info.dot11mode < eCSR_CFG_DOT11_MODE_11N)
-			is_legacy_phymode = true;
-
-	} else if ((adapter->device_mode == QDF_SAP_MODE) ||
-			(adapter->device_mode == QDF_P2P_GO_MODE)) {
-		struct hdd_ap_ctx *ap_ctx;
-
-		ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter->deflink);
-
-		if (!test_bit(SOFTAP_BSS_STARTED,
-			      &adapter->deflink->link_flags))
-			return -EINVAL;
-
-		switch (ap_ctx->sap_config.SapHw_mode) {
-		case eCSR_DOT11_MODE_11n:
-		case eCSR_DOT11_MODE_11n_ONLY:
-		case eCSR_DOT11_MODE_11ac:
-		case eCSR_DOT11_MODE_11ac_ONLY:
-		case eCSR_DOT11_MODE_11ax:
-		case eCSR_DOT11_MODE_11ax_ONLY:
-			is_legacy_phymode = false;
-			break;
-		default:
-			is_legacy_phymode = true;
-			break;
-		}
-	} else {
-		return -EINVAL;
-	}
-
-	vdev = wlan_key_get_link_vdev(adapter, WLAN_OSIF_ID, link_id);
-	if (!vdev)
-		return -EINVAL;
-
-	if (adapter->device_mode == QDF_STA_MODE) {
-		vdev_id = wlan_vdev_get_id(vdev);
-		link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
-		if (!link_info || !hdd_cm_is_vdev_associated(link_info)) {
-			wlan_key_put_link_vdev(vdev, WLAN_OSIF_ID);
-			return -EBUSY;
-		}
-		sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
-	}
-
-	chan_freq = vdev->vdev_mlme.des_chan->ch_freq;
-	chandef->center_freq1 = vdev->vdev_mlme.des_chan->ch_cfreq1;
-	chandef->center_freq2 = 0;
-	chandef->chan = ieee80211_get_channel(wiphy, chan_freq);
-
-	ch_width = vdev->vdev_mlme.des_chan->ch_width;
-	if (adapter->device_mode == QDF_STA_MODE ||
-	    adapter->device_mode == QDF_P2P_CLIENT_MODE) {
-		/* For STA/P2P CLI get the peer pymode as, in some IOT
-		 * cases VDEV BW will not be same as peer BW
-		 */
-		mlme_get_peer_phymode(hdd_ctx->psoc,
-				      sta_ctx->conn_info.bssid.bytes,
-				      &peer_phymode);
-		ch_width = wlan_mlme_get_ch_width_from_phymode(peer_phymode);
-	}
-
-	ch_params.ch_width = ch_width;
-	wlan_reg_set_channel_params_for_pwrmode(hdd_ctx->pdev,
-						chan_freq, 0, &ch_params,
-						REG_CURRENT_PWR_MODE);
-	chandef->center_freq1 = ch_params.mhz_freq_seg0;
-
 	switch (ch_width) {
 	case CH_WIDTH_20MHZ:
 		if (is_legacy_phymode)
@@ -28714,11 +28624,11 @@ static int __wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy,
 	case CH_WIDTH_160MHZ:
 		chandef->width = NL80211_CHAN_WIDTH_160;
 		/* Set center_freq1 to center frequency of complete 160MHz */
-		chandef->center_freq1 = vdev->vdev_mlme.des_chan->ch_cfreq2;
+		chandef->center_freq1 = ch_cfreq2;
 		break;
 	case CH_WIDTH_80P80MHZ:
 		chandef->width = NL80211_CHAN_WIDTH_80P80;
-		chandef->center_freq2 = vdev->vdev_mlme.des_chan->ch_cfreq2;
+		chandef->center_freq2 = ch_cfreq2;
 		break;
 	case CH_WIDTH_5MHZ:
 		chandef->width = NL80211_CHAN_WIDTH_5;
@@ -28730,17 +28640,202 @@ static int __wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy,
 		chandef->width = NL80211_CHAN_WIDTH_20;
 		break;
 	}
+}
+
+static int
+wlan_hdd_cfg80211_get_channel_sap(struct wiphy *wiphy,
+				  struct cfg80211_chan_def *chandef,
+				  struct hdd_adapter *adapter, int link_id)
+{
+	struct hdd_ap_ctx *ap_ctx;
+	struct wlan_objmgr_vdev *vdev;
+	bool is_legacy_phymode = false;
+	uint32_t chan_freq;
+	struct wlan_channel *des_chan;
+
+	if (!test_bit(SOFTAP_BSS_STARTED, &adapter->deflink->link_flags))
+		return -EINVAL;
+
+	ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(adapter->deflink);
+	switch (ap_ctx->sap_config.SapHw_mode) {
+	case eCSR_DOT11_MODE_11n:
+	case eCSR_DOT11_MODE_11n_ONLY:
+	case eCSR_DOT11_MODE_11ac:
+	case eCSR_DOT11_MODE_11ac_ONLY:
+	case eCSR_DOT11_MODE_11ax:
+	case eCSR_DOT11_MODE_11ax_ONLY:
+		is_legacy_phymode = false;
+		break;
+	default:
+		is_legacy_phymode = true;
+		break;
+	}
+
+	vdev = wlan_key_get_link_vdev(adapter, WLAN_OSIF_ID, link_id);
+	if (!vdev)
+		return -EINVAL;
+
+	des_chan = wlan_vdev_mlme_get_des_chan(vdev);
+	chan_freq = des_chan->ch_freq;
+	chandef->center_freq1 = des_chan->ch_cfreq1;
+	chandef->center_freq2 = 0;
+	chandef->chan = ieee80211_get_channel(wiphy, chan_freq);
 
-	wlan_hdd_set_chandef(vdev, chandef);
+	wlan_hdd_update_chandef(chandef, des_chan->ch_width,
+				des_chan->ch_cfreq2, is_legacy_phymode);
+
+	wlan_hdd_set_chandef_for_11be(chandef, des_chan);
 
 	wlan_key_put_link_vdev(vdev, WLAN_OSIF_ID);
 
-	hdd_debug("primary_freq:%d, ch_width:%d, center_freq1:%d, center_freq2:%d",
-		  chan_freq, chandef->width, chandef->center_freq1,
-		  chandef->center_freq2);
+	hdd_debug("vdev: %d, freq:%d, ch_width:%d, c_freq1:%d, c_freq2:%d",
+		  wlan_vdev_get_id(vdev), chan_freq, chandef->width,
+		  chandef->center_freq1, chandef->center_freq2);
 	return 0;
 }
 
+static int wlan_hdd_cfg80211_get_vdev_chan_info(struct hdd_context *hdd_ctx,
+						struct wlan_objmgr_vdev *vdev,
+						int link_id,
+						struct wlan_channel *chan_info)
+{
+	struct hdd_station_ctx *sta_ctx = NULL;
+	struct ch_params ch_params = {0};
+	struct wlan_hdd_link_info *link_info;
+	enum wlan_phymode peer_phymode;
+	uint8_t vdev_id;
+	struct wlan_channel *des_chan;
+
+	vdev_id = wlan_vdev_get_id(vdev);
+	link_info = hdd_get_link_info_by_vdev(hdd_ctx, vdev_id);
+	if (!link_info) {
+		hdd_debug("link_info is null");
+		return -EBUSY;
+	}
+
+	des_chan = wlan_vdev_mlme_get_des_chan(vdev);
+	chan_info->ch_freq = des_chan->ch_freq;
+	chan_info->ch_cfreq1 = des_chan->ch_cfreq1;
+	chan_info->ch_cfreq2 = des_chan->ch_cfreq2;
+	chan_info->ch_width = des_chan->ch_width;
+
+	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
+	/* For STA/P2P CLI get the peer pymode as, in some IOT
+	 * cases VDEV BW will not be same as peer BW
+	 */
+	mlme_get_peer_phymode(hdd_ctx->psoc, sta_ctx->conn_info.bssid.bytes,
+			      &peer_phymode);
+	chan_info->ch_width =
+			wlan_mlme_get_ch_width_from_phymode(peer_phymode);
+	ch_params.ch_width = chan_info->ch_width;
+	wlan_reg_set_channel_params_for_pwrmode(hdd_ctx->pdev,
+						chan_info->ch_freq, 0,
+						&ch_params,
+						REG_CURRENT_PWR_MODE);
+	chan_info->ch_cfreq1 = ch_params.mhz_freq_seg0;
+	chan_info->ch_cfreq2 = ch_params.mhz_freq_seg1;
+
+	hdd_debug("vdev: %d, freq: %d, freq1: %d, freq2: %d, ch_width: %d",
+		  vdev_id, chan_info->ch_freq, chan_info->ch_cfreq1,
+		  chan_info->ch_cfreq2, chan_info->ch_width);
+	return 0;
+}
+
+static int
+wlan_hdd_cfg80211_get_channel_sta(struct wiphy *wiphy,
+				  struct cfg80211_chan_def *chandef,
+				  struct hdd_context *hdd_ctx,
+				  struct hdd_adapter *adapter, int link_id)
+{
+	struct hdd_station_ctx *sta_ctx = NULL;
+	struct wlan_objmgr_vdev *vdev;
+	bool is_legacy_phymode = false;
+	struct wlan_channel chan_info;
+	int ret = 0;
+
+	if (!hdd_cm_is_vdev_associated(adapter->deflink)) {
+		hdd_debug("vdev not associated");
+		return -EINVAL;
+	}
+
+	sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(adapter->deflink);
+	if (sta_ctx->conn_info.dot11mode < eCSR_CFG_DOT11_MODE_11N)
+		is_legacy_phymode = true;
+
+	vdev = hdd_objmgr_get_vdev_by_user(adapter->deflink, WLAN_OSIF_ID);
+	if (!vdev) {
+		hdd_debug("vdev null");
+		return -EINVAL;
+	}
+
+	if (wlan_vdev_mlme_is_mlo_vdev(vdev)) {
+		ret = mlo_mgr_get_per_link_chan_info(vdev, link_id, &chan_info);
+		if (ret != 0)
+			goto release;
+	} else {
+		ret = wlan_hdd_cfg80211_get_vdev_chan_info(hdd_ctx, vdev,
+							   link_id,
+							   &chan_info);
+		if (ret != 0)
+			goto release;
+	}
+
+	chandef->chan = ieee80211_get_channel(wiphy, chan_info.ch_freq);
+	chandef->center_freq1 = chan_info.ch_cfreq1;
+	chandef->center_freq2 = 0;
+
+	wlan_hdd_update_chandef(chandef, chan_info.ch_width,
+				chan_info.ch_cfreq2, is_legacy_phymode);
+
+	wlan_hdd_set_chandef_for_11be(chandef, &chan_info);
+
+	hdd_debug("freq:%d, ch_width:%d, c_freq1:%d, c_freq2:%d",
+		  chan_info.ch_freq, chandef->width, chandef->center_freq1,
+		  chandef->center_freq2);
+
+release:
+	hdd_objmgr_put_vdev_by_user(vdev, WLAN_OSIF_ID);
+	return ret;
+}
+
+static int __wlan_hdd_cfg80211_get_channel(struct wiphy *wiphy,
+					   struct wireless_dev *wdev,
+					   struct cfg80211_chan_def *chandef,
+					   int link_id)
+{
+	struct net_device *dev = wdev->netdev;
+	struct hdd_adapter *adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	struct hdd_context *hdd_ctx;
+	int ret = 0;
+
+	if (hdd_validate_adapter(adapter))
+		return -EINVAL;
+
+	hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return -EINVAL;
+
+	hdd_debug("get channel for link id: %d, device mode: %d", link_id,
+		  adapter->device_mode);
+
+	switch (adapter->device_mode) {
+	case QDF_SAP_MODE:
+	case QDF_P2P_GO_MODE:
+		ret = wlan_hdd_cfg80211_get_channel_sap(wiphy, chandef,
+							adapter, link_id);
+		break;
+	case QDF_STA_MODE:
+	case QDF_P2P_CLIENT_MODE:
+		ret = wlan_hdd_cfg80211_get_channel_sta(wiphy, chandef, hdd_ctx,
+							adapter, link_id);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
 /**
  * wlan_hdd_cfg80211_get_channel() - API to process cfg80211 get_channel request
  * @wiphy: Pointer to wiphy

+ 1 - 9
core/hdd/src/wlan_hdd_driver_ops.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2015-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -310,14 +310,6 @@ static void hdd_psoc_shutdown_notify(struct hdd_context *hdd_ctx)
 	hdd_enter();
 	wlan_cfg80211_cleanup_scan_queue(hdd_ctx->pdev, NULL);
 
-	if (ucfg_ipa_is_enabled()) {
-		ucfg_ipa_uc_force_pipe_shutdown(hdd_ctx->pdev);
-
-		if (pld_is_fw_rejuvenate(hdd_ctx->parent_dev) ||
-		    pld_is_pdr(hdd_ctx->parent_dev))
-			ucfg_ipa_fw_rejuvenate_send_msg(hdd_ctx->pdev);
-	}
-
 	cds_shutdown_notifier_call();
 	cds_shutdown_notifier_purge();
 

+ 4 - 25
core/hdd/src/wlan_hdd_hostapd.c

@@ -1788,11 +1788,13 @@ static void hdd_hostapd_set_sap_key(struct hdd_adapter *adapter)
 	struct wlan_crypto_key *crypto_key;
 	uint8_t key_index;
 
-	for (key_index = 0; key_index < WLAN_CRYPTO_MAXKEYIDX; ++key_index) {
+	for (key_index = 0; key_index < WLAN_CRYPTO_TOTAL_KEYIDX; ++key_index) {
 		crypto_key = wlan_crypto_get_key(adapter->deflink->vdev,
 						 key_index);
 		if (!crypto_key)
 			continue;
+
+		hdd_debug("key idx %d", key_index);
 		ucfg_crypto_set_key_req(adapter->deflink->vdev, crypto_key,
 					WLAN_CRYPTO_KEY_TYPE_GROUP);
 		wma_update_set_key(adapter->deflink->vdev_id, false, key_index,
@@ -6406,7 +6408,6 @@ int wlan_hdd_cfg80211_start_bss(struct wlan_hdd_link_info *link_info,
 	bool bval = false;
 	bool enable_dfs_scan = true;
 	bool deliver_start_evt = true;
-	struct s_ext_cap p_ext_cap = {0};
 	enum reg_phymode reg_phy_mode, updated_phy_mode;
 	struct sap_context *sap_ctx;
 	struct wlan_objmgr_vdev *vdev;
@@ -6541,29 +6542,7 @@ int wlan_hdd_cfg80211_start_bss(struct wlan_hdd_link_info *link_info,
 
 	ucfg_policy_mgr_get_mcc_scc_switch(hdd_ctx->psoc, &mcc_to_scc_switch);
 
-	ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_EXTCAP, beacon->tail,
-				      beacon->tail_len);
-	if (ie && (ie[0] != DOT11F_EID_EXTCAP ||
-		   ie[1] > DOT11F_IE_EXTCAP_MAX_LEN)) {
-		hdd_err("Invalid IEs eid: %d elem_len: %d", ie[0], ie[1]);
-		ret = -EINVAL;
-		goto error;
-	}
-
-	if (ie) {
-		bool target_bigtk_support = false;
-
-		memcpy(&p_ext_cap, &ie[2], (ie[1] > sizeof(p_ext_cap)) ?
-		       sizeof(p_ext_cap) : ie[1]);
-
-		hdd_debug("beacon protection %d",
-			  p_ext_cap.beacon_protection_enable);
-
-		ucfg_mlme_get_bigtk_support(hdd_ctx->psoc,
-					    &target_bigtk_support);
-		if (target_bigtk_support && p_ext_cap.beacon_protection_enable)
-			mlme_set_bigtk_support(vdev, true);
-	}
+	wlan_hdd_set_sap_beacon_protection(hdd_ctx, link_info, beacon);
 
 	/* Overwrite second AP's channel with first only when:
 	 * 1. If operating mode is single mac

+ 101 - 0
core/hdd/src/wlan_hdd_ioctl.c

@@ -55,6 +55,8 @@
 #define SIOCIOCTLTX99 (SIOCDEVPRIVATE+13)
 #endif
 
+#define SIZE_OF_WIFI6E_CHAN_LIST       512
+
 /*
  * Size of Driver command strings from upper layer
  */
@@ -2646,6 +2648,102 @@ static int drv_cmd_set_wmmps(struct wlan_hdd_link_info *link_info,
 	return hdd_wmmps_helper(link_info->adapter, command);
 }
 
+#ifdef CONFIG_BAND_6GHZ
+/**
+ * drv_cmd_get_wifi6e_channels() - Handler for GET_WIFI6E_CHANNELS driver
+ *                                 command
+ * @link_info: Link info pointer in adapter
+ * @hdd_ctx: pointer to hdd context
+ * @command: command name
+ * @command_len: command buffer length
+ * @priv_data: output pointer to hold current country code
+ *
+ * Return: On success 0, negative value on error.
+ */
+static int drv_cmd_get_wifi6e_channels(struct wlan_hdd_link_info *link_info,
+				       struct hdd_context *hdd_ctx,
+				       uint8_t *command,
+				       uint8_t command_len,
+				       struct hdd_priv_data *priv_data)
+{
+	uint8_t power_type;
+	char extra[SIZE_OF_WIFI6E_CHAN_LIST] = {0};
+	int i, ret, copied_length = 0;
+	enum channel_state state;
+	struct regulatory_channel *chan_list;
+	size_t max_buf_len = QDF_MIN(priv_data->total_len,
+				     SIZE_OF_WIFI6E_CHAN_LIST);
+	QDF_STATUS status;
+
+	if (wlan_hdd_validate_context(hdd_ctx))
+		return -EINVAL;
+
+	ret = kstrtou8(command + command_len + 1, 10, &power_type);
+	if (ret) {
+		hdd_err("error %d parsing userspace 6 GHz power type parameter",
+			ret);
+		return -EINVAL;
+	}
+
+	switch (power_type) {
+	case 0:
+		power_type = REG_CLI_DEF_LPI;
+		break;
+	case 1:
+		power_type = REG_CLI_DEF_VLP;
+		break;
+	case 2:
+		power_type = REG_CLI_DEF_SP;
+		break;
+	default:
+		hdd_err("The power type : %u, is incorrect", power_type);
+		return -EINVAL;
+	}
+
+	chan_list = qdf_mem_malloc(NUM_CHANNELS * sizeof(*chan_list));
+	if (!chan_list)
+		return -ENOMEM;
+
+	status = wlan_reg_get_pwrmode_chan_list(hdd_ctx->pdev, chan_list,
+						power_type);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("Failed to get wifi6e channel list for given power type %u",
+			power_type);
+		ret =  qdf_status_to_os_return(status);
+		goto free;
+	}
+
+	for (i = 0; i < NUM_6GHZ_CHANNELS && copied_length < max_buf_len - 1;
+	     i++) {
+		state = chan_list[i + MIN_6GHZ_CHANNEL].state;
+		if (state == CHANNEL_STATE_INVALID ||
+		    state == CHANNEL_STATE_DISABLE)
+			continue;
+		copied_length += scnprintf(extra + copied_length,
+				max_buf_len - copied_length, "%u ",
+				chan_list[i + MIN_6GHZ_CHANNEL].chan_num);
+	}
+
+	if (copied_length == 0) {
+		hdd_err("No Channel List found for given power type %u",
+			power_type);
+		ret = -EINVAL;
+		goto free;
+	}
+
+	if (copy_to_user(priv_data->buf, &extra, copied_length + 1)) {
+		hdd_err("failed to copy data to user buffer");
+		ret = -EFAULT;
+		goto free;
+	}
+
+	hdd_debug("Power type = %u, Data = %s", power_type, extra);
+free:
+	qdf_mem_free(chan_list);
+	return ret;
+}
+#endif
+
 static inline int __drv_cmd_country(struct wlan_hdd_link_info *link_info,
 				    struct hdd_context *hdd_ctx,
 				    uint8_t *command,
@@ -7342,6 +7440,9 @@ static const struct hdd_drv_cmd hdd_drv_cmds[] = {
 	{"BTCOEXSCAN-START",          drv_cmd_dummy, false},
 	{"BTCOEXSCAN-STOP",           drv_cmd_dummy, false},
 	{"GET_SOFTAP_LINK_SPEED",     drv_cmd_get_sap_go_linkspeed, true},
+#ifdef CONFIG_BAND_6GHZ
+	{"GET_WIFI6E_CHANNELS",       drv_cmd_get_wifi6e_channels, true},
+#endif
 };
 
 /**

+ 126 - 15
core/hdd/src/wlan_hdd_main.c

@@ -250,6 +250,7 @@
 #include "cdp_txrx_mon.h"
 #include "os_if_ll_sap.h"
 #include "wlan_p2p_ucfg_api.h"
+#include "wlan_crypto_obj_mgr_i.h"
 
 #ifdef MULTI_CLIENT_LL_SUPPORT
 #define WLAM_WLM_HOST_DRIVER_PORT_ID 0xFFFFFF
@@ -9719,6 +9720,7 @@ static void hdd_stop_sap_go_adapter(struct hdd_adapter *adapter)
 	struct wlan_hdd_link_info *link_info = adapter->deflink;
 	QDF_STATUS status = QDF_STATUS_SUCCESS;
 	struct hdd_context *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
+	uint8_t link_id;
 
 	mode = adapter->device_mode;
 	ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(link_info);
@@ -9790,6 +9792,13 @@ static void hdd_stop_sap_go_adapter(struct hdd_adapter *adapter)
 		clear_bit(SOFTAP_INIT_DONE, &link_info->link_flags);
 		qdf_mem_free(ap_ctx->beacon);
 		ap_ctx->beacon = NULL;
+
+		if (vdev) {
+			link_id = wlan_vdev_get_link_id(vdev);
+			ucfg_crypto_free_key_by_link_id(hdd_ctx->psoc,
+							&link_info->link_addr,
+							link_id);
+		}
 	}
 	/* Clear all the cached sta info */
 	hdd_clear_cached_sta_info(adapter);
@@ -17763,37 +17772,75 @@ QDF_STATUS hdd_md_bl_evt_cb(void *ctx, struct sir_md_bl_evt *event)
 }
 #endif /* WLAN_FEATURE_MOTION_DETECTION */
 
-/**
- * hdd_ssr_on_pagefault_cb - Callback to trigger SSR because
- * of host wake up by firmware with reason pagefault
- *
- * Return: None
- */
-static void hdd_ssr_on_pagefault_cb(void)
+static QDF_STATUS hdd_ssr_on_pagefault_cb(struct hdd_context *hdd_ctx)
 {
 	uint32_t ssr_frequency_on_pagefault;
-	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
-	qdf_time_t curr_time;
+	qdf_time_t curr_time, ssr_threshold;
 
 	hdd_enter();
 
 	if (!hdd_ctx)
-		return;
+		return QDF_STATUS_E_NULL_VALUE;
 
 	ssr_frequency_on_pagefault =
 		ucfg_pmo_get_ssr_frequency_on_pagefault(hdd_ctx->psoc);
 
-	curr_time = qdf_get_time_of_the_day_ms();
+	curr_time = qdf_get_system_uptime();
+	ssr_threshold = qdf_system_msecs_to_ticks(ssr_frequency_on_pagefault);
 
 	if (!hdd_ctx->last_pagefault_ssr_time ||
-	    (curr_time - hdd_ctx->last_pagefault_ssr_time) >=
-					ssr_frequency_on_pagefault) {
+	    (curr_time - hdd_ctx->last_pagefault_ssr_time) >= ssr_threshold) {
 		hdd_info("curr_time %lu last_pagefault_ssr_time %lu ssr_frequency %d",
 			 curr_time, hdd_ctx->last_pagefault_ssr_time,
-			 ssr_frequency_on_pagefault);
+			 ssr_threshold);
 		hdd_ctx->last_pagefault_ssr_time = curr_time;
 		cds_trigger_recovery(QDF_HOST_WAKEUP_REASON_PAGEFAULT);
+
+		return QDF_STATUS_SUCCESS;
 	}
+
+	return QDF_STATUS_E_AGAIN;
+}
+
+#define FW_PAGE_FAULT_IDX QCA_NL80211_VENDOR_SUBCMD_FW_PAGE_FAULT_REPORT_INDEX
+static QDF_STATUS hdd_send_pagefault_report_to_user(struct hdd_context *hdd_ctx,
+						    void *buf, uint32_t buf_len)
+{
+	struct sk_buff *event_buf;
+	int flags = cds_get_gfp_flags();
+	uint8_t *ev_data = buf;
+	uint16_t event_len = NLMSG_HDRLEN + buf_len;
+
+	event_buf = wlan_cfg80211_vendor_event_alloc(hdd_ctx->wiphy, NULL,
+						     event_len,
+						     FW_PAGE_FAULT_IDX, flags);
+	if (!event_buf) {
+		hdd_err("wlan_cfg80211_vendor_event_alloc failed");
+		return QDF_STATUS_E_NOMEM;
+	}
+
+	if (nla_put(event_buf, QCA_WLAN_VENDOR_ATTR_FW_PAGE_FAULT_REPORT_DATA,
+		    buf_len, ev_data)) {
+		hdd_debug("Failed to fill pagefault blob data");
+		wlan_cfg80211_vendor_free_skb(event_buf);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	wlan_cfg80211_vendor_event(event_buf, flags);
+	return QDF_STATUS_SUCCESS;
+}
+
+static QDF_STATUS hdd_pagefault_action_cb(void *buf, uint32_t buf_len)
+{
+	struct hdd_context *hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+
+	if (!hdd_ctx)
+		return QDF_STATUS_E_NULL_VALUE;
+
+	if (wlan_pmo_enable_ssr_on_page_fault(hdd_ctx->psoc))
+		return hdd_ssr_on_pagefault_cb(hdd_ctx);
+
+	return hdd_send_pagefault_report_to_user(hdd_ctx, buf, buf_len);
 }
 
 /**
@@ -17904,7 +17951,7 @@ int hdd_register_cb(struct hdd_context *hdd_ctx)
 	sme_async_oem_event_init(mac_handle,
 				 hdd_oem_event_async_cb);
 
-	sme_register_ssr_on_pagefault_cb(mac_handle, hdd_ssr_on_pagefault_cb);
+	sme_register_pagefault_cb(mac_handle, hdd_pagefault_action_cb);
 
 	hdd_exit();
 
@@ -18466,6 +18513,65 @@ wlan_hdd_mlo_sap_reinit(struct wlan_hdd_link_info *link_info)
 }
 #endif
 
+void wlan_hdd_set_sap_beacon_protection(struct hdd_context *hdd_ctx,
+					struct wlan_hdd_link_info *link_info,
+					struct hdd_beacon_data *beacon)
+{
+	const uint8_t *ie = NULL;
+	struct s_ext_cap *p_ext_cap;
+	struct wlan_objmgr_vdev *vdev;
+	bool target_bigtk_support = false;
+	uint8_t vdev_id;
+	uint8_t ie_len;
+
+	if (!beacon) {
+		hdd_err("beacon is null");
+		return;
+	}
+
+	ie = wlan_get_ie_ptr_from_eid(DOT11F_EID_EXTCAP, beacon->tail,
+				      beacon->tail_len);
+	if (!ie) {
+		hdd_err("IE is null");
+		return;
+	}
+
+	if (ie[1] > DOT11F_IE_EXTCAP_MAX_LEN ||
+	    ie[1] < DOT11F_IE_EXTCAP_MIN_LEN) {
+		hdd_err("Invalid IEs eid: %d elem_len: %d", ie[0], ie[1]);
+		return;
+	}
+
+	p_ext_cap = qdf_mem_malloc(sizeof(*p_ext_cap));
+	if (!p_ext_cap)
+		return;
+
+	ie_len = (ie[1] > sizeof(*p_ext_cap)) ? sizeof(*p_ext_cap) : ie[1];
+
+	qdf_mem_copy(p_ext_cap, &ie[2], ie_len);
+
+	vdev = hdd_objmgr_get_vdev_by_user(link_info, WLAN_HDD_ID_OBJ_MGR);
+	if (!vdev) {
+		hdd_err("vdev is null");
+		goto end;
+	}
+
+	vdev_id = wlan_vdev_get_id(vdev);
+
+	hdd_debug("vdev %d beacon protection %d", vdev_id,
+		  p_ext_cap->beacon_protection_enable);
+
+	ucfg_mlme_get_bigtk_support(hdd_ctx->psoc, &target_bigtk_support);
+
+	if (target_bigtk_support && p_ext_cap->beacon_protection_enable)
+		mlme_set_bigtk_support(vdev, true);
+
+	hdd_objmgr_put_vdev_by_user(vdev, WLAN_HDD_ID_OBJ_MGR);
+
+end:
+	qdf_mem_free(p_ext_cap);
+}
+
 void wlan_hdd_start_sap(struct wlan_hdd_link_info *link_info, bool reinit)
 {
 	struct hdd_ap_ctx *ap_ctx;
@@ -18510,6 +18616,8 @@ void wlan_hdd_start_sap(struct wlan_hdd_link_info *link_info, bool reinit)
 	if (QDF_IS_STATUS_ERROR(qdf_status))
 		goto end;
 
+	wlan_hdd_set_sap_beacon_protection(hdd_ctx, link_info, ap_ctx->beacon);
+
 	hdd_debug("Waiting for SAP to start");
 	qdf_status = qdf_wait_single_event(&hostapd_state->qdf_event,
 					SME_CMD_START_BSS_TIMEOUT);
@@ -19626,6 +19734,9 @@ void hdd_component_psoc_close(struct wlan_objmgr_psoc *psoc)
 	ucfg_fwol_psoc_close(psoc);
 	ucfg_dlm_psoc_close(psoc);
 	ucfg_mlme_psoc_close(psoc);
+
+	if (!cds_is_driver_recovering())
+		ucfg_crypto_flush_entries(psoc);
 }
 
 void hdd_component_psoc_enable(struct wlan_objmgr_psoc *psoc)

+ 22 - 4
core/hdd/src/wlan_hdd_mlo.c

@@ -824,6 +824,14 @@ wlan_hdd_cached_link_state_request(struct hdd_adapter *adapter,
 	int errno;
 	struct qdf_mac_addr *mld_addr;
 	uint8_t link_iter = 0;
+	struct mlo_link_info *ml_link_info;
+	struct wlan_mlo_dev_context *mlo_ctx;
+
+	mlo_ctx = vdev->mlo_dev_ctx;
+	if (!mlo_ctx) {
+		hdd_err("null mlo_dev_ctx");
+		return -EINVAL;
+	}
 
 	hdd_adapter_for_each_link_info(adapter, link_info) {
 
@@ -835,8 +843,6 @@ wlan_hdd_cached_link_state_request(struct hdd_adapter *adapter,
 		sta_ctx = WLAN_HDD_GET_STATION_CTX_PTR(link_info);
 		link_state_event.link_info[link_iter].link_id =
 				sta_ctx->conn_info.ieee_link_id;
-		link_state_event.link_info[link_iter].link_status =
-				link_info->is_mlo_vdev_active;
 		link_state_event.link_info[link_iter].vdev_id =
 				link_info->vdev_id;
 		link_state_event.link_info[link_iter].chan_freq =
@@ -845,11 +851,23 @@ wlan_hdd_cached_link_state_request(struct hdd_adapter *adapter,
 		if (sta_ctx->conn_info.ieee_link_id == WLAN_INVALID_LINK_ID)
 			continue;
 
+		ml_link_info = mlo_mgr_get_ap_link_by_link_id(
+				mlo_ctx,
+				sta_ctx->conn_info.ieee_link_id);
+		if (!ml_link_info) {
+			hdd_debug("link: %d info does not exist",
+				  sta_ctx->conn_info.ieee_link_id);
+			return -EINVAL;
+		}
+
+		link_state_event.link_info[link_iter].link_status =
+			ml_link_info->is_link_active;
+
 		link_iter++;
 
 		hdd_debug_rl("vdev id %d sta_ctx->conn_info.ieee_link_id %d is_mlo_vdev_active %d ",
 			     link_info->vdev_id, sta_ctx->conn_info.ieee_link_id,
-			     link_info->is_mlo_vdev_active);
+			     ml_link_info->is_link_active);
 	}
 
 	link_state_event.num_mlo_vdev_link_info = link_iter;
@@ -909,7 +927,7 @@ static QDF_STATUS wlan_hdd_link_state_request(struct hdd_adapter *adapter,
 		.dealloc = NULL,
 	};
 
-	if (!wiphy || !vdev)
+	if (!wiphy || !vdev || !wlan_vdev_mlme_is_mlo_vdev(vdev))
 		return status;
 
 	if (adapter->device_mode != QDF_STA_MODE)

+ 22 - 1
core/hdd/src/wlan_hdd_p2p.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2024 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
@@ -1133,6 +1133,8 @@ __hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter,
 	struct hdd_adapter *assoc_adapter;
 	bool eht_capab;
 	struct hdd_ap_ctx *ap_ctx;
+	struct action_frm_hdr *action_hdr;
+	tpSirMacVendorSpecificPublicActionFrameHdr vendor_specific;
 
 	hdd_debug("Frame Type = %d Frame Length = %d freq = %d",
 		  frame_type, frm_len, rx_freq);
@@ -1171,6 +1173,24 @@ __hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter,
 		}
 	}
 
+	if (type == WLAN_FC0_TYPE_MGMT && sub_type == WLAN_FC0_STYPE_ACTION &&
+	    frm_len >= (sizeof(struct wlan_frame_hdr) +
+			sizeof(*vendor_specific))) {
+		action_hdr = (struct action_frm_hdr *)(pb_frames +
+						sizeof(struct wlan_frame_hdr));
+		vendor_specific =
+			(tpSirMacVendorSpecificPublicActionFrameHdr)action_hdr;
+		if (is_nan_oui(vendor_specific->Oui)) {
+			adapter = hdd_get_adapter(hdd_ctx, QDF_NAN_DISC_MODE);
+			if (!adapter) {
+				hdd_err("NAN adapter is null");
+				return;
+			}
+
+			goto check_adapter;
+		}
+	}
+
 	/* Get adapter from Destination mac address of the frame */
 	if (type == SIR_MAC_MGMT_FRAME &&
 	    sub_type != SIR_MAC_MGMT_PROBE_REQ && !is_pasn_auth_frame &&
@@ -1209,6 +1229,7 @@ __hdd_indicate_mgmt_frame_to_user(struct hdd_adapter *adapter,
 		}
 	}
 
+check_adapter:
 	if (!adapter->dev) {
 		hdd_err("adapter->dev is NULL");
 		return;

+ 8 - 0
core/hdd/src/wlan_hdd_power.c

@@ -1881,6 +1881,14 @@ QDF_STATUS hdd_wlan_shutdown(void)
 	if (!hdd_ctx)
 		return QDF_STATUS_E_FAILURE;
 
+	if (ucfg_ipa_is_enabled()) {
+		ucfg_ipa_uc_force_pipe_shutdown(hdd_ctx->pdev);
+
+		if (pld_is_fw_rejuvenate(hdd_ctx->parent_dev) ||
+		    pld_is_pdr(hdd_ctx->parent_dev))
+			ucfg_ipa_fw_rejuvenate_send_msg(hdd_ctx->pdev);
+	}
+
 	hdd_set_connection_in_progress(false);
 
 	hdd_debug("Invoking packetdump deregistration API");

+ 3 - 1
core/hdd/src/wlan_hdd_sar_limits.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -325,7 +325,9 @@ static u32 hdd_to_nl_sar_version(enum sar_version hdd_sar_version)
 	case (SAR_VERSION_3):
 		return QCA_WLAN_VENDOR_SAR_VERSION_3;
 	case (SAR_VERSION_4):
+		return QCA_WLAN_VENDOR_SAR_VERSION_4;
 	case (SAR_VERSION_5):
+		return QCA_WLAN_VENDOR_SAR_VERSION_5;
 	case (SAR_VERSION_6):
 		return QCA_WLAN_VENDOR_SAR_VERSION_1;
 	default:

+ 3 - 0
core/hdd/src/wlan_hdd_stats.c

@@ -9886,6 +9886,9 @@ hdd_convert_roam_failures_reason(enum wlan_roam_failure_reason_code fail)
 	case ROAM_FAIL_REASON_UNABLE_TO_START_ROAM_HO:
 	case ROAM_FAIL_REASON_NO_AP_FOUND_AND_FINAL_BMISS_SENT:
 	case ROAM_FAIL_REASON_NO_CAND_AP_FOUND_AND_FINAL_BMISS_SENT:
+	case ROAM_FAIL_REASON_SCAN_CANCEL:
+	case ROAM_FAIL_REASON_SCREEN_ACTIVITY:
+	case ROAM_FAIL_REASON_OTHER_PRIORITY_ROAM_SCAN:
 	case ROAM_FAIL_REASON_UNKNOWN:
 		hdd_err("Invalid roam failures reason");
 		break;

+ 3 - 2
core/hdd/src/wlan_hdd_tx_power.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2020 The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2022-2024 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
@@ -387,7 +387,8 @@ next_link:
 		}
 
 		hdd_debug("%d tpc for bssid "QDF_MAC_ADDR_FMT" is_psd %d reg power %d 6ghz pwr type %d ap_constraint_power %d",
-			  i, link_bssid[i].bytes, reg_tpc_info[i].is_psd_power,
+			  i, QDF_MAC_ADDR_REF(link_bssid[i].bytes),
+			  reg_tpc_info[i].is_psd_power,
 			  reg_tpc_info[i].reg_max[0],
 			  reg_tpc_info[i].power_type_6g,
 			  reg_tpc_info[i].ap_constraint_power);

+ 3 - 3
core/mac/inc/qwlan_version.h

@@ -32,9 +32,9 @@
 #define QWLAN_VERSION_MAJOR            5
 #define QWLAN_VERSION_MINOR            2
 #define QWLAN_VERSION_PATCH            1
-#define QWLAN_VERSION_EXTRA            "S"
-#define QWLAN_VERSION_BUILD            86
+#define QWLAN_VERSION_EXTRA            "N"
+#define QWLAN_VERSION_BUILD            87
 
-#define QWLAN_VERSIONSTR               "5.2.1.86S"
+#define QWLAN_VERSIONSTR               "5.2.1.87N"
 
 #endif /* QWLAN_VERSION_H */

+ 5 - 2
core/mac/src/pe/lim/lim_process_action_frame.c

@@ -2294,8 +2294,10 @@ void lim_process_action_frame_no_session(struct mac_context *mac, uint8_t *pBd)
 
 
 	pe_debug("Received an action frame category: %d action_id: %d",
-		 action_hdr->category, action_hdr->category ==
-		 ACTION_CATEGORY_PUBLIC ? action_hdr->actionID : 255);
+		 action_hdr->category, (action_hdr->category ==
+		 ACTION_CATEGORY_PUBLIC || action_hdr->category ==
+		 ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION) ?
+		 action_hdr->actionID : 255);
 
 	if (frame_len < sizeof(*action_hdr)) {
 		pe_debug("frame_len %d less than action frame header len",
@@ -2305,6 +2307,7 @@ void lim_process_action_frame_no_session(struct mac_context *mac, uint8_t *pBd)
 
 	switch (action_hdr->category) {
 	case ACTION_CATEGORY_PUBLIC:
+	case ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION:
 		if (action_hdr->actionID == PUB_ACTION_VENDOR_SPECIFIC) {
 			vendor_specific =
 				(tpSirMacVendorSpecificPublicActionFrameHdr)

+ 17 - 3
core/mac/src/pe/lim/lim_process_assoc_rsp_frame.c

@@ -1092,7 +1092,8 @@ void lim_send_join_fail_on_vdev(struct mac_context *mac_ctx,
 
 #ifdef WLAN_FEATURE_11BE_MLO
 static QDF_STATUS
-lim_gen_link_specific_probe_resp_from_assoc_resp(uint8_t *rx_pkt_info,
+lim_gen_link_specific_probe_resp_from_assoc_resp(struct mac_context *mac_ctx,
+						 uint8_t *rx_pkt_info,
 						 uint32_t frame_len,
 						 struct pe_session *session)
 {
@@ -1186,6 +1187,17 @@ lim_gen_link_specific_probe_resp_from_assoc_resp(uint8_t *rx_pkt_info,
 				goto mem_free;
 			}
 		}
+
+		status = lim_update_mlo_mgr_info(mac_ctx,
+						 session->vdev,
+						 &link_info->link_addr,
+						 link_info->link_id,
+						 link_info->chan_freq);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			pe_debug("failed to update mlo_mgr %d for link_id: %d",
+				 status, link_info->link_id);
+			goto mem_free;
+		}
 	}
 
 mem_free:
@@ -1196,7 +1208,8 @@ mem_free:
 }
 #else /* WLAN_FEATURE_11BE_MLO */
 static inline QDF_STATUS
-lim_gen_link_specific_probe_resp_from_assoc_resp(uint8_t *rx_pkt_info,
+lim_gen_link_specific_probe_resp_from_assoc_resp(struct mac_context *mac_ctx,
+						 uint8_t *rx_pkt_info,
 						 uint32_t frame_len,
 						 struct pe_session *session)
 {
@@ -1867,7 +1880,8 @@ lim_process_assoc_rsp_frame(struct mac_context *mac_ctx, uint8_t *rx_pkt_info,
 	 * Any failure during partner link probe resp generation, treat
 	 * it as connect failure and send deauth to AP.
 	 */
-	status = lim_gen_link_specific_probe_resp_from_assoc_resp(rx_pkt_info,
+	status = lim_gen_link_specific_probe_resp_from_assoc_resp(mac_ctx,
+								  rx_pkt_info,
 								  frame_body_len,
 								  session_entry);
 	if (QDF_IS_STATUS_ERROR(status)) {

+ 2 - 0
core/mac/src/pe/lim/lim_process_sme_req_messages.c

@@ -6309,6 +6309,8 @@ void lim_calculate_tpc(struct mac_context *mac,
 			}
 		} else {
 			max_tx_power = reg_max - local_constraint;
+			if (!max_tx_power)
+				max_tx_power = reg_max;
 		}
 
 		/* If TPE is present */

+ 22 - 3
core/mac/src/pe/lim/lim_send_sme_rsp_messages.c

@@ -59,6 +59,7 @@
 #include <../../core/src/wlan_cm_vdev_api.h>
 #include <wlan_mlo_mgr_sta.h>
 #include <spatial_reuse_api.h>
+#include <wlan_mlo_mgr_cmn.h>
 
 void lim_send_sme_rsp(struct mac_context *mac_ctx, uint16_t msg_type,
 		      tSirResultCodes result_code, uint8_t vdev_id)
@@ -1829,12 +1830,13 @@ static bool lim_is_csa_channel_allowed(struct mac_context *mac_ctx,
 	} else if (cnx_count > 2) {
 		is_allowed =
 		policy_mgr_allow_concurrency_csa(
-			mac_ctx->psoc, csa_freq,
+			mac_ctx->psoc,
 			policy_mgr_qdf_opmode_to_pm_con_mode(mac_ctx->psoc,
 							     mode,
 							     session_entry->vdev_id),
-			session_entry->vdev_id,
-			policy_mgr_get_bw(new_ch_width), false,
+			csa_freq,
+			policy_mgr_get_bw(new_ch_width),
+			session_entry->vdev_id, false,
 			CSA_REASON_UNKNOWN);
 	}
 
@@ -1931,6 +1933,12 @@ static void update_csa_link_info(struct wlan_objmgr_vdev *vdev,
 		 vdev_id, link_id);
 }
 
+static bool
+lim_mlo_is_csa_allow(struct wlan_objmgr_vdev *vdev, uint16_t csa_freq)
+{
+	return wlan_mlo_is_csa_allow(vdev, csa_freq);
+}
+
 #else
 static void lim_set_csa_chan_param_11be(struct pe_session *session,
 					struct csa_offload_params *csa_param,
@@ -1959,6 +1967,11 @@ static void update_csa_link_info(struct wlan_objmgr_vdev *vdev,
 {
 }
 
+static bool
+lim_mlo_is_csa_allow(struct wlan_objmgr_vdev *vdev, uint16_t csa_freq)
+{
+	return true;
+}
 #endif
 
 /**
@@ -2052,6 +2065,12 @@ void lim_handle_sta_csa_param(struct mac_context *mac_ctx,
 		pe_debug("Channel switch is not allowed");
 		goto err;
 	}
+
+	if (!lim_mlo_is_csa_allow(session_entry->vdev,
+				  csa_params->csa_chan_freq)) {
+		pe_debug("Channel switch for MLO vdev is not allowed");
+		goto err;
+	}
 	/*
 	 * on receiving channel switch announcement from AP, delete all
 	 * TDLS peers before leaving BSS and proceed for channel switch

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

@@ -11547,10 +11547,8 @@ lim_is_power_change_required_for_sta(struct mac_context *mac_ctx,
 		return false;
 	}
 
-	if (sta_session->curr_op_freq != sap_session->curr_op_freq) {
-		pe_err("STA and SAP are not in same frequency, do not change TPC power");
+	if (sta_session->curr_op_freq != sap_session->curr_op_freq)
 		return false;
-	}
 
 	wlan_reg_get_cur_6g_ap_pwr_type(mac_ctx->pdev, &ap_power_type_6g);
 

+ 7 - 5
core/sme/inc/sme_api.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2012-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -762,14 +762,16 @@ QDF_STATUS sme_neighbor_report_request(mac_handle_t mac_handle,
 		tpRrmNeighborRspCallbackInfo callbackInfo);
 
 /**
- * sme_register_ssr_on_pagefault_cb() - Register cb to trigger SSR on pagefault
+ * sme_register_pagefault_cb() - Register cb to handle host action on pagefault
  * @mac_handle: Opaque handle to the global MAC context.
- * @hdd_ssr_on_pagefault_cb: Callback which needs to be registered
+ * @hdd_pagefault_action_cb: Callback which needs to be registered
  *
  * Return: None
  */
-void sme_register_ssr_on_pagefault_cb(mac_handle_t mac_handle,
-				      void (*hdd_ssr_on_pagefault_cb)(void));
+void
+sme_register_pagefault_cb(mac_handle_t mac_handle,
+			  QDF_STATUS (*hdd_pagefault_action_cb)(void *buf,
+								uint32_t buf_len));
 
 /**
  * sme_deregister_ssr_on_pagefault_cb() - Deregister cb to trigger SSR on

+ 2 - 2
core/sme/inc/sme_internal.h

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2011-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -507,7 +507,7 @@ struct sme_context {
 			(const struct oem_data *oem_event_data);
 #endif
 
-	void (*ssr_on_pagefault_cb)(void);
+	QDF_STATUS (*pagefault_action_cb)(void *buf, uint32_t data);
 
 #ifdef MULTI_CLIENT_LL_SUPPORT
 	void (*latency_level_event_handler_cb)

+ 6 - 4
core/sme/src/common/sme_api.c

@@ -4058,8 +4058,10 @@ QDF_STATUS sme_neighbor_report_request(
 	return status;
 }
 
-void sme_register_ssr_on_pagefault_cb(mac_handle_t mac_handle,
-				      void (*hdd_ssr_on_pagefault_cb)(void))
+void
+sme_register_pagefault_cb(mac_handle_t mac_handle,
+			  QDF_STATUS (*hdd_pagefault_action_cb)(void *buf,
+								uint32_t data))
 {
 	QDF_STATUS status;
 	struct mac_context *mac = MAC_CONTEXT(mac_handle);
@@ -4068,7 +4070,7 @@ void sme_register_ssr_on_pagefault_cb(mac_handle_t mac_handle,
 
 	status = sme_acquire_global_lock(&mac->sme);
 	if (QDF_IS_STATUS_SUCCESS(status)) {
-		mac->sme.ssr_on_pagefault_cb = hdd_ssr_on_pagefault_cb;
+		mac->sme.pagefault_action_cb = hdd_pagefault_action_cb;
 		sme_release_global_lock(&mac->sme);
 	}
 
@@ -4084,7 +4086,7 @@ void sme_deregister_ssr_on_pagefault_cb(mac_handle_t mac_handle)
 
 	status = sme_acquire_global_lock(&mac->sme);
 	if (QDF_IS_STATUS_SUCCESS(status)) {
-		mac->sme.ssr_on_pagefault_cb = NULL;
+		mac->sme.pagefault_action_cb = NULL;
 		sme_release_global_lock(&mac->sme);
 	}
 

+ 60 - 6
core/wma/inc/wma.h

@@ -49,6 +49,7 @@
 #include "wmi.h"
 #include "wlan_cm_roam_public_struct.h"
 #include "target_if.h"
+#include <qdf_hang_event_notifier.h>
 
 /* Platform specific configuration for max. no. of fragments */
 #define QCA_OL_11AC_TX_MAX_FRAGS            2
@@ -802,6 +803,63 @@ struct wma_wlm_stats_data {
 };
 #endif
 
+#define WLAN_WMA_MAX_PF_SYM 50
+#define WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN QDF_HANG_EVENT_DATA_SIZE
+#define WLAN_WMA_PF_SYM_LEN 4
+#define WLAN_WMA_PF_SYM_CNT_LEN 1
+#define WLAN_WMA_PF_SYM_FLAGS_LEN 1
+#define WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN  (WLAN_WMA_PF_SYM_LEN + \
+					     WLAN_WMA_PF_SYM_CNT_LEN + \
+					     WLAN_WMA_PF_SYM_FLAGS_LEN)
+
+/*
+ * struct wow_pf_sym - WOW PF wakeup symbol info
+ * @symbol: Address of PF symbol
+ * @count: Count of PF symbol
+ * @flags: Flags associated with @symbol
+ */
+struct wow_pf_sym {
+	uint32_t symbol;
+	uint8_t count;
+	uint8_t flags;
+};
+
+/*
+ * struct wow_pf_wakeup_ev_data - WOW PF wakeup event data
+ * @pf_sym: Array of each unique PF symbol in wakeup event payload
+ * @num_pf_syms: Total unique symbols in event.
+ * @pending_pf_syms: Pending PF symbols to process
+ */
+struct wow_pf_wakeup_ev_data {
+	struct wow_pf_sym *pf_sym;
+	uint8_t num_pf_syms;
+	uint8_t pending_pf_syms;
+};
+
+/**
+ * struct wma_pf_sym - Per symbol PF data in PF symbol history
+ * @pf_sym: PF symbol info
+ * @pf_event_ts: Array of page fault event ts
+ */
+struct wma_pf_sym {
+	struct wow_pf_sym pf_sym;
+	qdf_time_t *pf_ev_ts;
+};
+
+/*
+ * struct wma_pf_sym_hist - System level FW PF symbol history
+ * @wma_pf_sym: Array of symbols in history.
+ * @pf_notify_buf_ptr: Pointer to APPS notify buffer
+ * @pf_notify_buf_len: Current data length of @pf_notify_buf_ptr
+ * @lock: Lock to access PF symbol history
+ */
+struct wma_pf_sym_hist {
+	struct wma_pf_sym wma_pf_sym[WLAN_WMA_MAX_PF_SYM];
+	uint8_t *pf_notify_buf_ptr;
+	uint32_t pf_notify_buf_len;
+	qdf_spinlock_t lock;
+};
+
 /**
  * struct t_wma_handle - wma context
  * @wmi_handle: wmi handle
@@ -920,10 +978,7 @@ struct wma_wlm_stats_data {
  * * @fw_therm_throt_support: FW Supports thermal throttling?
  * @eht_cap: 802.11be capabilities
  * @set_hw_mode_resp_status: Set HW mode response status
- * @pagefault_wakeups_ts: Stores timestamps at which host wakes up by fw
- * because of pagefaults
- * @num_page_fault_wakeups: Stores the number of times host wakes up by fw
- * because of pagefaults
+ * @wma_pf_hist: PF symbol history
  *
  * This structure is the global wma context.  It contains global wma
  * module parameters and handles of other modules.
@@ -1060,8 +1115,7 @@ typedef struct {
 	qdf_wake_lock_t sap_d3_wow_wake_lock;
 	qdf_wake_lock_t go_d3_wow_wake_lock;
 	enum set_hw_mode_status set_hw_mode_resp_status;
-	qdf_time_t *pagefault_wakeups_ts;
-	uint8_t num_page_fault_wakeups;
+	struct wma_pf_sym_hist wma_pf_hist;
 } t_wma_handle, *tp_wma_handle;
 
 /**

+ 17 - 8
core/wma/src/wma_dev_if.c

@@ -1668,11 +1668,19 @@ QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr,
 	bool peer_unmap_conf_support_enabled;
 	uint8_t peer_vdev_id;
 	struct peer_delete_cmd_params del_param = {0};
+	struct wma_txrx_node *iface;
 
-	if (!wma->interfaces[vdev_id].peer_count) {
+	if (vdev_id >= WLAN_MAX_VDEVS) {
+		wma_err("Invalid vdev_id %d", vdev_id);
+		QDF_BUG(0);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	iface = &wma->interfaces[vdev_id];
+	if (!iface->peer_count) {
 		wma_err("Can't remove peer with peer_addr "QDF_MAC_ADDR_FMT" vdevid %d peer_count %d",
 			QDF_MAC_ADDR_REF(peer_addr), vdev_id,
-			wma->interfaces[vdev_id].peer_count);
+			iface->peer_count);
 		QDF_ASSERT(0);
 		return QDF_STATUS_E_INVAL;
 	}
@@ -1685,14 +1693,14 @@ QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr,
 	if (!wma_objmgr_peer_exist(wma, peer_addr, &peer_vdev_id)) {
 		wma_err("peer doesn't exist peer_addr "QDF_MAC_ADDR_FMT" vdevid %d peer_count %d",
 			 QDF_MAC_ADDR_REF(peer_addr), vdev_id,
-			 wma->interfaces[vdev_id].peer_count);
+			 iface->peer_count);
 		return QDF_STATUS_E_INVAL;
 	}
 
 	if (peer_vdev_id != vdev_id) {
 		wma_err("peer "QDF_MAC_ADDR_FMT" is on vdev id %d but delete req on vdevid %d peer_count %d",
 			 QDF_MAC_ADDR_REF(peer_addr), peer_vdev_id, vdev_id,
-			 wma->interfaces[vdev_id].peer_count);
+			 iface->peer_count);
 		return QDF_STATUS_E_INVAL;
 	}
 	peer_unmap_conf_support_enabled =
@@ -1718,6 +1726,8 @@ QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr,
 			    0, 0);
 
 	del_param.vdev_id = vdev_id;
+	del_param.is_mlo_link_switch =
+		wlan_vdev_mlme_is_mlo_link_switch_in_progress(iface->vdev);
 	qdf_status = wmi_unified_peer_delete_send(wma->wmi_handle, peer_addr,
 						  &del_param);
 	if (QDF_IS_STATUS_ERROR(qdf_status)) {
@@ -1730,8 +1740,7 @@ QDF_STATUS wma_remove_peer(tp_wma_handle wma, uint8_t *mac_addr,
 
 peer_detach:
 	wma_debug("vdevid %d is detaching with peer_addr "QDF_MAC_ADDR_FMT" peer_count %d",
-		vdev_id, QDF_MAC_ADDR_REF(peer_addr),
-		wma->interfaces[vdev_id].peer_count);
+		vdev_id, QDF_MAC_ADDR_REF(peer_addr), iface->peer_count);
 	/* Copy peer mac to find and delete objmgr peer */
 	qdf_mem_copy(peer_mac, peer_addr, QDF_MAC_ADDR_SIZE);
 	if (no_fw_peer_delete &&
@@ -1759,9 +1768,9 @@ peer_detach:
 	}
 
 	wlan_release_peer_key_wakelock(wma->pdev, peer_mac);
-	wma_remove_objmgr_peer(wma, wma->interfaces[vdev_id].vdev, peer_mac);
+	wma_remove_objmgr_peer(wma, iface->vdev, peer_mac);
 
-	wma->interfaces[vdev_id].peer_count--;
+	iface->peer_count--;
 #undef PEER_ALL_TID_BITMASK
 
 	return qdf_status;

+ 382 - 58
core/wma/src/wma_features.c

@@ -3230,39 +3230,357 @@ static void wma_wake_event_log_reason(t_wma_handle *wma,
 	qdf_wma_wow_wakeup_stats_event(wma);
 }
 
-/**
- * wma_wow_wakeup_host_trigger_ssr() - Trigger SSR on host wakeup
- * @handle: wma handle
- * @reason: Host wakeup reason
- *
- * This function triggers SSR if host is woken up by fw with reason as pagefault
- *
- * Return: None
- */
-static void
-wma_wow_wakeup_host_trigger_ssr(t_wma_handle *wma, uint32_t reason)
+static QDF_STATUS wma_wow_pagefault_action_cb(void *buf)
 {
-	uint8_t pagefault_wakeups_for_ssr;
-	uint32_t interval_for_pagefault_wakeup_counts;
-	qdf_time_t curr_time;
 	struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE);
-	int i;
-	bool ignore_pf = true;
 
-	if (!mac) {
-		wma_debug("NULL mac ptr");
-		return;
+	return mac->sme.pagefault_action_cb(buf, WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN);
+}
+
+static QDF_STATUS
+wma_wow_pagefault_add_sym_to_event(tp_wma_handle wma, struct wow_pf_sym *pf_sym)
+{
+	uint8_t *buf_ptr = wma->wma_pf_hist.pf_notify_buf_ptr;
+	uint32_t buf_len = wma->wma_pf_hist.pf_notify_buf_len;
+
+	if (WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN - buf_len <
+	    WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN) {
+		wma_wow_pagefault_action_cb(buf_ptr);
+		buf_len = 0;
+	}
+
+	/* Mem zero to send buffer with zero padding */
+	if (!buf_len)
+		qdf_mem_zero(buf_ptr, WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN);
+
+	qdf_mem_copy(buf_ptr + buf_len, pf_sym,
+		     WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN);
+	buf_len += WLAN_WMA_PER_PF_SYM_NOTIFY_BUF_LEN;
+
+	wma->wma_pf_hist.pf_notify_buf_len = buf_len;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static void
+wma_wow_pagefault_add_new_sym_from_event(tp_wma_handle wma,
+					 struct wow_pf_wakeup_ev_data *pf_sym_list,
+					 qdf_time_t cur_time)
+{
+	uint8_t tbl_idx, ev_lst_idx, new_pf_idx, idx2;
+	uint8_t new_idx_cnt, cur_idx_cnt, ev_sym_cnt, pf_th;
+	uint8_t max_sym_count = WLAN_WMA_MAX_PF_SYM;
+	qdf_time_t new_idx_last_ts, cur_idx_last_ts;
+	qdf_time_t new_idx_old_ts, cur_idx_old_ts;
+	struct wma_pf_sym *cur_pf_entry, *new_pf_entry;
+	struct wma_pf_sym_hist *pf_sym_hist = &wma->wma_pf_hist;
+
+	pf_th = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc);
+	for (tbl_idx = 0; tbl_idx < max_sym_count; tbl_idx++) {
+		if (!pf_sym_list->pending_pf_syms)
+			break;
+
+		new_pf_idx = tbl_idx;
+		new_pf_entry = &pf_sym_hist->wma_pf_sym[tbl_idx];
+		new_idx_cnt = new_pf_entry->pf_sym.count;
+		new_idx_last_ts = new_pf_entry->pf_ev_ts[new_idx_cnt - 1];
+		new_idx_old_ts = new_pf_entry->pf_ev_ts[0];
+
+		for (ev_lst_idx = 0; ev_lst_idx < pf_sym_list->num_pf_syms;
+		     ev_lst_idx++) {
+			if (!pf_sym_list->pf_sym[ev_lst_idx].count)
+				continue;
+
+			/* Add the sym that already met threshold to the buf */
+			if (pf_sym_list->pf_sym[ev_lst_idx].count >= pf_th) {
+				wma_wow_pagefault_add_sym_to_event(wma,
+								   pf_sym_list->pf_sym);
+				pf_sym_list->pf_sym[ev_lst_idx].count = 0x0;
+				pf_sym_list->pending_pf_syms--;
+				continue;
+			}
+
+			ev_sym_cnt = pf_sym_list->pf_sym[ev_lst_idx].count;
+
+			/* If current entry is NULL, add the symbol here */
+			if (!new_idx_cnt)
+				goto add_sym;
+
+			/* Replace event if count is equal as current event
+			 * is latest and don't replace symbol from current event
+			 */
+			if (new_idx_cnt > ev_sym_cnt ||
+			    qdf_system_time_after_eq(new_idx_last_ts, cur_time))
+				break;
+
+			for (idx2 = tbl_idx + 1; idx2 < max_sym_count; idx2++) {
+				cur_pf_entry = &pf_sym_hist->wma_pf_sym[idx2];
+				cur_idx_cnt = cur_pf_entry->pf_sym.count;
+				cur_idx_last_ts =
+					cur_pf_entry->pf_ev_ts[cur_idx_cnt - 1];
+				cur_idx_old_ts = cur_pf_entry->pf_ev_ts[0];
+
+				if (cur_idx_cnt > new_idx_cnt)
+					continue;
+
+				/* Don't replace symbol from current event */
+				if (qdf_system_time_after_eq(cur_idx_last_ts,
+							     cur_time)) {
+					continue;
+				}
+
+				if (cur_idx_cnt == new_idx_cnt &&
+				    qdf_system_time_after_eq(cur_idx_old_ts,
+							     new_idx_old_ts)) {
+					continue;
+				}
+
+				new_pf_idx = idx2;
+				new_idx_cnt = cur_idx_cnt;
+				new_idx_last_ts = cur_idx_last_ts;
+				new_idx_old_ts = cur_idx_old_ts;
+			}
+
+add_sym:
+			/* Replace symbol with current event symbol */
+			new_pf_entry = &pf_sym_hist->wma_pf_sym[new_pf_idx];
+			new_pf_entry->pf_sym.symbol =
+					pf_sym_list->pf_sym[ev_lst_idx].symbol;
+			new_pf_entry->pf_sym.count = ev_sym_cnt;
+			for (idx2 = 0; idx2 < ev_sym_cnt; idx2++)
+				new_pf_entry->pf_ev_ts[idx2] = cur_time;
+
+			pf_sym_list->pending_pf_syms--;
+			pf_sym_list->pf_sym[ev_lst_idx].count = 0;
+			break;
+		}
+	}
+}
+
+static void
+wma_wow_pagefault_process_existing_syms(tp_wma_handle wma,
+					struct wma_pf_sym *pf_sym_entry,
+					struct wow_pf_wakeup_ev_data *ev_list,
+					qdf_time_t cur_time)
+{
+	uint8_t ev_idx, add_idx;
+	uint8_t pf_th, *tbl_sym_cnt, ev_sym_cnt;
+
+	pf_th = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc);
+	tbl_sym_cnt = &pf_sym_entry->pf_sym.count;
+
+	for (ev_idx = 0; ev_idx < ev_list->num_pf_syms; ev_idx++) {
+		/* Ignore if all symbols are processed */
+		if (!ev_list->pending_pf_syms)
+			break;
+
+		if (!ev_list->pf_sym[ev_idx].count)
+			continue;
+
+		/* Only process symbol equals current entry in the history */
+		if (ev_list->pf_sym[ev_idx].symbol !=
+		    pf_sym_entry->pf_sym.symbol) {
+			continue;
+		}
+
+		ev_sym_cnt = ev_list->pf_sym[ev_idx].count;
+
+		/* If symbol reaches threshold, then clear the ts data */
+		if (*tbl_sym_cnt + ev_sym_cnt >= pf_th) {
+			qdf_mem_zero(&pf_sym_entry->pf_ev_ts[0],
+				     (*tbl_sym_cnt * sizeof(qdf_time_t)));
+			*tbl_sym_cnt += ev_sym_cnt;
+			wma_wow_pagefault_add_sym_to_event(wma,
+							   &pf_sym_entry->pf_sym);
+			*tbl_sym_cnt = 0x0;
+
+			goto sym_handled;
+		}
+
+		for (add_idx = 0; add_idx < ev_sym_cnt; add_idx++)
+			pf_sym_entry->pf_ev_ts[(*tbl_sym_cnt)++] = cur_time;
+
+sym_handled:
+		ev_list->pending_pf_syms--;
+		ev_list->pf_sym[ev_idx].count = 0;
+		break;
+	}
+}
+
+static void
+wma_wow_pagefault_flush_ageout_entries(struct wma_pf_sym *pf_sym_entry,
+				       qdf_time_t cutoff_time)
+{
+	qdf_time_t entry_ts;
+	uint8_t *cur_pf_count, pf_ts_idx;
+
+	cur_pf_count = &pf_sym_entry->pf_sym.count;
+	/* Find the count of entries which elapsed cutoff time */
+	for (pf_ts_idx = 0; pf_ts_idx < *cur_pf_count; pf_ts_idx++) {
+		entry_ts = pf_sym_entry->pf_ev_ts[pf_ts_idx];
+		if (qdf_system_time_before(cutoff_time, entry_ts))
+			break;
+	}
+
+	/* Remove the entries which elapsed cutoff time */
+	if (pf_ts_idx > 0) {
+		*cur_pf_count -= pf_ts_idx;
+		qdf_mem_copy(&pf_sym_entry->pf_ev_ts[0],
+			     &pf_sym_entry->pf_ev_ts[pf_ts_idx],
+			     (*cur_pf_count * sizeof(qdf_time_t)));
+		qdf_mem_zero(&pf_sym_entry->pf_ev_ts[*cur_pf_count],
+			     pf_ts_idx * sizeof(qdf_time_t));
+	}
+}
+
+static QDF_STATUS
+wma_wow_pagefault_parse_event(struct wlan_objmgr_psoc *psoc,
+			      void *ev, uint32_t ev_len,
+			      struct wow_pf_wakeup_ev_data *pf_sym_list)
+{
+	WMI_WOW_WAKEUP_HOST_EVENTID_param_tlvs *event_param = ev;
+	uint8_t *cur_list_count, *pf_sym_addr, buf_idx, sym_idx, i;
+	uint32_t packet_len, symbol, pf_sym_count;
+	struct wow_pf_sym tmp_pf_sym;
+
+	if (event_param->num_wow_packet_buffer <= sizeof(packet_len)) {
+		wma_err("Invalid wow packet buffer from FW %u",
+			event_param->num_wow_packet_buffer);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	packet_len = *(uint32_t *)event_param->wow_packet_buffer;
+	if (!packet_len) {
+		wma_err("Wake event packet is empty");
+		return QDF_STATUS_E_INVAL;
 	}
 
-	if (WOW_REASON_PAGE_FAULT != reason)
+	if (packet_len >
+	    (event_param->num_wow_packet_buffer - sizeof(packet_len))) {
+		wma_err("Invalid packet_len from firmware, packet_len: %u, num_wow_packet_buffer: %u",
+			packet_len, event_param->num_wow_packet_buffer);
+		return QDF_STATUS_E_INVAL;
+	}
+
+	pf_sym_count = packet_len / sizeof(symbol);
+	/* First 4 bytes following packet len contains UUID */
+	pf_sym_count--;
+
+	pf_sym_list->pf_sym =
+		qdf_mem_malloc(pf_sym_count * sizeof(*pf_sym_list->pf_sym));
+	if (!pf_sym_list->pf_sym)
+		return QDF_STATUS_E_NOMEM;
+
+	/* First 4 bytes of buffer gives length and next 4 bytes for UUID */
+	pf_sym_addr = event_param->wow_packet_buffer + (sizeof(packet_len) * 2);
+
+	cur_list_count = &pf_sym_list->num_pf_syms;
+	for (buf_idx = 0; buf_idx < pf_sym_count; buf_idx++) {
+		symbol = *(uint32_t *)pf_sym_addr;
+
+		/* Ignore invalid symbols */
+		if (!symbol || symbol == (uint32_t)-1)
+			goto iter_next_sym;
+
+		for (sym_idx = 0; sym_idx < *cur_list_count; sym_idx++) {
+			if (pf_sym_list->pf_sym[sym_idx].symbol == symbol) {
+				pf_sym_list->pf_sym[sym_idx].count++;
+				goto iter_next_sym;
+			}
+		}
+
+		pf_sym_list->pf_sym[*cur_list_count].symbol = symbol;
+		pf_sym_list->pf_sym[*cur_list_count].count++;
+		(*cur_list_count)++;
+
+iter_next_sym:
+		pf_sym_addr += sizeof(symbol);
+	}
+
+	pf_sym_list->pending_pf_syms = *cur_list_count;
+
+	/* Reorder to prioritize syms with high frequency */
+	for (sym_idx = 0; sym_idx < *cur_list_count; sym_idx++) {
+		for (i = sym_idx + 1; i < *cur_list_count; i++) {
+			if (pf_sym_list->pf_sym[i].count <=
+			    pf_sym_list->pf_sym[sym_idx].count) {
+				continue;
+			}
+
+			tmp_pf_sym.symbol = pf_sym_list->pf_sym[sym_idx].symbol;
+			tmp_pf_sym.count = pf_sym_list->pf_sym[sym_idx].count;
+
+			pf_sym_list->pf_sym[sym_idx].symbol =
+						pf_sym_list->pf_sym[i].symbol;
+			pf_sym_list->pf_sym[sym_idx].count =
+						pf_sym_list->pf_sym[i].count;
+
+			pf_sym_list->pf_sym[i].symbol = tmp_pf_sym.symbol;
+			pf_sym_list->pf_sym[i].count = tmp_pf_sym.count;
+		}
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
+static void wma_wow_pagefault_handle_ssr_action(tp_wma_handle wma)
+{
+	QDF_STATUS status;
+	uint8_t *cur_count, pf_thresh;
+	qdf_time_t cur_time, cutoff_time;
+	uint32_t pf_wakeup_intv;
+	struct wma_pf_sym *pf_sym_entry;
+
+	cur_time = qdf_get_system_uptime();
+	pf_wakeup_intv =
+		wlan_pmo_get_interval_for_pagefault_wakeup_counts(wma->psoc);
+	pf_thresh = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc);
+	cutoff_time = cur_time - qdf_system_msecs_to_ticks(pf_wakeup_intv);
+
+	pf_sym_entry = &wma->wma_pf_hist.wma_pf_sym[0];
+	cur_count = &pf_sym_entry->pf_sym.count;
+	if (cutoff_time < cur_time) {
+		wma_wow_pagefault_flush_ageout_entries(pf_sym_entry,
+						       cutoff_time);
+	}
+
+	pf_sym_entry->pf_ev_ts[(*cur_count)++] = cur_time;
+	if (*cur_count < pf_thresh)
 		return;
 
-	if (!mac->sme.ssr_on_pagefault_cb) {
-		wma_debug("NULL SSR on pagefault cb");
+	/* If SSR threshold condition fails, SSR will not be triggered, so
+	 * save current event and flush oldest entry.
+	 */
+	status = wma_wow_pagefault_action_cb(NULL);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		(*cur_count)--;
+		qdf_mem_copy(&pf_sym_entry->pf_ev_ts[0],
+			     &pf_sym_entry->pf_ev_ts[1],
+			     (*cur_count * sizeof(qdf_time_t)));
+		qdf_mem_zero(&pf_sym_entry->pf_ev_ts[*cur_count],
+			     sizeof(qdf_time_t));
+	}
+}
+
+static void
+wma_wow_wakeup_pagefault_notify(tp_wma_handle wma, void *ev, uint32_t ev_len)
+{
+	QDF_STATUS status;
+	uint32_t pf_wakeup_intv;
+	qdf_time_t cur_time, cutoff_time;
+	struct wma_pf_sym *pf_sym_entry;
+	struct wma_pf_sym_hist *pf_sym_hist = &wma->wma_pf_hist;
+	struct wlan_objmgr_psoc *psoc = wma->psoc;
+	uint8_t pf_tbl_idx;
+	struct wow_pf_wakeup_ev_data pf_sym_list = {0};
+	struct mac_context *mac = cds_get_context(QDF_MODULE_ID_PE);
+
+	if (!mac) {
+		wma_debug("MAC context NULL");
 		return;
 	}
 
-	if (!wlan_pmo_enable_ssr_on_page_fault(wma->psoc))
+	if (wlan_pmo_no_op_on_page_fault(psoc))
 		return;
 
 	if (wmi_get_runtime_pm_inprogress(wma->wmi_handle)) {
@@ -3270,50 +3588,55 @@ wma_wow_wakeup_host_trigger_ssr(t_wma_handle *wma, uint32_t reason)
 		return;
 	}
 
-	pagefault_wakeups_for_ssr =
-			wlan_pmo_get_max_pagefault_wakeups_for_ssr(wma->psoc);
+	if (!mac->sme.pagefault_action_cb) {
+		wma_debug("NULL pagefault action cb");
+		return;
+	}
 
-	interval_for_pagefault_wakeup_counts =
-		wlan_pmo_get_interval_for_pagefault_wakeup_counts(wma->psoc);
+	if (wlan_pmo_enable_ssr_on_page_fault(psoc)) {
+		wma_wow_pagefault_handle_ssr_action(wma);
+		return;
+	}
 
-	curr_time = qdf_get_time_of_the_day_ms();
+	cur_time = qdf_get_system_uptime();
+	pf_wakeup_intv =
+		wlan_pmo_get_interval_for_pagefault_wakeup_counts(psoc);
+	cutoff_time = cur_time - qdf_system_msecs_to_ticks(pf_wakeup_intv);
 
-	for (i = wma->num_page_fault_wakeups - 1; i >= 0; i--) {
-		if (curr_time - wma->pagefault_wakeups_ts[i] >
-		    interval_for_pagefault_wakeup_counts) {
-			if (i == wma->num_page_fault_wakeups - 1) {
-				wma->num_page_fault_wakeups = 0;
-			} else {
-				qdf_mem_copy(&wma->pagefault_wakeups_ts[0],
-					&wma->pagefault_wakeups_ts[i+1],
-					(wma->num_page_fault_wakeups - (i+1)) *
-					sizeof(qdf_time_t));
-				wma->num_page_fault_wakeups -= (i + 1);
-			}
-			ignore_pf = false;
-			break;
-		}
+	status = wma_wow_pagefault_parse_event(psoc, ev, ev_len, &pf_sym_list);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		wma_debug("Failed during page fault payload parse");
+		return;
 	}
 
-	if (wma->num_page_fault_wakeups == pagefault_wakeups_for_ssr) {
-		qdf_mem_copy(&wma->pagefault_wakeups_ts[0],
-			     &wma->pagefault_wakeups_ts[1],
-			     (pagefault_wakeups_for_ssr - 1) *
-			     sizeof(qdf_time_t));
-		wma->num_page_fault_wakeups--;
+	qdf_spinlock_acquire(&pf_sym_hist->lock);
+	for (pf_tbl_idx = 0; pf_tbl_idx < WLAN_WMA_MAX_PF_SYM; pf_tbl_idx++) {
+		pf_sym_entry = &pf_sym_hist->wma_pf_sym[pf_tbl_idx];
+		if (cutoff_time < cur_time) {
+			wma_wow_pagefault_flush_ageout_entries(pf_sym_entry,
+							       cutoff_time);
+		}
+
+		wma_wow_pagefault_process_existing_syms(wma, pf_sym_entry,
+							&pf_sym_list, cur_time);
+
+		if (!pf_sym_list.pending_pf_syms)
+			goto send_event;
 	}
 
-	wma->pagefault_wakeups_ts[wma->num_page_fault_wakeups++] = curr_time;
+	/* Process if any new symbols are present in the event */
+	wma_wow_pagefault_add_new_sym_from_event(wma, &pf_sym_list, cur_time);
 
-	wma_nofl_debug("num pagefault wakeups %d", wma->num_page_fault_wakeups);
+send_event:
+	if (pf_sym_hist->pf_notify_buf_len) {
+		wma_wow_pagefault_action_cb(pf_sym_hist->pf_notify_buf_ptr);
+		pf_sym_hist->pf_notify_buf_len = 0;
+	}
 
-	if (!ignore_pf ||
-	    (wma->num_page_fault_wakeups < pagefault_wakeups_for_ssr))
-		return;
+	qdf_spinlock_release(&pf_sym_hist->lock);
 
-	if (curr_time - wma->pagefault_wakeups_ts[0] <=
-					interval_for_pagefault_wakeup_counts)
-		mac->sme.ssr_on_pagefault_cb();
+	qdf_mem_free(pf_sym_list.pf_sym);
+	pf_sym_list.pf_sym = NULL;
 }
 
 /**
@@ -3348,7 +3671,8 @@ int wma_wow_wakeup_host_event(void *handle, uint8_t *event, uint32_t len)
 	}
 
 	wma_wake_event_log_reason(wma, wake_info);
-	wma_wow_wakeup_host_trigger_ssr(wma, wake_info->wake_reason);
+	if (wake_info->wake_reason == WOW_REASON_PAGE_FAULT)
+		wma_wow_wakeup_pagefault_notify(wma, event, len);
 
 	if (wake_info->wake_reason == WOW_REASON_LOCAL_DATA_UC_DROP)
 		hif_rtpm_set_autosuspend_delay(WOW_LARGE_RX_RTPM_DELAY);

+ 78 - 9
core/wma/src/wma_main.c

@@ -1,6 +1,6 @@
 /*
  * Copyright (c) 2013-2021 The Linux Foundation. All rights reserved.
- * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2021-2024 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
@@ -3426,6 +3426,78 @@ wma_set_exclude_selftx_from_cca_busy_time(bool exclude_selftx_from_cca_busy,
 	cfg->exclude_selftx_from_cca_busy = exclude_selftx_from_cca_busy;
 }
 
+static void wma_deinit_pagefault_wakeup_history(tp_wma_handle wma)
+{
+	struct wma_pf_sym *pf_sym_entry;
+	int8_t idx, max_sym_count = WLAN_WMA_MAX_PF_SYM;
+	bool is_ssr = false;
+
+	if (wlan_pmo_enable_ssr_on_page_fault(wma->psoc)) {
+		is_ssr = true;
+		max_sym_count = 0x1;
+	}
+
+	for (idx = 0; idx < max_sym_count; idx++) {
+		pf_sym_entry = &wma->wma_pf_hist.wma_pf_sym[idx];
+		pf_sym_entry->pf_sym.symbol = 0x0;
+		pf_sym_entry->pf_sym.count = 0x0;
+		qdf_mem_free(pf_sym_entry->pf_ev_ts);
+		pf_sym_entry->pf_ev_ts = NULL;
+	}
+
+	if (!is_ssr) {
+		qdf_mem_free(wma->wma_pf_hist.pf_notify_buf_ptr);
+		wma->wma_pf_hist.pf_notify_buf_ptr = NULL;
+		wma->wma_pf_hist.pf_notify_buf_len = 0x0;
+	}
+	qdf_spinlock_destroy(&wma->wma_pf_hist.lock);
+}
+
+static QDF_STATUS wma_init_pagefault_wakeup_history(tp_wma_handle wma)
+{
+	struct wma_pf_sym *pf_sym_entry;
+	int8_t idx, idx2, max_sym_count = WLAN_WMA_MAX_PF_SYM;
+	uint8_t max_pf_count;
+	bool is_ssr = false;
+
+	if (wlan_pmo_enable_ssr_on_page_fault(wma->psoc)) {
+		is_ssr = true;
+		max_sym_count = 0x1;
+	}
+
+	max_pf_count = wlan_pmo_get_min_pagefault_wakeups_for_action(wma->psoc);
+	for (idx = 0; idx < max_sym_count; idx++) {
+		pf_sym_entry = &wma->wma_pf_hist.wma_pf_sym[idx];
+		pf_sym_entry->pf_sym.symbol = 0x0;
+		pf_sym_entry->pf_sym.count = 0x0;
+		pf_sym_entry->pf_ev_ts = qdf_mem_malloc(max_pf_count *
+							sizeof(qdf_time_t));
+		if (!pf_sym_entry->pf_ev_ts)
+			goto mem_err;
+	}
+
+	if (!is_ssr) {
+		wma->wma_pf_hist.pf_notify_buf_len = 0x0;
+		wma->wma_pf_hist.pf_notify_buf_ptr =
+				qdf_mem_malloc(WLAN_WMA_PF_APPS_NOTIFY_BUF_LEN);
+		if (!wma->wma_pf_hist.pf_notify_buf_ptr)
+			goto mem_err;
+	}
+
+	qdf_spinlock_create(&wma->wma_pf_hist.lock);
+
+	return QDF_STATUS_SUCCESS;
+
+mem_err:
+	for (idx2 = --idx; idx2 >= 0; idx2--) {
+		pf_sym_entry = &wma->wma_pf_hist.wma_pf_sym[idx2];
+		qdf_mem_free(pf_sym_entry->pf_ev_ts);
+		pf_sym_entry->pf_ev_ts = NULL;
+	}
+
+	return QDF_STATUS_E_NOMEM;
+}
+
 /**
  * wma_open() - Allocate wma context and initialize it.
  * @psoc: psoc object
@@ -3531,12 +3603,9 @@ QDF_STATUS wma_open(struct wlan_objmgr_psoc *psoc,
 	}
 	wma_handle->psoc = psoc;
 
-	if (wlan_pmo_enable_ssr_on_page_fault(psoc)) {
-		wma_handle->pagefault_wakeups_ts =
-			qdf_mem_malloc(
-			wlan_pmo_get_max_pagefault_wakeups_for_ssr(psoc) *
-			sizeof(qdf_time_t));
-		if (!wma_handle->pagefault_wakeups_ts)
+	if (!wlan_pmo_no_op_on_page_fault(psoc)) {
+		qdf_status = wma_init_pagefault_wakeup_history(wma_handle);
+		if (QDF_IS_STATUS_ERROR(qdf_status))
 			goto err_wma_handle;
 	}
 
@@ -4949,8 +5018,8 @@ QDF_STATUS wma_close(void)
 	if (wmi_validate_handle(wmi_handle))
 		return QDF_STATUS_E_INVAL;
 
-	if (wlan_pmo_enable_ssr_on_page_fault(wma_handle->psoc))
-		qdf_mem_free(wma_handle->pagefault_wakeups_ts);
+	if (!wlan_pmo_no_op_on_page_fault(wma_handle->psoc))
+		wma_deinit_pagefault_wakeup_history(wma_handle);
 
 	qdf_atomic_set(&wma_handle->sap_num_clients_connected, 0);
 	qdf_atomic_set(&wma_handle->go_num_clients_connected, 0);

+ 23 - 4
core/wma/src/wma_mgmt.c

@@ -90,6 +90,7 @@
 #include "wlan_cm_api.h"
 #include "wlan_mlo_link_force.h"
 #include <target_if_spatial_reuse.h>
+#include "wlan_nan_api_i.h"
 
 /* Max debug string size for WMM in bytes */
 #define WMA_WMM_DEBUG_STRING_SIZE    512
@@ -3340,7 +3341,8 @@ int wma_process_rmf_frame(tp_wma_handle wma_handle,
 			return -EINVAL;
 		}
 
-		if (iface->type == WMI_VDEV_TYPE_NDI) {
+		if (iface->type == WMI_VDEV_TYPE_NDI ||
+		    iface->type == WMI_VDEV_TYPE_NAN) {
 			hdr_len = IEEE80211_CCMP_HEADERLEN;
 			mic_len = IEEE80211_CCMP_MICLEN;
 		} else {
@@ -3491,14 +3493,16 @@ wma_check_and_process_rmf_frame(tp_wma_handle wma_handle,
 	struct ieee80211_frame *hdr = *wh;
 
 	iface = &(wma_handle->interfaces[vdev_id]);
-	if (iface->type != WMI_VDEV_TYPE_NDI && !iface->rmfEnabled)
+	if ((iface->type != WMI_VDEV_TYPE_NDI &&
+	     iface->type != WMI_VDEV_TYPE_NAN) && !iface->rmfEnabled)
 		return 0;
 
 	if (qdf_is_macaddr_group((struct qdf_mac_addr *)(hdr->i_addr1)) ||
 	    qdf_is_macaddr_broadcast((struct qdf_mac_addr *)(hdr->i_addr1)) ||
 	    wma_get_peer_pmf_status(wma_handle, hdr->i_addr2) ||
-	    (iface->type == WMI_VDEV_TYPE_NDI &&
-	    (hdr->i_fc[1] & IEEE80211_FC1_WEP))) {
+	    ((iface->type == WMI_VDEV_TYPE_NDI ||
+	      iface->type == WMI_VDEV_TYPE_NAN) &&
+	     (hdr->i_fc[1] & IEEE80211_FC1_WEP))) {
 		status = wma_process_rmf_frame(wma_handle, iface, hdr,
 					       rx_pkt, buf);
 		if (status)
@@ -3702,6 +3706,21 @@ int wma_form_rx_packet(qdf_nbuf_t buf,
 								 buf);
 			if (status)
 				return status;
+		} else if (mgt_subtype == MGMT_SUBTYPE_ACTION) {
+			/* NAN Action frame */
+			vdev_id = wlan_nan_get_vdev_id_from_bssid(
+							wma_handle->pdev,
+							wh->i_addr3,
+							WLAN_ACTION_OUI_ID);
+
+			if (vdev_id != WMA_INVALID_VDEV_ID) {
+				status = wma_check_and_process_rmf_frame(
+								wma_handle,
+								vdev_id, &wh,
+								rx_pkt, buf);
+				if (status)
+					return status;
+			}
 		}
 	}