Ver código fonte

qcacld-3.0: Support for pre CAC on SAP for user requested channel

Add support for pre CAC on SAP for the user requested channel.
The reason for the requirement is to have SAP operations without
interruptions due to CAC. After starting SAP on the 2.4GHz
channel, the driver will start a pre CAC on the 5GHz DFS channel
requested. If no radar is detected, SAP is expected to switch
from the 2.4GHz channel to the DFS channel mentioned in the pre
CAC request. If radar is detected SAP will continue to operate on
the 2.4GHz channel. To provide the pre CAC channel to the driver,
vendor commands/attributes are used .

Change-Id: I4cf4bb5a861eea05f37fafb3bf35d327f5377aab
CRs-Fixed: 1045242
Manishekar Chandrasekaran 8 anos atrás
pai
commit
9e8c7be23d

+ 2 - 3
core/cds/inc/cds_concurrency.h

@@ -654,6 +654,8 @@ QDF_STATUS cds_deinit_policy_mgr(void);
 QDF_STATUS cds_get_pcl(enum cds_con_mode mode,
 			uint8_t *pcl_channels, uint32_t *len,
 			uint8_t *pcl_weight, uint32_t weight_len);
+void cds_update_with_safe_channel_list(uint8_t *pcl_channels, uint32_t *len,
+		uint8_t *weight_list, uint32_t weight_len);
 uint8_t cds_get_nondfs_preferred_channel(enum cds_con_mode mode,
 					bool for_existing_conn);
 bool cds_is_any_nondfs_chnl_present(uint8_t *channel);
@@ -754,9 +756,6 @@ QDF_STATUS qdf_reset_connection_update(void);
 QDF_STATUS qdf_set_connection_update(void);
 QDF_STATUS qdf_init_connection_update(void);
 QDF_STATUS cds_stop_start_opportunistic_timer(void);
-QDF_STATUS cds_handle_hw_mode_change_on_csa(uint16_t session_id,
-		uint8_t channel, uint8_t *bssid, void *dst, void *src,
-		uint32_t numbytes);
 QDF_STATUS cds_modify_sap_pcl_based_on_mandatory_channel(uint8_t *pcl_list_org,
 		uint8_t *weight_list_org,
 		uint32_t *pcl_len_org);

+ 19 - 2
core/hdd/inc/wlan_hdd_main.h

@@ -259,6 +259,7 @@
 
 #define BSS_WAIT_TIMEOUT 10000
 
+#define PRE_CAC_SSID "pre_cac_ssid"
 /*
  * Generic asynchronous request/response support
  *
@@ -1057,6 +1058,11 @@ struct hdd_adapter_s {
 	ol_txrx_tx_fp tx_fn;
 	/* debugfs entry */
 	struct dentry *debugfs_phy;
+	/*
+	 * The pre cac channel is saved here and will be used when the SAP's
+	 * channel needs to be moved from the existing 2.4GHz channel.
+	 */
+	uint8_t pre_cac_chan;
 };
 
 #define WLAN_HDD_GET_STATION_CTX_PTR(pAdapter) (&(pAdapter)->sessionCtx.station)
@@ -1445,6 +1451,7 @@ struct hdd_context_s {
 
 	/* the radio index assigned by cnss_logger */
 	int radio_index;
+	qdf_work_t sap_pre_cac_work;
 	bool hbw_requested;
 #ifdef WLAN_FEATURE_NAN_DATAPATH
 	bool nan_datapath_enabled;
@@ -1540,7 +1547,6 @@ struct qdf_mac_addr *
 hdd_wlan_get_ibss_mac_addr_from_staid(hdd_adapter_t *pAdapter,
 				      uint8_t staIdx);
 void hdd_checkandupdate_phymode(hdd_context_t *pHddCtx);
-
 #ifdef MSM_PLATFORM
 void hdd_start_bus_bw_compute_timer(hdd_adapter_t *pAdapter);
 void hdd_stop_bus_bw_compute_timer(hdd_adapter_t *pAdapter);
@@ -1636,6 +1642,9 @@ static inline int wlan_hdd_get_cpu(void)
 }
 #endif
 
+void wlan_hdd_sap_pre_cac_failure(void *data);
+void hdd_clean_up_pre_cac_interface(hdd_context_t *hdd_ctx);
+
 void wlan_hdd_txrx_pause_cb(uint8_t vdev_id,
 	enum netif_action_type action, enum netif_reason_type reason);
 
@@ -1658,7 +1667,15 @@ int hdd_update_config(hdd_context_t *hdd_ctx);
 QDF_STATUS hdd_chan_change_notify(hdd_adapter_t *adapter,
 		struct net_device *dev,
 		struct hdd_chan_change_params chan_change);
-
+int wlan_hdd_set_channel(struct wiphy *wiphy,
+		struct net_device *dev,
+		struct cfg80211_chan_def *chandef,
+		enum nl80211_channel_type channel_type);
+int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
+		struct cfg80211_beacon_data *params,
+		const u8 *ssid, size_t ssid_len,
+		enum nl80211_hidden_ssid hidden_ssid,
+		bool check_for_concurrency);
 #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH
 QDF_STATUS hdd_register_for_sap_restart_with_channel_switch(void);
 #else

+ 554 - 1
core/hdd/src/wlan_hdd_cfg80211.c

@@ -69,6 +69,7 @@
 #include "wlan_hdd_tdls.h"
 #include "wlan_hdd_wmm.h"
 #include "wma_types.h"
+#include "wma.h"
 #include "wlan_hdd_misc.h"
 #include "wlan_hdd_nan.h"
 #include <wlan_hdd_ipa.h>
@@ -1078,6 +1079,10 @@ static const struct nl80211_vendor_cmd_info wlan_hdd_cfg80211_vendor_events[] =
 		.vendor_id = QCA_NL80211_VENDOR_ID,
 		.subcmd = QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP
 	},
+	[QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX] = {
+		.vendor_id = QCA_NL80211_VENDOR_ID,
+		.subcmd = QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH
+	},
 };
 
 /**
@@ -5520,6 +5525,108 @@ static int wlan_hdd_cfg80211_txpower_scale_decr_db(struct wiphy *wiphy,
 	return ret;
 }
 
+/**
+ * __wlan_hdd_cfg80211_conditional_chan_switch() - Conditional channel switch
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Data length
+ *
+ * Processes the conditional channel switch request and invokes the helper
+ * APIs to process the channel switch request.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int __wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy,
+						struct wireless_dev *wdev,
+						const void *data,
+						int data_len)
+{
+	int ret;
+	hdd_context_t *hdd_ctx = wiphy_priv(wiphy);
+	struct net_device *dev = wdev->netdev;
+	hdd_adapter_t *adapter;
+	struct nlattr
+		*tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX + 1];
+	uint32_t freq_len, i;
+	uint32_t *freq;
+	uint8_t chans[QDF_MAX_NUM_CHAN];
+
+	ENTER_DEV(dev);
+
+	ret = wlan_hdd_validate_context(hdd_ctx);
+	if (ret)
+		return ret;
+
+	if (!hdd_ctx->config->enableDFSMasterCap) {
+		hdd_err("DFS master capability is not present in the driver");
+		return -EINVAL;
+	}
+
+	if (QDF_GLOBAL_FTM_MODE == hdd_get_conparam()) {
+		hdd_err("Command not allowed in FTM mode");
+		return -EPERM;
+	}
+
+	adapter = WLAN_HDD_GET_PRIV_PTR(dev);
+	if (adapter->device_mode != QDF_SAP_MODE) {
+		hdd_err("Invalid device mode %d", adapter->device_mode);
+		return -EINVAL;
+	}
+
+	if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX,
+		      data, data_len, NULL)) {
+		hdd_err("Invalid ATTR");
+		return -EINVAL;
+	}
+
+	if (!tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]) {
+		hdd_err("Frequency list is missing");
+		return -EINVAL;
+	}
+
+	freq_len = nla_len(
+		tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST])/
+		sizeof(uint32_t);
+
+	if (freq_len > QDF_MAX_NUM_CHAN) {
+		hdd_err("insufficient space to hold channels");
+		return -ENOMEM;
+	}
+
+	hdd_debug("freq_len=%d", freq_len);
+
+	freq = nla_data(
+		tb[QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST]);
+
+
+	for (i = 0; i < freq_len; i++) {
+		if (freq[i] == 0)
+			chans[i] = 0;
+		else
+			chans[i] = ieee80211_frequency_to_channel(freq[i]);
+
+		hdd_debug("freq[%d]=%d", i, freq[i]);
+	}
+
+	/*
+	 * The input frequency list from user space is designed to be a
+	 * priority based frequency list. This is only to accommodate any
+	 * future request. But, current requirement is only to perform CAC
+	 * on a single channel. So, the first entry from the list is picked.
+	 *
+	 * If channel is zero, any channel in the available outdoor regulatory
+	 * domain will be selected.
+	 */
+	ret = wlan_hdd_request_pre_cac(chans[0]);
+	if (ret) {
+		hdd_err("pre cac request failed with reason:%d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
 /**
  * __wlan_hdd_cfg80211_p2p_lo_start () - start P2P Listen Offload
  * @wiphy: Pointer to wireless phy
@@ -5760,6 +5867,33 @@ static int wlan_hdd_cfg80211_p2p_lo_stop(struct wiphy *wiphy,
 	return ret;
 }
 
+/**
+ * wlan_hdd_cfg80211_conditional_chan_switch() - SAP conditional channel switch
+ * @wiphy: Pointer to wireless phy
+ * @wdev: Pointer to wireless device
+ * @data: Pointer to data
+ * @data_len: Data length
+ *
+ * Inovkes internal API __wlan_hdd_cfg80211_conditional_chan_switch()
+ * to process the conditional channel switch request.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+static int wlan_hdd_cfg80211_conditional_chan_switch(struct wiphy *wiphy,
+						struct wireless_dev *wdev,
+						const void *data,
+						int data_len)
+{
+	int ret;
+
+	cds_ssr_protect(__func__);
+	ret = __wlan_hdd_cfg80211_conditional_chan_switch(wiphy, wdev,
+						data, data_len);
+	cds_ssr_unprotect(__func__);
+
+	return ret;
+}
+
 /*
  * define short names for the global vendor params
  * used by __wlan_hdd_cfg80211_bpf_offload()
@@ -6098,6 +6232,417 @@ static int wlan_hdd_cfg80211_bpf_offload(struct wiphy *wiphy,
 	return ret;
 }
 
+#ifdef WLAN_FEATURE_MBSSID
+/**
+ * wlan_hdd_set_pre_cac_status() - Set the pre cac status
+ * @pre_cac_adapter: AP adapter used for pre cac
+ * @status: Status (true or false)
+ * @handle: Global handle
+ *
+ * Sets the status of pre cac i.e., whether the pre cac is active or not
+ *
+ * Return: Zero on success, non-zero on failure
+ */
+static int wlan_hdd_set_pre_cac_status(hdd_adapter_t *pre_cac_adapter,
+				bool status, tHalHandle handle)
+{
+	QDF_STATUS ret;
+
+	ret = wlan_sap_set_pre_cac_status(
+		WLAN_HDD_GET_SAP_CTX_PTR(pre_cac_adapter), status, handle);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * wlan_hdd_set_chan_before_pre_cac() - Save the channel before pre cac
+ * @ap_adapter: AP adapter
+ * @chan_before_pre_cac: Channel
+ *
+ * Saves the channel which the AP was beaconing on before moving to the pre
+ * cac channel. If radar is detected on the pre cac channel, this saved
+ * channel will be used for AP operations.
+ *
+ * Return: Zero on success, non-zero on failure
+ */
+static int wlan_hdd_set_chan_before_pre_cac(hdd_adapter_t *ap_adapter,
+			uint8_t chan_before_pre_cac)
+{
+	QDF_STATUS ret;
+
+	ret = wlan_sap_set_chan_before_pre_cac(
+		WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), chan_before_pre_cac);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * wlan_hdd_sap_get_nol() - Get SAPs NOL
+ * @ap_adapter: AP adapter
+ * @nol: Non-occupancy list
+ * @nol_len: Length of NOL
+ *
+ * Get the NOL for SAP
+ *
+ * Return: Zero on success, non-zero on failure
+ */
+static int wlan_hdd_sap_get_nol(hdd_adapter_t *ap_adapter, uint8_t *nol,
+				uint32_t *nol_len)
+{
+	QDF_STATUS ret;
+
+	ret = wlansap_get_dfs_nol(WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter),
+				nol, nol_len);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+#else
+static int wlan_hdd_set_pre_cac_status(hdd_adapter_t *pre_cac_adapter,
+				bool status, tHalHandle handle)
+{
+	QDF_STATUS ret;
+
+	ret = wlan_sap_set_pre_cac_status(
+		(WLAN_HDD_GET_CTX(pre_cac_adapter))->pcds_context, status,
+		handle);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int wlan_hdd_set_chan_before_pre_cac(hdd_adapter_t *ap_adapter,
+			uint8_t chan_before_pre_cac)
+{
+	QDF_STATUS ret;
+
+	ret = wlan_sap_set_chan_before_pre_cac(
+		(WLAN_HDD_GET_CTX(ap_adapter))->pcds_context,
+		chan_before_pre_cac);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int wlan_hdd_sap_get_nol(hdd_adapter_t *ap_adapter, uint8_t *nol,
+				uint32_t *nol_len)
+{
+	QDF_STATUS ret;
+
+	ret = wlansap_get_dfs_nol(
+		(WLAN_HDD_GET_CTX(ap_adapter))->pcds_context,
+		nol, nol_len);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+#endif
+
+/**
+ * wlan_hdd_validate_and_get_pre_cac_ch() - Validate and get pre cac channel
+ * @hdd_ctx: HDD context
+ * @ap_adapter: AP adapter
+ * @channel: Channel requested by userspace
+ * @pre_cac_chan: Pointer to the pre CAC channel
+ *
+ * Validates the channel provided by userspace. If user provided channel 0,
+ * a valid outdoor channel must be selected from the regulatory channel.
+ *
+ * Return: Zero on success and non zero value on error
+ */
+int wlan_hdd_validate_and_get_pre_cac_ch(hdd_context_t *hdd_ctx,
+					hdd_adapter_t *ap_adapter,
+					uint8_t channel,
+					uint8_t *pre_cac_chan)
+{
+	uint32_t i, j;
+	QDF_STATUS status;
+	int ret;
+	uint8_t nol[QDF_MAX_NUM_CHAN];
+	uint32_t nol_len = 0, weight_len = 0;
+	bool found;
+	uint32_t len = WNI_CFG_VALID_CHANNEL_LIST_LEN;
+	uint8_t channel_list[QDF_MAX_NUM_CHAN] = {0};
+	uint8_t pcl_weights[QDF_MAX_NUM_CHAN] = {0};
+
+	if (0 == channel) {
+		/* Channel is not obtained from PCL because PCL may not have
+		 * the entire channel list. For example: if SAP is up on
+		 * channel 6 and PCL is queried for the next SAP interface,
+		 * if SCC is preferred, the PCL will contain only the channel
+		 * 6. But, we are in need of a DFS channel. So, going with the
+		 * first channel from the valid channel list.
+		 */
+		status = cds_get_valid_chans(channel_list, &len);
+		if (QDF_IS_STATUS_ERROR(status)) {
+			hdd_err("Failed to get channel list");
+			return -EINVAL;
+		}
+		cds_update_with_safe_channel_list(channel_list, &len,
+				pcl_weights, weight_len);
+		ret = wlan_hdd_sap_get_nol(ap_adapter, nol, &nol_len);
+		for (i = 0; i < len; i++) {
+			found = false;
+			for (j = 0; j < nol_len; j++) {
+				if (channel_list[i] == nol[j]) {
+					found = true;
+					break;
+				}
+			}
+			if (found)
+				continue;
+			if (CDS_IS_DFS_CH(channel_list[i])) {
+				*pre_cac_chan = channel_list[i];
+				break;
+			}
+		}
+		if (*pre_cac_chan == 0) {
+			hdd_err("unable to find outdoor channel");
+			return -EINVAL;
+		}
+	} else {
+		/* Only when driver selects a channel, check is done for
+		 * unnsafe and NOL channels. When user provides a fixed channel
+		 * the user is expected to take care of this.
+		 */
+		if (!sme_is_channel_valid(hdd_ctx->hHal, channel) ||
+			!CDS_IS_DFS_CH(channel)) {
+			hdd_err("Invalid channel for pre cac:%d", channel);
+			return -EINVAL;
+		} else {
+			*pre_cac_chan = channel;
+		}
+	}
+	hdd_info("selected pre cac channel:%d", *pre_cac_chan);
+	return 0;
+}
+
+/**
+ * wlan_hdd_request_pre_cac() - Start pre CAC in the driver
+ * @channel: Channel option provided by userspace
+ *
+ * Sets the driver to the required hardware mode and start an adapater for
+ * pre CAC which will mimic an AP.
+ *
+ * Return: Zero on success, non-zero value on error
+ */
+int wlan_hdd_request_pre_cac(uint8_t channel)
+{
+	uint8_t pre_cac_chan = 0;
+	hdd_context_t *hdd_ctx;
+	int ret;
+	hdd_adapter_t *ap_adapter, *pre_cac_adapter;
+	hdd_ap_ctx_t *hdd_ap_ctx;
+	QDF_STATUS status;
+	struct wiphy *wiphy;
+	struct net_device *dev;
+	struct cfg80211_chan_def chandef;
+	enum nl80211_channel_type channel_type;
+	uint32_t freq;
+	struct ieee80211_channel *chan;
+	tHalHandle handle;
+	bool val;
+
+	hdd_ctx = cds_get_context(QDF_MODULE_ID_HDD);
+	if (0 != wlan_hdd_validate_context(hdd_ctx))
+		return -EINVAL;
+
+	if (cds_get_connection_count() > 1) {
+		hdd_err("pre cac not allowed in concurrency");
+		return -EINVAL;
+	}
+
+	ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE);
+	if (!ap_adapter) {
+		hdd_err("unable to get SAP adapter");
+		return -EINVAL;
+	}
+
+	handle = WLAN_HDD_GET_HAL_CTX(ap_adapter);
+	if (!handle) {
+		hdd_err("Invalid handle");
+		return -EINVAL;
+	}
+
+	val = wlan_sap_is_pre_cac_active(handle);
+	if (val) {
+		hdd_err("pre cac is already in progress");
+		return -EINVAL;
+	}
+
+	hdd_ap_ctx = WLAN_HDD_GET_AP_CTX_PTR(ap_adapter);
+	if (!hdd_ap_ctx) {
+		hdd_err("SAP context is NULL");
+		return -EINVAL;
+	}
+
+	if (CDS_IS_DFS_CH(hdd_ap_ctx->operatingChannel)) {
+		hdd_err("SAP is already on DFS channel:%d",
+			hdd_ap_ctx->operatingChannel);
+		return -EINVAL;
+	}
+
+	if (!CDS_IS_CHANNEL_24GHZ(hdd_ap_ctx->operatingChannel)) {
+		hdd_err("pre CAC alllowed only when SAP is in 2.4GHz:%d",
+			hdd_ap_ctx->operatingChannel);
+		return -EINVAL;
+	}
+
+	hdd_info("channel:%d", channel);
+
+	ret = wlan_hdd_validate_and_get_pre_cac_ch(hdd_ctx, ap_adapter, channel,
+							&pre_cac_chan);
+	if (ret != 0)
+		return ret;
+
+	/* Since current SAP is in 2.4GHz and pre CAC channel is in 5GHz, this
+	 * connection update should result in DBS mode
+	 */
+	status = cds_update_and_wait_for_connection_update(
+						ap_adapter->sessionId,
+						pre_cac_chan,
+						SIR_UPDATE_REASON_PRE_CAC);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("error in moving to DBS mode");
+		return -EINVAL;
+	}
+
+	hdd_debug("starting pre cac SAP  adapter");
+
+	/* Starting a SAP adapter:
+	 * Instead of opening an adapter, we could just do a SME open session
+	 * for AP type. But, start BSS would still need an adapter.
+	 * So, this option is not taken.
+	 *
+	 * hdd open adapter is going to register this precac interface with
+	 * user space. This interface though exposed to user space will be in
+	 * DOWN state. Consideration was done to avoid this registration to the
+	 * user space. But, as part of SAP operations multiple events are sent
+	 * to user space. Some of these events received from unregistered
+	 * interface was causing crashes. So, retaining the registration.
+	 *
+	 * So, this interface would remain registered and will remain in DOWN
+	 * state for the CAC duration. We will add notes in the feature
+	 * announcement to not use this temporary interface for any activity
+	 * from user space.
+	 */
+	pre_cac_adapter = hdd_open_adapter(hdd_ctx, QDF_SAP_MODE, "precac%d",
+			wlan_hdd_get_intf_addr(hdd_ctx),
+			NET_NAME_UNKNOWN, true);
+	if (!pre_cac_adapter) {
+		hdd_err("error starting pre cac adapter");
+		return -EINVAL;
+	}
+
+	hdd_debug("preparing for start ap/bss on the pre cac adapter");
+
+	wiphy = hdd_ctx->wiphy;
+	dev = pre_cac_adapter->dev;
+
+	/* Since this is only a dummy interface lets us use the IEs from the
+	 * other active SAP interface. In regular scenarios, these IEs would
+	 * come from the user space entity
+	 */
+	pre_cac_adapter->sessionCtx.ap.beacon = qdf_mem_malloc(
+			sizeof(*ap_adapter->sessionCtx.ap.beacon));
+	if (!pre_cac_adapter->sessionCtx.ap.beacon) {
+		hdd_err("failed to alloc mem for beacon");
+		goto close_pre_cac_adapter;
+	}
+	qdf_mem_copy(pre_cac_adapter->sessionCtx.ap.beacon,
+			ap_adapter->sessionCtx.ap.beacon,
+			sizeof(*pre_cac_adapter->sessionCtx.ap.beacon));
+	pre_cac_adapter->sessionCtx.ap.sapConfig.ch_width_orig =
+			ap_adapter->sessionCtx.ap.sapConfig.ch_width_orig;
+	pre_cac_adapter->sessionCtx.ap.sapConfig.authType =
+			ap_adapter->sessionCtx.ap.sapConfig.authType;
+
+	/* Premise is that on moving from 2.4GHz to 5GHz, the SAP will continue
+	 * to operate on the same bandwidth as that of the 2.4GHz operations.
+	 * Only bandwidths 20MHz/40MHz are possible on 2.4GHz band.
+	 */
+	switch (ap_adapter->sessionCtx.ap.sapConfig.ch_width_orig) {
+	case CH_WIDTH_20MHZ:
+		channel_type = NL80211_CHAN_HT20;
+		break;
+	case CH_WIDTH_40MHZ:
+		if (ap_adapter->sessionCtx.ap.sapConfig.sec_ch >
+				ap_adapter->sessionCtx.ap.sapConfig.channel)
+			channel_type = NL80211_CHAN_HT40PLUS;
+		else
+			channel_type = NL80211_CHAN_HT40MINUS;
+		break;
+	default:
+		channel_type = NL80211_CHAN_NO_HT;
+		break;
+	}
+
+	freq = cds_chan_to_freq(pre_cac_chan);
+	chan = __ieee80211_get_channel(wiphy, freq);
+	if (!chan) {
+		hdd_err("channel converion failed");
+		goto close_pre_cac_adapter;
+	}
+
+	cfg80211_chandef_create(&chandef, chan, channel_type);
+
+	hdd_debug("orig width:%d channel_type:%d freq:%d",
+		ap_adapter->sessionCtx.ap.sapConfig.ch_width_orig,
+		channel_type, freq);
+
+	ret = wlan_hdd_set_channel(wiphy, dev, &chandef, channel_type);
+	if (0 != ret) {
+		hdd_err("failed to set channel");
+		goto close_pre_cac_adapter;
+	}
+
+	status = wlan_hdd_cfg80211_start_bss(pre_cac_adapter, NULL,
+			PRE_CAC_SSID, qdf_str_len(PRE_CAC_SSID),
+			eHIDDEN_SSID_NOT_IN_USE, false);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("start bss failed");
+		goto close_pre_cac_adapter;
+	}
+
+	/*
+	 * The pre cac status is set here. But, it would not be reset explicitly
+	 * anywhere, since after the pre cac success/failure, the pre cac
+	 * adapter itself would be removed.
+	 */
+	ret = wlan_hdd_set_pre_cac_status(pre_cac_adapter, true, handle);
+	if (0 != ret) {
+		hdd_err("failed to set pre cac status");
+		goto stop_pre_cac_adapter;
+	}
+
+	ret = wlan_hdd_set_chan_before_pre_cac(ap_adapter,
+				hdd_ap_ctx->operatingChannel);
+	if (0 != ret) {
+		hdd_err("failed to set channel before pre cac");
+		goto stop_pre_cac_adapter;
+	}
+
+	ap_adapter->pre_cac_chan = pre_cac_chan;
+
+	return 0;
+
+stop_pre_cac_adapter:
+	hdd_stop_adapter(hdd_ctx, pre_cac_adapter, false);
+close_pre_cac_adapter:
+	qdf_mem_free(pre_cac_adapter->sessionCtx.ap.beacon);
+	pre_cac_adapter->sessionCtx.ap.beacon = NULL;
+	hdd_close_adapter(hdd_ctx, pre_cac_adapter, false);
+	return -EINVAL;
+}
+
 /**
  * hdd_init_bpf_completion() - Initialize the completion event for bpf
  *
@@ -6773,7 +7318,15 @@ const struct wiphy_vendor_command hdd_wiphy_vendor_commands[] = {
 				WIPHY_VENDOR_CMD_NEED_RUNNING,
 		.doit = wlan_hdd_cfg80211_p2p_lo_stop
 	},
-
+	{
+		.info.vendor_id = QCA_NL80211_VENDOR_ID,
+		.info.subcmd =
+			QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH,
+		.flags = WIPHY_VENDOR_CMD_NEED_WDEV |
+				WIPHY_VENDOR_CMD_NEED_NETDEV |
+				WIPHY_VENDOR_CMD_NEED_RUNNING,
+		.doit = wlan_hdd_cfg80211_conditional_chan_switch
+	},
 #ifdef WLAN_FEATURE_NAN_DATAPATH
 	{
 		.info.vendor_id = QCA_NL80211_VENDOR_ID,

+ 40 - 0
core/hdd/src/wlan_hdd_cfg80211.h

@@ -270,6 +270,18 @@ typedef enum {
  *	device. As an event, it indicates either the feature stopped after it
  *	was already running or feature has actually failed to start.
  * @QCA_NL80211_VENDOR_SUBCMD_GET_STATION: send BSS Information
+ * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH: After SAP starts
+ *     beaconing, this sub command provides the driver, the frequencies on the
+ *     5 GHz to check for any radar activity. Driver selects one channel from
+ *     this priority list provided through
+ *     @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST and starts
+ *     to check for radar activity on it. If no radar activity is detected
+ *     during the channel availability check period, driver internally switches
+ *     to the selected frequency of operation. If the frequency is zero, driver
+ *     internally selects a channel. The status of this conditional switch is
+ *     indicated through an event using the same sub command through
+ *     @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS. Attributes are
+ *     listed in qca_wlan_vendor_attr_sap_conditional_chan_switch
  */
 
 enum qca_nl80211_vendor_subcmds {
@@ -396,6 +408,7 @@ enum qca_nl80211_vendor_subcmds {
 	QCA_NL80211_VENDOR_SUBCMD_GET_STATION = 121,
 	QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_START = 122,
 	QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP = 123,
+	QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH = 124,
 };
 
 /**
@@ -606,6 +619,8 @@ enum qca_wlan_vendor_attr_get_station_info {
  * @QCA_NL80211_VENDOR_SUBCMD_TSF_INDEX: TSF response events index
  * @QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX:
  *      P2P listen offload index
+ * @QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX: SAP
+ *      conditional channel switch index
  */
 
 enum qca_nl80211_vendor_subcmds_index {
@@ -682,6 +697,7 @@ enum qca_nl80211_vendor_subcmds_index {
 	QCA_NL80211_VENDOR_SUBCMD_NDP_INDEX,
 #endif /* WLAN_FEATURE_NAN_DATAPATH */
 	QCA_NL80211_VENDOR_SUBCMD_P2P_LO_EVENT_INDEX,
+	QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX,
 };
 
 /**
@@ -2079,6 +2095,29 @@ enum qca_wlan_vendor_features {
 	NUM_QCA_WLAN_VENDOR_FEATURES
 };
 
+/**
+ * enum qca_wlan_vendor_attr_sap_conditional_chan_switch - Parameters for SAP
+ *     conditional channel switch
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID: Invalid initial
+ *     value
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST: Priority based
+ * frequency list (an array of u32 values in host byte order)
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS: Status of the
+ *     conditional switch (u32)- 0: Success, Non-zero: Failure
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST: After last
+ * @QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX: Subcommand max
+ */
+enum qca_wlan_vendor_attr_sap_conditional_chan_switch {
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_INVALID = 0,
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_FREQ_LIST = 1,
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS = 2,
+
+	/* Keep Last */
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST,
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_MAX =
+	QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_AFTER_LAST - 1,
+};
+
 /* Feature defines */
 #define WIFI_FEATURE_INFRA              0x0001  /* Basic infrastructure mode */
 #define WIFI_FEATURE_INFRA_5G           0x0002  /* Support for 5 GHz Band */
@@ -2758,6 +2797,7 @@ nla_fail:
 }
 #define cfg80211_vendor_event_alloc backported_cfg80211_vendor_event_alloc
 #endif
+int wlan_hdd_request_pre_cac(uint8_t channel);
 int wlan_hdd_sap_cfg_dfs_override(hdd_adapter_t *adapter);
 
 enum cds_con_mode wlan_hdd_convert_nl_iftype_to_hdd_type(

+ 227 - 19
core/hdd/src/wlan_hdd_hostapd.c

@@ -786,6 +786,179 @@ QDF_STATUS hdd_send_radar_event(hdd_context_t *hdd_context,
 	return QDF_STATUS_SUCCESS;
 }
 
+/**
+ * hdd_send_conditional_chan_switch_status() - Send conditional channel switch
+ * status
+ * @hdd_ctx: HDD context
+ * @wdev: Wireless device structure
+ * @status: Status of conditional channel switch
+ * (0: Success, Non-zero: Failure)
+ *
+ * Sends the status of conditional channel switch to user space. This is named
+ * conditional channel switch because the SAP will move to the provided channel
+ * after some condition (pre-cac) is met.
+ *
+ * Return: None
+ */
+static void hdd_send_conditional_chan_switch_status(hdd_context_t *hdd_ctx,
+						struct wireless_dev *wdev,
+						bool status)
+{
+	struct sk_buff *event;
+
+	ENTER_DEV(wdev->netdev);
+
+	if (!hdd_ctx) {
+		hdd_err("Invalid HDD context pointer");
+		return;
+	}
+
+	event = cfg80211_vendor_event_alloc(hdd_ctx->wiphy,
+		  wdev, sizeof(uint32_t) + NLMSG_HDRLEN,
+		  QCA_NL80211_VENDOR_SUBCMD_SAP_CONDITIONAL_CHAN_SWITCH_INDEX,
+		  GFP_KERNEL);
+	if (!event) {
+		hdd_err("cfg80211_vendor_event_alloc failed");
+		return;
+	}
+
+	if (nla_put_u32(event,
+			QCA_WLAN_VENDOR_ATTR_SAP_CONDITIONAL_CHAN_SWITCH_STATUS,
+			status)) {
+		hdd_err("nla put failed");
+		kfree_skb(event);
+		return;
+	}
+
+	cfg80211_vendor_event(event, GFP_KERNEL);
+}
+
+#ifdef WLAN_FEATURE_MBSSID
+/**
+ * wlan_hdd_set_pre_cac_complete_status() - Set pre cac complete status
+ * @ap_adapter: AP adapter
+ * @status: Status which can be true or false
+ *
+ * Sets the status of pre cac i.e., whether it is complete or not
+ *
+ * Return: Zero on success, non-zero on failure
+ */
+static int wlan_hdd_set_pre_cac_complete_status(hdd_adapter_t *ap_adapter,
+		bool status)
+{
+	QDF_STATUS ret;
+
+	ret = wlan_sap_set_pre_cac_complete_status(
+			WLAN_HDD_GET_SAP_CTX_PTR(ap_adapter), status);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+#else
+static int wlan_hdd_set_pre_cac_complete_status(hdd_adapter_t *ap_adapter,
+		bool status)
+{
+	QDF_STATUS ret;
+
+	ret = wlan_sap_set_pre_cac_complete_status(
+			(WLAN_HDD_GET_CTX(ap_adapter))->pcds_context, status);
+	if (QDF_IS_STATUS_ERROR(ret))
+		return -EINVAL;
+
+	return 0;
+}
+#endif
+
+/**
+ * wlan_hdd_sap_pre_cac_failure() - Process the pre cac failure
+ * @data: AP adapter
+ *
+ * Deletes the pre cac adapter
+ *
+ * Return: None
+ */
+void wlan_hdd_sap_pre_cac_failure(void *data)
+{
+	hdd_adapter_t *pHostapdAdapter;
+	hdd_context_t *hdd_ctx;
+
+	ENTER();
+
+	pHostapdAdapter = (hdd_adapter_t *) data;
+	if (!pHostapdAdapter) {
+		hdd_err("AP adapter is NULL");
+		return;
+	}
+
+	hdd_ctx = (hdd_context_t *) (pHostapdAdapter->pHddCtx);
+	if (!hdd_ctx) {
+		hdd_err("HDD context is null");
+		return;
+	}
+
+	cds_ssr_protect(__func__);
+	hdd_stop_adapter(hdd_ctx, pHostapdAdapter, false);
+	hdd_close_adapter(hdd_ctx, pHostapdAdapter, false);
+	cds_ssr_unprotect(__func__);
+}
+
+
+/**
+ * wlan_hdd_sap_pre_cac_success() - Process the pre cac result
+ * @data: AP adapter
+ *
+ * Deletes the pre cac adapter and moves the existing SAP to the pre cac
+ * channel
+ *
+ * Return: None
+ */
+static void wlan_hdd_sap_pre_cac_success(void *data)
+{
+	hdd_adapter_t *pHostapdAdapter, *ap_adapter;
+	int i;
+	hdd_context_t *hdd_ctx;
+
+	ENTER();
+
+	pHostapdAdapter = (hdd_adapter_t *) data;
+	if (!pHostapdAdapter) {
+		hdd_err("AP adapter is NULL");
+		return;
+	}
+
+	hdd_ctx = (hdd_context_t *) (pHostapdAdapter->pHddCtx);
+	if (!hdd_ctx) {
+		hdd_err("HDD context is null");
+		return;
+	}
+
+	cds_ssr_protect(__func__);
+	hdd_stop_adapter(hdd_ctx, pHostapdAdapter, false);
+	hdd_close_adapter(hdd_ctx, pHostapdAdapter, false);
+	cds_ssr_unprotect(__func__);
+
+	/* Prepare to switch AP from 2.4GHz channel to the pre CAC channel */
+	ap_adapter = hdd_get_adapter(hdd_ctx, QDF_SAP_MODE);
+	if (!ap_adapter) {
+		hdd_err("failed to get SAP adapter, no restart on pre CAC channel");
+		return;
+	}
+
+	/*
+	 * Setting of the pre cac complete status will ensure that on channel
+	 * switch to the pre CAC DFS channel, there is no CAC again.
+	 */
+	wlan_hdd_set_pre_cac_complete_status(ap_adapter, true);
+	i = hdd_softap_set_channel_change(ap_adapter->dev,
+			ap_adapter->pre_cac_chan,
+			CH_WIDTH_MAX);
+	if (0 != i) {
+		hdd_err("failed to change channel");
+		wlan_hdd_set_pre_cac_complete_status(ap_adapter, false);
+	}
+}
+
 QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
 				    void *usrDataForCallback)
 {
@@ -997,8 +1170,9 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
 		else
 			pHddApCtx->dfs_cac_block_tx = true;
 
-		hdd_info("The value of dfs_cac_block_tx[%d] for ApCtx[%p]",
-		       pHddApCtx->dfs_cac_block_tx, pHddApCtx);
+		hdd_info("The value of dfs_cac_block_tx[%d] for ApCtx[%p]:%d",
+				pHddApCtx->dfs_cac_block_tx, pHddApCtx,
+				pHostapdAdapter->sessionId);
 
 		if ((CHANNEL_STATE_DFS ==
 		     cds_get_channel_state(pHddApCtx->operatingChannel))
@@ -1131,7 +1305,6 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
 			hdd_info("Sent CAC end to user space");
 		}
 		break;
-
 	case eSAP_DFS_RADAR_DETECT:
 		wlan_hdd_send_svc_nlink_msg(WLAN_SVC_DFS_RADAR_DETECT_IND,
 					    &dfs_info,
@@ -1145,7 +1318,30 @@ QDF_STATUS hdd_hostapd_sap_event_cb(tpSap_Event pSapEvent,
 			hdd_info("Sent radar detected to user space");
 		}
 		break;
+	case eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC:
+		hdd_debug("notification for radar detect during pre cac:%d",
+			pHostapdAdapter->sessionId);
+		hdd_send_conditional_chan_switch_status(pHddCtx,
+			&pHostapdAdapter->wdev, false);
+		pHddCtx->dev_dfs_cac_status = DFS_CAC_NEVER_DONE;
+		qdf_create_work(0, &pHddCtx->sap_pre_cac_work,
+				wlan_hdd_sap_pre_cac_failure,
+				(void *)pHostapdAdapter);
+		qdf_sched_work(0, &pHddCtx->sap_pre_cac_work);
+		break;
+	case eSAP_DFS_PRE_CAC_END:
+		hdd_debug("pre cac end notification received:%d",
+			pHostapdAdapter->sessionId);
+		hdd_send_conditional_chan_switch_status(pHddCtx,
+			&pHostapdAdapter->wdev, true);
+		pHddApCtx->dfs_cac_block_tx = false;
+		pHddCtx->dev_dfs_cac_status = DFS_CAC_ALREADY_DONE;
 
+		qdf_create_work(0, &pHddCtx->sap_pre_cac_work,
+				wlan_hdd_sap_pre_cac_success,
+				(void *)pHostapdAdapter);
+		qdf_sched_work(0, &pHddCtx->sap_pre_cac_work);
+		break;
 	case eSAP_DFS_NO_AVAILABLE_CHANNEL:
 		wlan_hdd_send_svc_nlink_msg
 			(WLAN_SVC_DFS_ALL_CHANNEL_UNAVAIL_IND, &dfs_info,
@@ -3009,6 +3205,8 @@ static __iw_softap_getparam(struct net_device *dev,
 	QDF_STATUS status;
 	int ret;
 	hdd_context_t *hdd_ctx;
+	uint8_t nol[QDF_MAX_NUM_CHAN];
+	uint32_t nol_len = 0;
 
 	ENTER_DEV(dev);
 
@@ -3135,10 +3333,11 @@ static __iw_softap_getparam(struct net_device *dev,
 		wlansap_get_dfs_nol(
 #ifdef WLAN_FEATURE_MBSSID
 			WLAN_HDD_GET_SAP_CTX_PTR
-				(pHostapdAdapter)
+				(pHostapdAdapter),
 #else
-			pHddCtx->pcds_context
+			pHddCtx->pcds_context,
 #endif
+			nol, &nol_len
 			);
 	}
 	break;
@@ -6398,7 +6597,7 @@ static bool wlan_hdd_get_sap_obss(hdd_adapter_t *pHostapdAdapter)
  *
  * Return: 0 for success non-zero for failure
  */
-static int wlan_hdd_set_channel(struct wiphy *wiphy,
+int wlan_hdd_set_channel(struct wiphy *wiphy,
 				struct net_device *dev,
 				struct cfg80211_chan_def *chandef,
 				enum nl80211_channel_type channel_type)
@@ -6856,7 +7055,7 @@ int wlan_hdd_cfg80211_alloc_new_beacon(hdd_adapter_t *pAdapter,
 	size = sizeof(beacon_data_t) + head_len + tail_len +
 		proberesp_ies_len + assocresp_ies_len;
 
-	beacon = kzalloc(size, GFP_KERNEL);
+	beacon = qdf_mem_malloc(size);
 
 	if (beacon == NULL) {
 		hdd_err("Mem allocation for beacon failed");
@@ -6891,7 +7090,8 @@ int wlan_hdd_cfg80211_alloc_new_beacon(hdd_adapter_t *pAdapter,
 
 	*ppBeacon = beacon;
 
-	kfree(old);
+	pAdapter->sessionCtx.ap.beacon = NULL;
+	qdf_mem_free(old);
 
 	return 0;
 
@@ -7354,13 +7554,15 @@ setup_acs_overrides:
  * @ssid: Pointer ssid
  * @ssid_len: Length of ssid
  * @hidden_ssid: Hidden SSID parameter
+ * @check_for_concurrency: Flag to indicate if check for concurrency is needed
  *
  * Return: 0 for success non-zero for failure
  */
-static int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
+int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
 				       struct cfg80211_beacon_data *params,
 				       const u8 *ssid, size_t ssid_len,
-				       enum nl80211_hidden_ssid hidden_ssid)
+				       enum nl80211_hidden_ssid hidden_ssid,
+				       bool check_for_concurrency)
 {
 	tsap_Config_t *pConfig;
 	beacon_data_t *pBeacon = NULL;
@@ -7804,12 +8006,14 @@ static int wlan_hdd_cfg80211_start_bss(hdd_adapter_t *pHostapdAdapter,
 		return 0;
 	}
 
-	if (!cds_allow_concurrency(
-				cds_convert_device_mode_to_qdf_type(
-				pHostapdAdapter->device_mode),
-				pConfig->channel, HW_MODE_20_MHZ)) {
-		hdd_warn("This concurrency combination is not allowed");
-		return -EINVAL;
+	if (check_for_concurrency) {
+		if (!cds_allow_concurrency(
+					cds_convert_device_mode_to_qdf_type(
+					pHostapdAdapter->device_mode),
+					pConfig->channel, HW_MODE_20_MHZ)) {
+			hdd_warn("This concurrency combination is not allowed");
+			return -EINVAL;
+		}
 	}
 
 	if (!cds_set_connection_in_progress(true)) {
@@ -8022,10 +8226,13 @@ static int __wlan_hdd_cfg80211_stop_ap(struct wiphy *wiphy,
 		cds_decr_session_set_pcl(pAdapter->device_mode,
 						pAdapter->sessionId);
 		pAdapter->sessionCtx.ap.beacon = NULL;
-		kfree(old);
+		qdf_mem_free(old);
 	}
 	mutex_unlock(&pHddCtx->sap_lock);
 
+	if (wlan_sap_is_pre_cac_active(pHddCtx->hHal))
+		hdd_clean_up_pre_cac_interface(pHddCtx);
+
 	if (status != QDF_STATUS_SUCCESS) {
 		hdd_err("Stopping the BSS");
 		return -EINVAL;
@@ -8247,7 +8454,7 @@ static int __wlan_hdd_cfg80211_start_ap(struct wiphy *wiphy,
 			wlan_hdd_cfg80211_start_bss(pAdapter,
 				&params->beacon,
 				params->ssid, params->ssid_len,
-				params->hidden_ssid);
+				params->hidden_ssid, true);
 	}
 
 	EXIT();
@@ -8333,7 +8540,8 @@ static int __wlan_hdd_cfg80211_change_beacon(struct wiphy *wiphy,
 	}
 
 	pAdapter->sessionCtx.ap.beacon = new;
-	status = wlan_hdd_cfg80211_start_bss(pAdapter, params, NULL, 0, 0);
+	status = wlan_hdd_cfg80211_start_bss(pAdapter, params, NULL,
+						0, 0, true);
 
 	EXIT();
 	return status;

+ 38 - 1
core/hdd/src/wlan_hdd_main.c

@@ -3506,7 +3506,7 @@ QDF_STATUS hdd_stop_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
 			}
 			/* Reset WNI_CFG_PROBE_RSP Flags */
 			wlan_hdd_reset_prob_rspies(adapter);
-			kfree(adapter->sessionCtx.ap.beacon);
+			qdf_mem_free(adapter->sessionCtx.ap.beacon);
 			adapter->sessionCtx.ap.beacon = NULL;
 		}
 		mutex_unlock(&hdd_ctx->sap_lock);
@@ -3533,6 +3533,8 @@ QDF_STATUS hdd_stop_all_adapters(hdd_context_t *hdd_ctx)
 
 	ENTER();
 
+	cds_flush_work(&hdd_ctx->sap_pre_cac_work);
+
 	status = hdd_get_front_adapter(hdd_ctx, &adapterNode);
 
 	while (NULL != adapterNode && QDF_STATUS_SUCCESS == status) {
@@ -3555,6 +3557,8 @@ QDF_STATUS hdd_reset_all_adapters(hdd_context_t *hdd_ctx)
 
 	ENTER();
 
+	cds_flush_work(&hdd_ctx->sap_pre_cac_work);
+
 	status = hdd_get_front_adapter(hdd_ctx, &adapterNode);
 
 	while (NULL != adapterNode && QDF_STATUS_SUCCESS == status) {
@@ -8745,6 +8749,39 @@ static inline bool hdd_is_lpass_supported(hdd_context_t *hdd_ctx)
 }
 #endif
 
+/**
+ * hdd_clean_up_pre_cac_interface() - Clean up the pre cac interface
+ * @hdd_ctx: HDD context
+ *
+ * Cleans up the pre cac interface, if it exists
+ *
+ * Return: None
+ */
+void hdd_clean_up_pre_cac_interface(hdd_context_t *hdd_ctx)
+{
+	uint8_t session_id;
+	QDF_STATUS status;
+	struct hdd_adapter_s *precac_adapter;
+
+	status = wlan_sap_get_pre_cac_vdev_id(hdd_ctx->hHal, &session_id);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		hdd_err("failed to get pre cac vdev id");
+		return;
+	}
+
+	precac_adapter = hdd_get_adapter_by_vdev(hdd_ctx, session_id);
+	if (!precac_adapter) {
+		hdd_err("invalid pre cac adapater");
+		return;
+	}
+
+	qdf_create_work(0, &hdd_ctx->sap_pre_cac_work,
+			wlan_hdd_sap_pre_cac_failure,
+			(void *)precac_adapter);
+	qdf_sched_work(0, &hdd_ctx->sap_pre_cac_work);
+
+}
+
 /**
  * hdd_update_ol_config - API to update ol configuration parameters
  * @hdd_ctx: HDD context

+ 5 - 0
core/hdd/src/wlan_hdd_p2p.c

@@ -2136,6 +2136,11 @@ int __wlan_hdd_del_virtual_intf(struct wiphy *wiphy, struct wireless_dev *wdev)
 	wlan_hdd_release_intf_addr(pHddCtx,
 				   pVirtAdapter->macAddressCurrent.bytes);
 
+	if ((pVirtAdapter->device_mode == QDF_SAP_MODE) &&
+		wlan_sap_is_pre_cac_active(pHddCtx->hHal)) {
+		hdd_clean_up_pre_cac_interface(pHddCtx);
+	}
+
 	hdd_stop_adapter(pHddCtx, pVirtAdapter, true);
 	hdd_close_adapter(pHddCtx, pVirtAdapter, true);
 	EXIT();

+ 1 - 0
core/mac/inc/sir_api.h

@@ -133,6 +133,7 @@ enum sir_conn_update_reason {
 	SIR_UPDATE_REASON_NSS_UPDATE,
 	SIR_UPDATE_REASON_CHANNEL_SWITCH,
 	SIR_UPDATE_REASON_CHANNEL_SWITCH_STA,
+	SIR_UPDATE_REASON_PRE_CAC,
 };
 
 typedef enum {

+ 14 - 0
core/mac/src/pe/lim/lim_session.c

@@ -118,6 +118,20 @@ void pe_reset_protection_callback(void *ptr)
 		return;
 	}
 
+	/*
+	 * During CAC period, if the callback is triggered, the beacon
+	 * template may get updated. Subsequently if the vdev is not up, the
+	 * vdev would be made up -- which should not happen during the CAC
+	 * period. To avoid this, ignore the protection callback if the session
+	 * is not yet up.
+	 */
+	if (!wma_is_vdev_up(pe_session_entry->smeSessionId)) {
+		QDF_TRACE(QDF_MODULE_ID_PE,
+			  QDF_TRACE_LEVEL_ERROR,
+			  FL("session is not up yet. exiting timer callback"));
+		return;
+	}
+
 	current_protection_state |=
 	       pe_session_entry->gLimOverlap11gParams.protectionEnabled        |
 	       pe_session_entry->gLimOverlap11aParams.protectionEnabled   << 1 |

+ 10 - 2
core/sap/inc/sap_api.h

@@ -171,7 +171,9 @@ typedef enum {
 	eSAP_DFS_CAC_START,
 	eSAP_DFS_CAC_INTERRUPTED,
 	eSAP_DFS_CAC_END,
+	eSAP_DFS_PRE_CAC_END,
 	eSAP_DFS_RADAR_DETECT,
+	eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC,
 	/* Event sent when user need to get the DFS NOL from CNSS */
 	eSAP_DFS_NOL_GET,
 	/* Event sent when user need to set the DFS NOL to CNSS */
@@ -818,7 +820,13 @@ uint8_t wlansap_get_state(void *p_cds_gctx);
 QDF_STATUS wlansap_start_bss(void *p_cds_gctx,
 	 tpWLAN_SAPEventCB pSapEventCallback,
 	 tsap_Config_t *pConfig, void *pUsrContext);
-
+QDF_STATUS wlan_sap_set_pre_cac_status(void *ctx, bool status,
+		tHalHandle handle);
+QDF_STATUS wlan_sap_set_chan_before_pre_cac(void *ctx,
+		uint8_t chan_before_pre_cac);
+QDF_STATUS wlan_sap_set_pre_cac_complete_status(void *ctx, bool status);
+bool wlan_sap_is_pre_cac_active(tHalHandle handle);
+QDF_STATUS wlan_sap_get_pre_cac_vdev_id(tHalHandle handle, uint8_t *vdev_id);
 #ifdef FEATURE_WLAN_MCC_TO_SCC_SWITCH
 uint16_t wlansap_check_cc_intf(void *Ctx);
 #endif
@@ -898,7 +906,7 @@ void wlansap_extend_to_acs_range(uint8_t *startChannelNum,
 		uint8_t *endChannelNum,
 		uint8_t *bandStartChannel,
 		uint8_t *bandEndChannel);
-QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx);
+QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx, uint8_t *nol, uint32_t *nol_len);
 QDF_STATUS wlansap_set_dfs_nol(void *pSapCtx, eSapDfsNolType conf);
 void wlansap_populate_del_sta_params(const uint8_t *mac,
 		uint16_t reason_code,

+ 35 - 2
core/sap/src/sap_api_link_cntl.c

@@ -455,7 +455,18 @@ wlansap_roam_process_ch_change_success(tpAniSirGlobal mac_ctx,
 	}
 
 	/* check if currently selected channel is a DFS channel */
-	if (is_ch_dfs) {
+	if (is_ch_dfs && sap_ctx->pre_cac_complete) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED, FL(
+		    "sapdfs: eSAP_DISCONNECTING => eSAP_STARTING, on pre cac"));
+		/* Start beaconing on the new pre cac channel */
+		wlansap_start_beacon_req(sap_ctx);
+		sap_ctx->sapsMachine = eSAP_STARTING;
+		mac_ctx->sap.SapDfsInfo.sap_radar_found_status = false;
+		sap_event.event = eSAP_MAC_START_BSS_SUCCESS;
+		sap_event.params = csr_roam_info;
+		sap_event.u1 = eCSR_ROAM_INFRA_IND;
+		sap_event.u2 = eCSR_ROAM_RESULT_INFRA_STARTED;
+	} else if (is_ch_dfs) {
 		if ((false == mac_ctx->sap.SapDfsInfo.ignore_cac)
 		    && (eSAP_DFS_DO_NOT_SKIP_CAC ==
 			mac_ctx->sap.SapDfsInfo.cac_state)) {
@@ -533,6 +544,7 @@ wlansap_roam_process_dfs_chansw_update(tHalHandle hHal,
 		 * with no CSA IE will be sent to firmware.
 		 */
 		dfs_beacon_start_req = eSAP_TRUE;
+		sap_ctx->pre_cac_complete = false;
 		*ret_status = sme_roam_start_beacon_req(hHal, sap_ctx->bssid,
 							dfs_beacon_start_req);
 		return;
@@ -663,8 +675,12 @@ wlansap_roam_process_dfs_radar_found(tpAniSirGlobal mac_ctx,
 				"sapdfs: DFS channel switch disabled");
 			return;
 		}
-		if (false == mac_ctx->sap.SapDfsInfo.sap_radar_found_status)
+		if (false == mac_ctx->sap.SapDfsInfo.sap_radar_found_status) {
+			QDF_TRACE(QDF_MODULE_ID_SAP,
+				QDF_TRACE_LEVEL_ERROR,
+				"sapdfs: sap_radar_found_status is false");
 			return;
+		}
 		QDF_TRACE(QDF_MODULE_ID_SAP,
 			  QDF_TRACE_LEVEL_INFO_MED,
 			  FL("sapdfs:Posting event eSAP_DFS_CHANNEL_CAC_RADAR_FOUND"));
@@ -926,6 +942,23 @@ wlansap_roam_callback(void *ctx, tCsrRoamInfo *csr_roam_info, uint32_t roamId,
 	case eCSR_ROAM_DFS_RADAR_IND:
 		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
 			  FL("Received Radar Indication"));
+
+		if (sap_ctx->is_pre_cac_on) {
+			QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
+				FL("sapdfs: Radar detect on pre cac:%d"),
+				sap_ctx->sessionId);
+			qdf_mc_timer_stop(
+				&mac_ctx->sap.SapDfsInfo.sap_dfs_cac_timer);
+			qdf_mc_timer_destroy(
+				&mac_ctx->sap.SapDfsInfo.sap_dfs_cac_timer);
+			mac_ctx->sap.SapDfsInfo.is_dfs_cac_timer_running =
+				false;
+			sap_signal_hdd_event(sap_ctx, NULL,
+					eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC,
+					(void *) eSAP_STATUS_SUCCESS);
+			break;
+		}
+
 		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
 			  FL("sapdfs: Indicate eSAP_DFS_RADAR_DETECT to HDD"));
 		sap_signal_hdd_event(sap_ctx, NULL, eSAP_DFS_RADAR_DETECT,

+ 129 - 20
core/sap/src/sap_fsm.c

@@ -827,7 +827,9 @@ static uint8_t *sap_hdd_event_to_string(eSapHddEvent event)
 	CASE_RETURN_STRING(eSAP_CHANNEL_CHANGE_EVENT);
 	CASE_RETURN_STRING(eSAP_DFS_CAC_START);
 	CASE_RETURN_STRING(eSAP_DFS_CAC_END);
+	CASE_RETURN_STRING(eSAP_DFS_PRE_CAC_END);
 	CASE_RETURN_STRING(eSAP_DFS_RADAR_DETECT);
+	CASE_RETURN_STRING(eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC);
 	CASE_RETURN_STRING(eSAP_DFS_NOL_GET);
 	CASE_RETURN_STRING(eSAP_DFS_NOL_SET);
 	CASE_RETURN_STRING(eSAP_DFS_NO_AVAILABLE_CHANNEL);
@@ -2450,6 +2452,9 @@ QDF_STATUS sap_open_session(tHalHandle hHal, ptSapContext sapContext,
 		sapContext->csr_roamProfile.csrPersona;
 	*session_id = sapContext->sessionId;
 	sapContext->isSapSessionOpen = eSAP_TRUE;
+	sapContext->is_pre_cac_on = false;
+	sapContext->pre_cac_complete = false;
+	sapContext->chan_before_pre_cac = 0;
 	return QDF_STATUS_SUCCESS;
 }
 
@@ -2704,7 +2709,9 @@ QDF_STATUS sap_signal_hdd_event(ptSapContext sap_ctx,
 	case eSAP_DFS_CAC_START:
 	case eSAP_DFS_CAC_INTERRUPTED:
 	case eSAP_DFS_CAC_END:
+	case eSAP_DFS_PRE_CAC_END:
 	case eSAP_DFS_RADAR_DETECT:
+	case eSAP_DFS_RADAR_DETECT_DURING_PRE_CAC:
 	case eSAP_DFS_NO_AVAILABLE_CHANNEL:
 #ifdef FEATURE_WLAN_AP_AP_ACS_OPTIMIZE
 	case eSAP_ACS_SCAN_SUCCESS_EVENT:
@@ -2985,6 +2992,45 @@ ptSapContext sap_find_valid_concurrent_session(tHalHandle hHal)
 	return NULL;
 }
 
+/**
+ * sap_find_cac_wait_session() - Get context of a SAP session in CAC wait state
+ * @handle: Global MAC handle
+ *
+ * Finds and gets the context of a SAP session in CAC wait state.
+ *
+ * Return: Valid SAP context on success, else NULL
+ */
+ptSapContext sap_find_cac_wait_session(tHalHandle handle)
+{
+	tpAniSirGlobal mac = PMAC_STRUCT(handle);
+	uint8_t i = 0;
+	ptSapContext sapContext;
+
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
+			"%s", __func__);
+
+	for (i = 0; i < SAP_MAX_NUM_SESSION; i++) {
+		sapContext = (ptSapContext) mac->sap.sapCtxList[i].pSapContext;
+		if (((QDF_SAP_MODE == mac->sap.sapCtxList[i].sapPersona)
+		    ||
+		    (QDF_P2P_GO_MODE == mac->sap.sapCtxList[i].sapPersona)) &&
+		    (sapContext) &&
+		    (sapContext->sapsMachine == eSAP_DFS_CAC_WAIT)) {
+			QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
+				"%s: found SAP in cac wait state", __func__);
+			return sapContext;
+		}
+		if (sapContext) {
+			QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
+					"sapdfs: mode:%d intf:%d state:%d",
+					mac->sap.sapCtxList[i].sapPersona, i,
+					sapContext->sapsMachine);
+		}
+	}
+
+	return NULL;
+}
+
 /*==========================================================================
    FUNCTION   sap_close_session
 
@@ -3028,6 +3074,9 @@ QDF_STATUS sap_close_session(tHalHandle hHal,
 	sapContext->isCacEndNotified = false;
 	pMac->sap.sapCtxList[sapContext->sessionId].pSapContext = NULL;
 	sapContext->isSapSessionOpen = false;
+	sapContext->pre_cac_complete = false;
+	sapContext->is_pre_cac_on = false;
+	sapContext->chan_before_pre_cac = 0;
 
 	if (NULL == sap_find_valid_concurrent_session(hHal)) {
 		/* If timer is running then stop the timer and destory it */
@@ -3133,6 +3182,44 @@ QDF_STATUS sap_cac_start_notify(tHalHandle hHal)
 	return qdf_status;
 }
 
+/**
+ * wlansap_update_pre_cac_end() - Update pre cac end to upper layer
+ * @sap_context: SAP context
+ * @mac: Global MAC structure
+ * @intf: Interface number
+ *
+ * Notifies pre cac end to upper layer
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS wlansap_update_pre_cac_end(ptSapContext sap_context,
+		tpAniSirGlobal mac, uint8_t intf)
+{
+	QDF_STATUS qdf_status;
+
+	sap_context->isCacEndNotified = true;
+	mac->sap.SapDfsInfo.sap_radar_found_status = false;
+	sap_context->sapsMachine = eSAP_STARTED;
+
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			"In %s, pre cac end notify on %d: from state %s => %s",
+			__func__, intf, "eSAP_DFS_CAC_WAIT",
+			"eSAP_STARTED");
+
+	qdf_status = sap_signal_hdd_event(sap_context,
+			NULL, eSAP_DFS_PRE_CAC_END,
+			(void *)eSAP_STATUS_SUCCESS);
+	if (QDF_IS_STATUS_ERROR(qdf_status)) {
+		QDF_TRACE(QDF_MODULE_ID_SAP,
+				QDF_TRACE_LEVEL_ERROR,
+				"In %s, pre cac notify failed on intf %d",
+				__func__, intf);
+		return qdf_status;
+	}
+
+	return QDF_STATUS_SUCCESS;
+}
+
 /*==========================================================================
    FUNCTION  sap_cac_end_notify
 
@@ -3165,8 +3252,26 @@ QDF_STATUS sap_cac_end_notify(tHalHandle hHal, tCsrRoamInfo *roamInfo)
 		    ||
 		    (QDF_P2P_GO_MODE == pMac->sap.sapCtxList[intf].sapPersona))
 		    && pMac->sap.sapCtxList[intf].pSapContext != NULL &&
-		    (false == pSapContext->isCacEndNotified)) {
+		    (false == pSapContext->isCacEndNotified) &&
+		    (pSapContext->sapsMachine == eSAP_DFS_CAC_WAIT)) {
 			pSapContext = pMac->sap.sapCtxList[intf].pSapContext;
+
+			/* If this is an end notification of a pre cac, the
+			 * SAP must not start beaconing and must delete the
+			 * temporary interface created for pre cac and switch
+			 * the original SAP to the pre CAC channel.
+			 */
+			if (pSapContext->is_pre_cac_on) {
+				qdf_status = wlansap_update_pre_cac_end(
+						pSapContext, pMac, intf);
+				if (QDF_IS_STATUS_ERROR(qdf_status))
+					return qdf_status;
+				/* pre CAC is not allowed with any concurrency.
+				 * So, we can break from here.
+				 */
+				break;
+			}
+
 			qdf_status = sap_signal_hdd_event(pSapContext, NULL,
 							  eSAP_DFS_CAC_END,
 							  (void *)
@@ -3649,11 +3754,13 @@ static QDF_STATUS sap_fsm_state_starting(ptSapContext sap_ctx,
 							CHANNEL_STATE_DFS)
 				is_dfs = true;
 		}
+
 		if (is_dfs) {
 			sap_dfs_info = &mac_ctx->sap.SapDfsInfo;
 			if ((false == sap_dfs_info->ignore_cac) &&
 			    (eSAP_DFS_DO_NOT_SKIP_CAC ==
-					sap_dfs_info->cac_state)) {
+					sap_dfs_info->cac_state) &&
+			    !sap_ctx->pre_cac_complete) {
 				/* Move the device in CAC_WAIT_STATE */
 				sap_ctx->sapsMachine = eSAP_DFS_CAC_WAIT;
 
@@ -4608,6 +4715,13 @@ uint8_t sap_indicate_radar(ptSapContext sapContext,
 			      dfs_event->chan_list.nchannels,
 			      cds_get_monotonic_boottime());
 
+	if (sapContext->chan_before_pre_cac) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO,
+			FL("sapdfs: set chan before pre cac %d as target chan"),
+			sapContext->chan_before_pre_cac);
+		return sapContext->chan_before_pre_cac;
+	}
+
 	/*
 	 * (1) skip static turbo channel as it will require STA to be in
 	 * static turbo to work.
@@ -4646,11 +4760,10 @@ void sap_dfs_cac_timer_callback(void *data)
 		return;
 	}
 	pMac = PMAC_STRUCT(hHal);
-	sapContext = sap_find_valid_concurrent_session(hHal);
-
+	sapContext = sap_find_cac_wait_session(hHal);
 	if (NULL == sapContext) {
 		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
-			  "In %s no SAP contexts are found", __func__);
+			"%s: no SAP contexts in wait state", __func__);
 		return;
 	}
 
@@ -4664,23 +4777,19 @@ void sap_dfs_cac_timer_callback(void *data)
 		pMac->sap.SapDfsInfo.is_dfs_cac_timer_running = false;
 	}
 
-	/* Check to ensure that SAP is in DFS WAIT state */
-	if (sapContext->sapsMachine == eSAP_DFS_CAC_WAIT) {
-		/*
-		 * CAC Complete, post eSAP_DFS_CHANNEL_CAC_END to sap_fsm
-		 */
-		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
-			  "sapdfs: Sending eSAP_DFS_CHANNEL_CAC_END for target_channel = %d on sapctx[%p]",
-			  sapContext->channel, sapContext);
-
-		sapEvent.event = eSAP_DFS_CHANNEL_CAC_END;
-		sapEvent.params = 0;
-		sapEvent.u1 = 0;
-		sapEvent.u2 = 0;
+	/*
+	 * CAC Complete, post eSAP_DFS_CHANNEL_CAC_END to sap_fsm
+	 */
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_MED,
+			"sapdfs: Sending eSAP_DFS_CHANNEL_CAC_END for target_channel = %d on sapctx[%p]",
+			sapContext->channel, sapContext);
 
-		sap_fsm(sapContext, &sapEvent);
-	}
+	sapEvent.event = eSAP_DFS_CHANNEL_CAC_END;
+	sapEvent.params = 0;
+	sapEvent.u1 = 0;
+	sapEvent.u2 = 0;
 
+	sap_fsm(sapContext, &sapEvent);
 }
 
 /*

+ 3 - 0
core/sap/src/sap_internal.h

@@ -264,6 +264,9 @@ typedef struct sSapContext {
 	uint32_t roc_ind_scan_id;
 
 	qdf_event_t sap_session_opened_evt;
+	bool is_pre_cac_on;
+	bool pre_cac_complete;
+	uint8_t chan_before_pre_cac;
 } *ptSapContext;
 
 /*----------------------------------------------------------------------------

+ 171 - 21
core/sap/src/sap_module.c

@@ -1659,6 +1659,11 @@ wlansap_set_channel_change_with_csa(void *p_cds_gctx, uint32_t targetChannel,
 	}
 	pMac = PMAC_STRUCT(hHal);
 
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO,
+		"%s: sap chan:%d target:%d conc:%d",
+		__func__, sapContext->channel, targetChannel,
+		cds_concurrent_open_sessions_running());
+
 	/*
 	 * Now, validate if the passed channel is valid in the
 	 * current regulatory domain.
@@ -2301,6 +2306,155 @@ QDF_STATUS wlansap_cancel_remain_on_channel(void *pCtx,
 
 	return QDF_STATUS_E_FAULT;
 }
+
+/**
+ * wlan_sap_set_pre_cac_status() - Set the pre cac status
+ * @ctx: SAP context
+ * @status: Status of pre cac
+ * @handle: Global MAC handle
+ *
+ * Sets the pre cac status in the MAC context and updates the state
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_sap_set_pre_cac_status(void *ctx, bool status,
+					tHalHandle handle)
+{
+	ptSapContext sap_ctx = CDS_GET_SAP_CB(ctx);
+	tpAniSirGlobal mac_ctx = PMAC_STRUCT(handle);
+
+	if (!mac_ctx) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Invalid mac pointer", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	if (!sap_ctx) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Invalid SAP pointer", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	sap_ctx->is_pre_cac_on = status;
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG,
+		"%s: is_pre_cac_on:%d", __func__, sap_ctx->is_pre_cac_on);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * wlan_sap_set_chan_before_pre_cac() - Save the channel before pre cac
+ * @ctx: SAP context
+ * @chan_before_pre_cac: Channel before pre cac
+ *
+ * Saves the channel that was in use before pre cac operation
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_sap_set_chan_before_pre_cac(void *ctx,
+					uint8_t chan_before_pre_cac)
+{
+	ptSapContext sap_ctx = CDS_GET_SAP_CB(ctx);
+
+	if (!sap_ctx) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Invalid SAP pointer", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	sap_ctx->chan_before_pre_cac = chan_before_pre_cac;
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * wlan_sap_set_pre_cac_complete_status() - Sets pre cac complete status
+ * @ctx: SAP context
+ * @status: Status of pre cac complete
+ *
+ * Sets the status of pre cac i.e., whether pre cac is complete or not
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_sap_set_pre_cac_complete_status(void *ctx, bool status)
+{
+	ptSapContext sap_ctx = CDS_GET_SAP_CB(ctx);
+
+	if (!sap_ctx) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
+			  "%s: Invalid SAP pointer", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	sap_ctx->pre_cac_complete = status;
+
+	QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_DEBUG,
+			"%s: pre cac complete status:%d session:%d",
+			__func__, status, sap_ctx->sessionId);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * wlan_sap_is_pre_cac_active() - Checks if pre cac in in progress
+ * @handle: Global MAC handle
+ *
+ * Checks if pre cac is in progress in any of the SAP contexts
+ *
+ * Return: True is pre cac is active, false otherwise
+ */
+bool wlan_sap_is_pre_cac_active(tHalHandle handle)
+{
+	tpAniSirGlobal mac = NULL;
+	int i;
+
+	mac = PMAC_STRUCT(handle);
+	if (!mac) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
+			"%s: Invalid mac context", __func__);
+		return false;
+	}
+
+	for (i = 0; i < SAP_MAX_NUM_SESSION; i++) {
+		ptSapContext context =
+			(ptSapContext) mac->sap.sapCtxList[i].pSapContext;
+		if (context && context->is_pre_cac_on)
+			return true;
+	}
+	return false;
+}
+
+/**
+ * wlan_sap_get_pre_cac_vdev_id() - Get vdev id of the pre cac interface
+ * @handle: Global handle
+ * @vdev_id: vdev id
+ *
+ * Fetches the vdev id of the pre cac interface
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlan_sap_get_pre_cac_vdev_id(tHalHandle handle, uint8_t *vdev_id)
+{
+	tpAniSirGlobal mac = NULL;
+	uint8_t i;
+
+	mac = PMAC_STRUCT(handle);
+	if (!mac) {
+		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_INFO_HIGH,
+			"%s: Invalid mac context", __func__);
+		return QDF_STATUS_E_FAULT;
+	}
+
+	for (i = 0; i < SAP_MAX_NUM_SESSION; i++) {
+		ptSapContext context =
+			(ptSapContext) mac->sap.sapCtxList[i].pSapContext;
+		if (context && context->is_pre_cac_on) {
+			*vdev_id = i;
+			return QDF_STATUS_SUCCESS;
+		}
+	}
+	return QDF_STATUS_E_FAILURE;
+}
+
 /**
  * wlansap_register_mgmt_frame() - register management frame
  * @pCtx: Pointer to the global cds context; a handle to SAP's control block
@@ -2548,6 +2702,7 @@ QDF_STATUS wlansap_start_beacon_req(void *pSapCtx)
 	if (pMac->sap.SapDfsInfo.sap_radar_found_status == false) {
 		/* CAC Wait done without any Radar Detection */
 		dfsCacWaitStatus = true;
+		sapContext->pre_cac_complete = false;
 		qdf_ret_status = sme_roam_start_beacon_req(hHal,
 							   sapContext->bssid,
 							   dfsCacWaitStatus);
@@ -3077,28 +3232,19 @@ void wlansap_extend_to_acs_range(uint8_t *startChannelNum,
 	}
 }
 
-/*==========================================================================
-   FUNCTION    wlansap_get_dfs_nol
-
-   DESCRIPTION
-   This API is used to dump the dfs nol
-   DEPENDENCIES
-   NA.
-
-   PARAMETERS
-   IN
-   sapContext: Pointer to cds global context structure
-
-   RETURN VALUE
-   The QDF_STATUS code associated with performing the operation
-
-   QDF_STATUS_SUCCESS:  Success
-
-   SIDE EFFECTS
-   ============================================================================*/
-QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx)
+/**
+ * wlansap_get_dfs_nol() - Get the DFS NOL
+ * @pSapCtx: SAP context
+ * @nol: Pointer to the NOL
+ * @nol_len: Length of the NOL
+ *
+ * Provides the DFS NOL
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx, uint8_t *nol, uint32_t *nol_len)
 {
-	int i = 0;
+	int i = 0, j = 0;
 	ptSapContext sapContext = (ptSapContext) pSapCtx;
 	void *hHal = NULL;
 	tpAniSirGlobal pMac = NULL;
@@ -3106,6 +3252,7 @@ QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx)
 	unsigned long left_time;
 	tSapDfsNolInfo *dfs_nol = NULL;
 	bool bAvailable = false;
+	*nol_len = 0;
 
 	if (NULL == sapContext) {
 		QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
@@ -3168,6 +3315,9 @@ QDF_STATUS wlansap_get_dfs_nol(void *pSapCtx)
 			left_time = SAP_DFS_NON_OCCUPANCY_PERIOD - elapsed_time;
 			left_time = left_time / (60 * 1000 * 1000);
 
+			nol[j++] = dfs_nol[i].dfs_channel_number;
+			(*nol_len)++;
+
 			QDF_TRACE(QDF_MODULE_ID_SAP, QDF_TRACE_LEVEL_ERROR,
 				  "%s: Channel[%d] is UNAVAILABLE [%lu min left]",
 				  __func__,