Explorar o código

qca-wifi: Add WIN only DFS files from qca-wifi-host-cmn

WIN DFS only files are moved from qca-wifi-host-cmn to qca-wifi-oss

Change-Id: Ieeb658331fd9990a48744248e83a35760259d27d
Hariharan Basuthkar %!s(int64=6) %!d(string=hai) anos
pai
achega
fa65cbe5a8

+ 316 - 0
umac/dfs/core/src/filtering/dfs_ar.c

@@ -0,0 +1,316 @@
+/*
+ * Copyright (c) 2013, 2016-2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2002-2010, Atheros Communications Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/**
+ * DOC: This file contains now obsolete code which used to implement AR
+ * (Adaptive Radio) feature for older chipsets.
+ */
+
+#include "dfs.h"
+
+#define UPDATE_TOP_THREE_PEAKS(_histo, _peakPtrList, _currWidth) \
+	do {	\
+		if ((_histo)[(_peakPtrList)[0]] < (_histo)[(_currWidth)]) { \
+			(_peakPtrList)[2] = \
+			(_currWidth != (_peakPtrList)[1]) ? \
+			(_peakPtrList)[1] : (_peakPtrList)[2];  \
+			(_peakPtrList)[1] = (_peakPtrList)[0]; \
+			(_peakPtrList)[0] = (_currWidth); \
+		} else if ((_currWidth != (_peakPtrList)[0]) \
+				&& ((_histo)[(_peakPtrList)[1]] <	\
+					(_histo)[(_currWidth)])) { \
+			(_peakPtrList)[2] = (_peakPtrList)[1]; \
+			(_peakPtrList)[1] = (_currWidth);      \
+		} else if ((_currWidth != (_peakPtrList)[1])   \
+				&& (_currWidth != (_peakPtrList)[0])  \
+				&& ((_histo)[(_peakPtrList)[2]] < \
+					(_histo)[(_currWidth)])) { \
+			(_peakPtrList)[2] = (_currWidth);  \
+		} \
+	} while (0)
+
+void dfs_process_ar_event(struct wlan_dfs *dfs,
+		struct dfs_channel *chan)
+{
+	struct dfs_ar_state *ar;
+	struct dfs_event *re = NULL;
+	uint32_t sumpeak = 0, numpeaks = 0;
+	uint32_t rssi = 0, width = 0;
+	uint32_t origregionsum = 0, i = 0;
+	uint16_t thistimestamp;
+	int empty;
+
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+
+	ar = (struct dfs_ar_state *) &(dfs->dfs_ar_state);
+	WLAN_ARQ_LOCK(dfs);
+	empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+	WLAN_ARQ_UNLOCK(dfs);
+	while (!empty) {
+		WLAN_ARQ_LOCK(dfs);
+		re = STAILQ_FIRST(&(dfs->dfs_arq));
+		if (re != NULL)
+			STAILQ_REMOVE_HEAD(&(dfs->dfs_arq), re_list);
+		WLAN_ARQ_UNLOCK(dfs);
+		if (!re)
+			return;
+
+		thistimestamp = re->re_ts;
+		rssi = re->re_rssi;
+		width = re->re_dur;
+
+		/* Return the dfs event to the free event list. */
+		qdf_mem_zero(re, sizeof(struct dfs_event));
+		WLAN_DFSEVENTQ_LOCK(dfs);
+		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), re, re_list);
+		WLAN_DFSEVENTQ_UNLOCK(dfs);
+
+		/*
+		 * Determine if current radar is an extension of previous
+		 * radar.
+		 */
+		if (ar->ar_prevwidth == 255) {
+			/*
+			 * Tag on previous width for consideraion of low data
+			 * rate ACKs.
+			 */
+			ar->ar_prevwidth += width;
+			width = (width == 255) ? 255 : ar->ar_prevwidth;
+		} else if ((width == 255) &&
+				(ar->ar_prevwidth == 510 ||
+				 ar->ar_prevwidth == 765 ||
+				 ar->ar_prevwidth == 1020)) {
+			/*
+			 * Aggregate up to 5 consecuate max radar widths to
+			 * consider 11Mbps long preamble 1500-byte pkts.
+			 */
+			ar->ar_prevwidth += width;
+		} else if (ar->ar_prevwidth == 1275 && width != 255) {
+			/* Found 5th consecute maxed out radar, reset history */
+			width += ar->ar_prevwidth;
+			ar->ar_prevwidth = 0;
+		} else if (ar->ar_prevwidth > 255) {
+			/*
+			 * Ignore if there are less than 5 consecutive maxed
+			 * out radars.
+			 */
+			ar->ar_prevwidth = width;
+			width = 255;
+		} else {
+			ar->ar_prevwidth = width;
+		}
+
+		/*
+		 * For ignoring noises with radar duration in ranges
+		 * of 3-30: AP4x. Region 7 - 5.5Mbps (long pre)
+		 * ACK = 270 = 216 us.
+		 */
+		if ((width >= 257 && width <= 278) ||
+				/*
+				 * Region 8 - 2Mbps (long pre)
+				 * ACKC = 320 = 256us.
+				 */
+				(width >= 295 && width <= 325) ||
+				(width >= 1280 && width <= 1300)) {
+			uint16_t wraparoundadj = 0;
+			uint16_t base = (width >= 1280) ? 1275 : 255;
+
+			if (thistimestamp < ar->ar_prevtimestamp)
+				wraparoundadj = 32768;
+
+			if ((thistimestamp + wraparoundadj -
+				    ar->ar_prevtimestamp) != (width - base))
+				width = 1;
+		}
+		if (width <= 10) {
+			WLAN_ARQ_LOCK(dfs);
+			empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+			WLAN_ARQ_UNLOCK(dfs);
+			continue;
+		}
+
+		/*
+		 * Overloading the width=2 in: Store a count of
+		 * radars w/max duration and high RSSI (not noise)
+		 */
+		if ((width == 255) && (rssi > DFS_AR_RSSI_THRESH_STRONG_PKTS))
+			width = 2;
+
+		/*
+		 * Overloading the width=3 bin:
+		 * Double and store a count of rdars of durtaion that matches
+		 * 11Mbps (long preamble) TCP ACKs or 1500-byte data packets.
+		 */
+		if ((width >= 1280 && width <= 1300) ||
+				(width >= 318 && width <= 325)) {
+			width = 3;
+			ar->ar_phyerrcount[3] += 2;
+			ar->ar_acksum += 2;
+		}
+
+		/* Build histogram of radar duration. */
+		if (width > 0 && width <= 510)
+			ar->ar_phyerrcount[width]++;
+		else {
+			/* Invalid radar width, throw it away. */
+			WLAN_ARQ_LOCK(dfs);
+			empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+			WLAN_ARQ_UNLOCK(dfs);
+			continue;
+		}
+
+		/* Received radar of interest (i.e., signature match),
+		 * proceed to check if there is enough neighboring
+		 * traffic to drop out of Turbo.
+		 */
+		/* Region 0: 24Mbps ACK = 35 = 28us */
+		if ((width >= REG0_MIN_WIDTH && width <= REG0_MAX_WIDTH) ||
+			/* Region 1: 12Mbps ACK = 40 = 32us */
+			(width >= REG1_MIN_WIDTH && width <= REG1_MAX_WIDTH) ||
+			/* Region 2: 6Mbps ACK = 55 = 44us */
+			(width >= REG2_MIN_WIDTH && width <= REG2_MAX_WIDTH) ||
+			/* Region 3: 11Mbps ACK = 135 = 108us */
+			(width >= REG3_MIN_WIDTH && width <= REG3_MAX_WIDTH) ||
+			/* Region 4: 5.5Mbps ACK = 150 = 120us */
+			(width >= REG4_MIN_WIDTH && width <= REG4_MAX_WIDTH) ||
+			/* Region 5: 2Mbps ACK = 200 = 160us */
+			(width >= REG5_MIN_WIDTH && width <= REG5_MAX_WIDTH) ||
+			/* Region 6: 1Mbps ACK = 400 = 320us */
+			(width >= REG6_MIN_WIDTH && width <= REG6_MAX_WIDTH) ||
+			/* Region 7: 5.5Mbps (Long Pre) ACK = 270 = 216us. */
+			(width >= REG7_MIN_WIDTH && width <= REG7_MAX_WIDTH) ||
+			/* Region 8: 2Mbps (Long Pre) ACK = 320 = 256us. */
+			(width >= REG8_MIN_WIDTH && width <= REG8_MAX_WIDTH) ||
+			/*
+			 * Ignoring Region 9 due to overlap with 255 which is
+			 * same as board noise.
+			 */
+			/* Region 9: 11Mbps (Long Pre) ACK = 255 = 204us. */
+			(width == 3)) {
+			ar->ar_acksum++;
+			/*
+			 * Double the count for strong radars that match
+			 * one of the ACK signatures.
+			 */
+			if (rssi > DFS_AR_RSSI_DOUBLE_THRESHOLD) {
+				ar->ar_phyerrcount[width]++;
+				ar->ar_acksum++;
+			}
+			UPDATE_TOP_THREE_PEAKS(ar->ar_phyerrcount,
+				ar->ar_peaklist, width);
+			/* Sum the counts of these peaks. */
+			numpeaks = DFS_AR_MAX_NUM_PEAKS;
+			origregionsum = ar->ar_acksum;
+			for (i = 0; i < DFS_AR_MAX_NUM_PEAKS; i++) {
+				if (ar->ar_peaklist[i] > 0) {
+					if ((i == 0) &&
+						(ar->ar_peaklist[i] == 3) &&
+						(ar->ar_phyerrcount[3] <
+						 ar->ar_phyerrcount[2]) &&
+						(ar->ar_phyerrcount[3] > 6)) {
+						/*
+						 * If the top peak is one that
+						 * matches the 11Mbps long
+						 * preamble TCP Ack/1500-byte
+						 * data, include the count for
+						 * radars that hav emax duration
+						 * and high rssi (width = 2) to
+						 * boost the sum for the PAR
+						 * test that follows.
+						 */
+						sumpeak +=
+						    (ar->ar_phyerrcount[2] +
+						     ar->ar_phyerrcount[3]);
+						ar->ar_acksum +=
+						    (ar->ar_phyerrcount[2] +
+						     ar->ar_phyerrcount[3]);
+					} else {
+						sumpeak += ar->ar_phyerrcount[
+						    ar->ar_peaklist[i]];
+					}
+				} else
+					numpeaks--;
+			}
+			/*
+			 * If sum of patterns matches exceeds packet threshold,
+			 * perform comparison between peak-to-avg ratio against
+			 * parThreshold.
+			 */
+			if ((ar->ar_acksum > ar->ar_packetthreshold) &&
+				((sumpeak * DFS_AR_REGION_WIDTH) >
+				 (ar->ar_parthreshold * numpeaks *
+				  ar->ar_acksum))) {
+				/*
+				 * Neighboring traffic detected, get out of
+				 * Turbo.
+				 */
+				chan->dfs_ch_flagext |= CHANNEL_INTERFERENCE;
+				qdf_mem_zero(ar->ar_peaklist,
+						sizeof(ar->ar_peaklist));
+				ar->ar_acksum = 0;
+				qdf_mem_zero(ar->ar_phyerrcount,
+					sizeof(ar->ar_phyerrcount));
+			} else {
+				/*
+				 * Reset sum of matches to discount the count
+				 * of strong radars with max duration.
+				 */
+				ar->ar_acksum = origregionsum;
+			}
+		}
+		ar->ar_prevtimestamp = thistimestamp;
+		WLAN_ARQ_LOCK(dfs);
+		empty = STAILQ_EMPTY(&(dfs->dfs_arq));
+		WLAN_ARQ_UNLOCK(dfs);
+	}
+}
+
+void dfs_reset_ar(struct wlan_dfs *dfs)
+{
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+
+	qdf_mem_zero(&dfs->dfs_ar_state, sizeof(dfs->dfs_ar_state));
+	dfs->dfs_ar_state.ar_packetthreshold = DFS_AR_PKT_COUNT_THRESH;
+	dfs->dfs_ar_state.ar_parthreshold = DFS_AR_ACK_DETECT_PAR_THRESH;
+}
+
+void dfs_reset_arq(struct wlan_dfs *dfs)
+{
+	struct dfs_event *event;
+
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+
+	WLAN_ARQ_LOCK(dfs);
+	WLAN_DFSEVENTQ_LOCK(dfs);
+	while (!STAILQ_EMPTY(&(dfs->dfs_arq))) {
+		event = STAILQ_FIRST(&(dfs->dfs_arq));
+		STAILQ_REMOVE_HEAD(&(dfs->dfs_arq), re_list);
+		qdf_mem_zero(event, sizeof(struct dfs_event));
+		STAILQ_INSERT_TAIL(&(dfs->dfs_eventq), event, re_list);
+	}
+	WLAN_DFSEVENTQ_UNLOCK(dfs);
+	WLAN_ARQ_UNLOCK(dfs);
+}

+ 345 - 0
umac/dfs/core/src/misc/dfs_etsi_precac.c

@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * DOC: This file has ETSI CAC DFS functions.
+ * Summary: In ETSI domain, after CAC, channel is added to ETSI CAC cleared
+ * list, so that next time it can skip CAC if channel already present in this
+ * list. If radar found, remove channel from this list indicating to redo
+ * CAC,when channel is selected in future.
+ */
+
+#include "dfs_etsi_precac.h"
+#include "wlan_dfs_lmac_api.h"
+#include "wlan_dfs_mlme_api.h"
+#include "wlan_dfs_utils_api.h"
+#include "dfs_internal.h"
+#include "dfs_process_radar_found_ind.h"
+
+#define ETSI_CAC_TIME_OUT_MS 86400000
+int dfs_is_subchan_in_etsi_precac_done_list(struct wlan_dfs *dfs,
+					    uint8_t channel)
+{
+	struct dfs_etsi_precac_entry *precac_entry = NULL;
+	struct dfs_etsi_precac_entry *tmp_precac_entry = NULL;
+	uint8_t found = 0;
+	uint32_t diff_ms;
+
+	if (utils_is_dfs_ch(dfs->dfs_pdev_obj, channel)) {
+		/*For each channel in the list here do the below operation*/
+		PRECAC_LIST_LOCK(dfs);
+		if (TAILQ_EMPTY(&dfs->dfs_etsiprecac_done_list)) {
+			PRECAC_LIST_UNLOCK(dfs);
+			return 0;
+		}
+
+		TAILQ_FOREACH_SAFE(precac_entry, &dfs->dfs_etsiprecac_done_list,
+				   pe_list, tmp_precac_entry) {
+			diff_ms = qdf_system_ticks_to_msecs(qdf_system_ticks() -
+					precac_entry->etsi_caclst_ticks);
+			if (channel == precac_entry->ieee &&
+			    diff_ms < ETSI_CAC_TIME_OUT_MS) {
+				found = 1;
+				break;
+			}
+		}
+		PRECAC_LIST_UNLOCK(dfs);
+		if (found)
+			return 1;
+	} else {
+		return 1;
+	}
+
+	return 0;
+}
+
+bool dfs_is_etsi_precac_done(struct wlan_dfs *dfs)
+{
+	bool ret_val = 1;
+	uint8_t channels[NUM_CHANNELS_160MHZ];
+	uint8_t nchannels = 0;
+	int i;
+
+	nchannels = dfs_get_bonding_channels_without_seg_info(dfs->dfs_curchan,
+							      channels);
+
+	for (i = 0; i < nchannels; i++) {
+		if (dfs_is_subchan_in_etsi_precac_done_list(dfs, channels[i])) {
+			continue;
+		} else {
+			ret_val = 0;
+			break;
+		}
+	}
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "ret_val = %d", ret_val);
+
+	return ret_val;
+}
+
+void dfs_mark_etsi_precac_dfs(struct wlan_dfs *dfs, uint8_t *channels,
+			      uint8_t num_channels)
+{
+	struct dfs_etsi_precac_entry *precac_entry = NULL;
+	struct dfs_etsi_precac_entry *tmp_precac_entry = NULL;
+	int i = 0;
+
+	if (num_channels > NUM_CHANNELS_160MHZ) {
+		dfs_err(dfs, WLAN_DEBUG_DFS,
+			"Invalid num channels: %d", num_channels);
+		return;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+	for (i = 0; i < num_channels; i++) {
+		TAILQ_FOREACH_SAFE(precac_entry, &dfs->dfs_etsiprecac_done_list,
+				   pe_list, tmp_precac_entry) {
+			if (channels[i] != precac_entry->ieee)
+				continue;
+
+		TAILQ_REMOVE(&dfs->dfs_etsiprecac_done_list,
+			     precac_entry, pe_list);
+		TAILQ_INSERT_TAIL(&dfs->dfs_etsiprecac_required_list,
+				  precac_entry, pe_list);
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+void dfs_init_etsi_precac_list(struct wlan_dfs *dfs)
+{
+	u_int i;
+	uint8_t found;
+	struct dfs_etsi_precac_entry *tmp_precac_entry;
+	int nchans = 0;
+	struct dfs_channel *chan_list = NULL;
+
+	/*
+	 * We need to prepare list of unique VHT20 center frequencies.
+	 * But at the beginning we do not know how many unique frequencies
+	 * are present. Therefore, we calculate the MAX size and allocate
+	 * a temporary list/array. However we fill the temporary array with
+	 * unique frequencies and copy the unique list of frequencies to
+	 * the final list with exact size.
+	 */
+	TAILQ_INIT(&dfs->dfs_etsiprecac_done_list);
+	TAILQ_INIT(&dfs->dfs_etsiprecac_required_list);
+
+	nchans = dfs_get_num_chans();
+
+	chan_list = qdf_mem_malloc(nchans * sizeof(*chan_list));
+	if (!chan_list)
+		return;
+
+	utils_dfs_get_chan_list(dfs->dfs_pdev_obj, (void *)chan_list, &nchans);
+	if (!nchans) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "zero channels");
+		qdf_mem_free(chan_list);
+		return;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+	for (i = 0; i < nchans; i++) {
+		if (!WLAN_IS_CHAN_DFS(&chan_list[i]))
+			continue;
+
+		found = 0;
+		TAILQ_FOREACH(tmp_precac_entry,
+			      &dfs->dfs_etsiprecac_required_list,
+			      pe_list) {
+			if (tmp_precac_entry->ieee ==
+					chan_list[i].dfs_ch_ieee) {
+				found = 1;
+				break;
+			}
+		}
+		if (!found) {
+			struct dfs_etsi_precac_entry *etsi_precac_entry;
+
+			etsi_precac_entry = qdf_mem_malloc(
+					sizeof(*etsi_precac_entry));
+			if (!etsi_precac_entry) {
+				dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+					"etsi precac entry alloc fail");
+				continue;
+			}
+			etsi_precac_entry->ieee = chan_list[i].dfs_ch_ieee;
+			etsi_precac_entry->dfs = dfs;
+			TAILQ_INSERT_TAIL(&dfs->dfs_etsiprecac_required_list,
+					  etsi_precac_entry, pe_list);
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "Print the list of VHT20 frequencies from linked list");
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_etsiprecac_required_list,
+			pe_list)
+	dfs_info(dfs, WLAN_DEBUG_DFS, "ieee=%u", tmp_precac_entry->ieee);
+
+	qdf_mem_free(chan_list);
+}
+
+void dfs_deinit_etsi_precac_list(struct wlan_dfs *dfs)
+{
+	struct dfs_etsi_precac_entry *tmp_precac_entry, *precac_entry;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "Free list of HT20/VHT20 freq from etsiprecac_required list");
+	PRECAC_LIST_LOCK(dfs);
+
+	if (!TAILQ_EMPTY(&dfs->dfs_etsiprecac_required_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				   &dfs->dfs_etsiprecac_required_list,
+				pe_list,
+				tmp_precac_entry) {
+			TAILQ_REMOVE(&dfs->dfs_etsiprecac_required_list,
+				     precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "Free list of HT20/VHT20 freq from etsipcac_done list");
+
+	if (!TAILQ_EMPTY(&dfs->dfs_etsiprecac_done_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				   &dfs->dfs_etsiprecac_done_list,
+				pe_list,
+				tmp_precac_entry) {
+			TAILQ_REMOVE(&dfs->dfs_etsiprecac_done_list,
+				     precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+void dfs_etsi_precac_attach(struct wlan_dfs *dfs)
+{
+}
+
+void dfs_etsi_precac_detach(struct wlan_dfs *dfs)
+{
+	dfs_deinit_etsi_precac_list(dfs);
+}
+
+void dfs_print_etsi_precaclists(struct wlan_dfs *dfs)
+{
+	struct dfs_etsi_precac_entry *tmp_precac_entry;
+
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+	/* Print the ETSI Pre-CAC required List */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "Pre-cac-required list of VHT20 frequencies");
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_etsiprecac_required_list,
+			pe_list) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			 "ieee=%u ", tmp_precac_entry->ieee);
+	}
+
+	/* Print the ETSI Pre-CAC done List */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+		 "Pre-cac-done list of VHT20 frequencies");
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_etsiprecac_done_list,
+			pe_list) {
+		unsigned long time_added =
+			qdf_system_ticks_to_msecs(
+					tmp_precac_entry->etsi_caclst_ticks);
+
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			 "ieee=%u added at (msec): %lu", tmp_precac_entry->ieee,
+			 time_added);
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+void dfs_reset_etsiprecaclists(struct wlan_dfs *dfs)
+{
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		  "Reset precaclist of VHT80 frequencies");
+	dfs_deinit_etsi_precac_list(dfs);
+	dfs_init_etsi_precac_list(dfs);
+}
+
+void dfs_reset_etsi_precac_lists(struct wlan_dfs *dfs)
+{
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+	dfs_reset_etsiprecaclists(dfs);
+}
+
+int dfs_add_chan_to_etsi_done_list(struct wlan_dfs *dfs, uint8_t channel)
+{
+	struct dfs_etsi_precac_entry *precac_entry, *tmp_precac_entry;
+
+	PRECAC_LIST_LOCK(dfs);
+	TAILQ_FOREACH_SAFE(precac_entry,
+			   &dfs->dfs_etsiprecac_required_list,
+			   pe_list, tmp_precac_entry) {
+		if (channel == precac_entry->ieee) {
+			TAILQ_REMOVE(&dfs->dfs_etsiprecac_required_list,
+				     precac_entry, pe_list);
+			TAILQ_INSERT_TAIL(&dfs->dfs_etsiprecac_done_list,
+					  precac_entry,
+					  pe_list);
+			precac_entry->etsi_caclst_ticks = qdf_system_ticks();
+			PRECAC_LIST_UNLOCK(dfs);
+			return 1;
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	return 0;
+}
+
+void dfs_add_to_etsi_precac_done_list(struct wlan_dfs *dfs)
+{
+	uint8_t channels[NUM_CHANNELS_160MHZ];
+	uint8_t nchannels = 0;
+	int i = 0;
+
+	nchannels = dfs_get_bonding_channels_without_seg_info(dfs->dfs_curchan,
+							      channels);
+	for (i = 0; i < nchannels; i++) {
+		if (!utils_is_dfs_ch(dfs->dfs_pdev_obj, channels[i]))
+			continue;
+
+		if (TAILQ_EMPTY(&dfs->dfs_etsiprecac_required_list))
+			break;
+
+		dfs_add_chan_to_etsi_done_list(dfs, channels[i]);
+	}
+}

+ 1230 - 0
umac/dfs/core/src/misc/dfs_zero_cac.c

@@ -0,0 +1,1230 @@
+/*
+ * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * DOC: This file has ZERO CAC DFS functions.
+ * Abstract:- Operation in a DFS channel requires CAC that adds additional
+ * delay as well as loss of connection even when CSA is used. ETSI allows
+ * pre-CAC, i.e. performing CAC at a convenient time and using that channel
+ * later. Once Pre-CAC is done in a channel, it is no longer required to
+ * perform a CAC in the channel before TX/RX as long as radar is not found in
+ * it or we reset or restart the device.
+ *
+ * Design:-
+ * The pre-CAC is done in a RADIO that has  VHT80_80 capable radio where the
+ * primary and secondary HT80s can be programmed independently with two
+ * different HT80 channels. Three new lists are introduced to handle pre-CAC.
+ * The lists are:
+ * 1)Pre-CAC-required list
+ * 2)Pre-CAC-done list
+ * 3)Pre-CAC-NOL list
+ * At the beginning the Pre-CAC-required list is populated with the unique
+ * secondary HT80 frequencies of HT80_80 channels.  Whenever a HT80 channel
+ * change happens we convert the HT80 channel change to a HT80_80 channel and
+ * the secondary HT80 is set to the first element(HT80 frequency) from the
+ * Pre-CAC-required list.  Pre-CAC period is same as the CAC period. After
+ * the pre-CAC period is over the pre-CAC timer expires and the HT80 frequency
+ * (the pre-CAC-required list element) is removed from the Pre-CAC-required
+ * list and inserted into the Pre-CAC-done list. While Pre-CAC timer is running
+ * if there is any RADAR detect then the current HT80 frequency is removed from
+ * the Pre-CAC-required list and inserted into the Pre-CAC-NOL list. Each
+ * element of pre-CAC-NOL times out individually after 30 minutes of its
+ * insertion. Pre-CAC-NOL timeout is just like the regular NOL timeout. Upon
+ * Pre-CAC-NOL expiry of an element (HT80 frequency), the element is removed
+ * from the Pre-CAC-NOL list and inserted into the Pre-CAC-required list.
+ * At any point of time if there is a channel change and the new channel is
+ * DFS, the Pre-CAC-done list is consulted to check if pre-CAC has been
+ * completed for the entire bandwidth of the new channel. If Pre-CAC has
+ * already been done  for the entire bandwidth of the channel then regular CAC
+ * can be skipped(this is what is known as Zero wait DFS) if we are in ETSI
+ * domain.
+ *
+ * New RadarTool commands:-
+ * 1)radartool -i wifi[X] secondSegmentBangradar
+ * It simulates RADAR from the secondary HT80 when the
+ * secondary HT80 is doing pre-CAC. If secondary is not
+ * doing any pre-CAC then this command has no effect.
+ * 2)radartool -i wifi[X] showPreCACLists
+ * It shows all 3 pre-CAC Lists' contents.
+ *
+ * New iwpriv commands:-
+ * 1)iwpriv wifi[X] preCACEn 0/1
+ * This command enables/disables the zero-cac-DFS.
+ * 2)iwpriv wifi[X] pCACTimeout <timeout>
+ * Override the pCACTimeout.
+ *
+ * FAQ(Frequently Asked Questions):-
+ * 1)
+ * Question)We already have NOL list. Why do we need separate pre-CAC-NOL
+ * list?
+ * Answer) pre-CAC is done on an HT80 channel and  the same HT80 channel is
+ * inserted into pre-CAC-NOL list after pre-CAC radar detection. NOL list
+ * contains HT20 channels. Since after pre-CAC-NOL expiry we need
+ * to move the HT80 channel from pre-CAC-NOL list  to pre-CAC-required list
+ * it is very easy to remove the HT80 channel and insert it. Having
+ * a separate pre-CAC-NOL also provides some separation from the existing
+ * code and helps modularize.
+ */
+
+#include "dfs_zero_cac.h"
+#include "wlan_dfs_lmac_api.h"
+#include "wlan_dfs_mlme_api.h"
+#include "wlan_dfs_utils_api.h"
+#include "dfs_internal.h"
+#include "dfs_etsi_precac.h"
+
+void dfs_zero_cac_reset(struct wlan_dfs *dfs)
+{
+	struct dfs_precac_entry *tmp_precac_entry, *precac_entry;
+
+	dfs_get_override_precac_timeout(dfs,
+			&(dfs->dfs_precac_timeout_override));
+	qdf_timer_sync_cancel(&dfs->dfs_precac_timer);
+	dfs->dfs_precac_primary_freq = 0;
+	dfs->dfs_precac_secondary_freq = 0;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_nol_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_nol_list,
+				pe_list,
+				tmp_precac_entry) {
+			qdf_timer_free(&precac_entry->precac_nol_timer);
+			TAILQ_REMOVE(&dfs->dfs_precac_required_list,
+				     precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+void dfs_zero_cac_timer_detach(struct wlan_dfs *dfs)
+{
+	qdf_timer_free(&dfs->dfs_precac_timer);
+}
+
+int dfs_override_precac_timeout(struct wlan_dfs *dfs, int precac_timeout)
+{
+	if (!dfs)
+		return -EIO;
+
+	dfs->dfs_precac_timeout_override = precac_timeout;
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "PreCAC timeout is now %s (%d)",
+		(precac_timeout == -1) ? "default" : "overridden",
+		precac_timeout);
+
+	return 0;
+}
+
+int dfs_get_override_precac_timeout(struct wlan_dfs *dfs, int *precac_timeout)
+{
+	if (!dfs)
+		return -EIO;
+
+	(*precac_timeout) = dfs->dfs_precac_timeout_override;
+
+	return 0;
+}
+
+#define VHT80_OFFSET 6
+#define IS_WITHIN_RANGE(_A, _B, _C)  \
+	(((_A) >= ((_B)-(_C))) && ((_A) <= ((_B)+(_C))))
+
+bool dfs_is_ht20_40_80_chan_in_precac_done_list(struct wlan_dfs *dfs,
+						struct dfs_channel *chan)
+{
+	struct dfs_precac_entry *precac_entry;
+	bool ret_val = 0;
+
+	/*
+	 * A is within B-C and B+C
+	 * (B-C) <= A <= (B+C)
+	 */
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_done_list))
+		TAILQ_FOREACH(precac_entry,
+				&dfs->dfs_precac_done_list,
+				pe_list) {
+			/* Find if the VHT80 freq1 is in Pre-CAC done list */
+			if (IS_WITHIN_RANGE(chan->dfs_ch_ieee,
+					    precac_entry->vht80_freq,
+					    VHT80_OFFSET)) {
+				ret_val = 1;
+				break;
+			}
+		}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "vht80_freq = %u ret_val = %d",
+		 dfs->dfs_curchan->dfs_ch_ieee, ret_val);
+
+	return ret_val;
+}
+
+bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs,
+					     struct dfs_channel *chan)
+{
+	struct dfs_precac_entry *precac_entry;
+	bool ret_val = 0;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_done_list)) {
+		bool primary_found = 0;
+		/* Check if primary is DFS then search */
+		if (WLAN_IS_CHAN_DFS(chan)) {
+			TAILQ_FOREACH(precac_entry,
+					&dfs->dfs_precac_done_list,
+					pe_list) {
+				if (chan->dfs_ch_vhtop_ch_freq_seg1
+						== precac_entry->vht80_freq) {
+					primary_found = 1;
+					break;
+				}
+			}
+		} else {
+			primary_found = 1;
+		}
+
+		/* Check if secondary DFS then search */
+		if (WLAN_IS_CHAN_DFS_CFREQ2(chan) &&
+		    primary_found) {
+			TAILQ_FOREACH(precac_entry,
+					&dfs->dfs_precac_done_list,
+					pe_list) {
+				if (chan->dfs_ch_vhtop_ch_freq_seg2
+						== precac_entry->vht80_freq) {
+					/* Now secondary also found */
+					ret_val = 1;
+					break;
+				}
+			}
+		} else {
+			if (primary_found)
+				ret_val = 1;
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"freq_seg1 = %u freq_seq2 = %u ret_val = %d",
+		chan->dfs_ch_vhtop_ch_freq_seg1,
+		chan->dfs_ch_vhtop_ch_freq_seg2,
+		ret_val);
+
+	return ret_val;
+}
+
+bool dfs_is_precac_done(struct wlan_dfs *dfs, struct dfs_channel *chan)
+{
+	bool ret_val = 0;
+
+	if (WLAN_IS_CHAN_11AC_VHT20(chan) ||
+	    WLAN_IS_CHAN_11AC_VHT40(chan) ||
+	    WLAN_IS_CHAN_11AC_VHT80(chan)) {
+		ret_val = dfs_is_ht20_40_80_chan_in_precac_done_list(dfs, chan);
+	} else if (WLAN_IS_CHAN_11AC_VHT80_80(chan) ||
+		   WLAN_IS_CHAN_11AC_VHT160(chan)) {
+		ret_val = dfs_is_ht80_80_chan_in_precac_done_list(dfs, chan);
+	}
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "ret_val = %d", ret_val);
+
+	return ret_val;
+}
+
+#define VHT80_IEEE_FREQ_OFFSET 6
+
+void dfs_mark_precac_dfs(struct wlan_dfs *dfs,
+		uint8_t is_radar_found_on_secondary_seg)
+{
+	struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL;
+	uint8_t found = 0;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"is_radar_found_on_secondary_seg = %u secondary_freq = %u primary_freq = %u",
+		is_radar_found_on_secondary_seg,
+		dfs->dfs_precac_secondary_freq,
+		dfs->dfs_precac_primary_freq);
+
+	/*
+	 * Even if radar found on primary, we need to move the channel from
+	 * precac-required-list and precac-done-list to precac-nol-list.
+	 */
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_required_list)) {
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_required_list,
+				pe_list,
+				tmp_precac_entry) {
+			/*
+			 * If on primary then use IS_WITHIN_RANGE else use
+			 * equality directly.
+			 */
+			if (is_radar_found_on_secondary_seg ?
+				(dfs->dfs_precac_secondary_freq ==
+				 precac_entry->vht80_freq) : IS_WITHIN_RANGE(
+				     dfs->dfs_curchan->dfs_ch_ieee,
+				     precac_entry->vht80_freq,
+				     VHT80_IEEE_FREQ_OFFSET)) {
+				TAILQ_REMOVE(&dfs->dfs_precac_required_list,
+						precac_entry, pe_list);
+
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					"removing the freq = %u from required list and adding to NOL list",
+					precac_entry->vht80_freq);
+				TAILQ_INSERT_TAIL(&dfs->dfs_precac_nol_list,
+						precac_entry, pe_list);
+				qdf_timer_mod(&precac_entry->precac_nol_timer,
+						dfs_get_nol_timeout(dfs)*1000);
+				found = 1;
+				break;
+			}
+		}
+	}
+
+	/* If not found in precac-required-list remove from precac-done-list */
+	if (!found && !TAILQ_EMPTY(&dfs->dfs_precac_done_list)) {
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_done_list,
+				pe_list,
+				tmp_precac_entry) {
+			/*
+			 * If on primary then use IS_WITHIN_RANGE else use
+			 * equality directly.
+			 */
+			if (is_radar_found_on_secondary_seg ?
+					(dfs->dfs_precac_secondary_freq ==
+					 precac_entry->vht80_freq) :
+					IS_WITHIN_RANGE(
+						dfs->dfs_curchan->dfs_ch_ieee,
+						precac_entry->vht80_freq, 6)) {
+				TAILQ_REMOVE(&dfs->dfs_precac_done_list,
+					precac_entry, pe_list);
+
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					"removing the the freq = %u from done list and adding to NOL list",
+					precac_entry->vht80_freq);
+				TAILQ_INSERT_TAIL(&dfs->dfs_precac_nol_list,
+						precac_entry, pe_list);
+				qdf_timer_mod(&precac_entry->precac_nol_timer,
+						dfs_get_nol_timeout(dfs)*1000);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	/* TO BE DONE  xxx:- Need to lock the channel change */
+	/*
+	 * If radar Found on Primary no need to do restart VAP's channels since
+	 * channel change will happen after RANDOM channel selection anyway.
+	 */
+
+	if (dfs->dfs_precac_timer_running) {
+		/* Cancel the PreCAC timer */
+		qdf_timer_stop(&dfs->dfs_precac_timer);
+		dfs->dfs_precac_timer_running = 0;
+
+		/*
+		 * Change the channel
+		 * case 1:-  No  VHT80 channel for precac is available so bring
+		 * it back to VHT80
+		 * case 2:-  pick a new VHT80 channel for precac
+		 */
+		if (is_radar_found_on_secondary_seg) {
+			if (dfs_is_ap_cac_timer_running(dfs)) {
+				dfs->dfs_defer_precac_channel_change = 1;
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					"Primary CAC is running, defer the channel change"
+					);
+			} else {
+				dfs_mlme_channel_change_by_precac(
+						dfs->dfs_pdev_obj);
+			}
+		}
+	}
+}
+
+bool dfs_is_precac_timer_running(struct wlan_dfs *dfs)
+{
+	return dfs->dfs_precac_timer_running ? true : false;
+}
+
+#define VHT80_IEEE_FREQ_OFFSET 6
+void dfs_find_precac_secondary_vht80_chan(struct wlan_dfs *dfs,
+		struct dfs_channel *chan)
+{
+	uint8_t first_primary_dfs_ch_ieee;
+
+	first_primary_dfs_ch_ieee =
+		dfs->dfs_precac_secondary_freq - VHT80_IEEE_FREQ_OFFSET;
+
+	dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+			first_primary_dfs_ch_ieee, 0,
+			WLAN_PHYMODE_11AC_VHT80,
+			&(chan->dfs_ch_freq),
+			&(chan->dfs_ch_flags),
+			&(chan->dfs_ch_flagext),
+			&(chan->dfs_ch_ieee),
+			&(chan->dfs_ch_vhtop_ch_freq_seg1),
+			&(chan->dfs_ch_vhtop_ch_freq_seg2));
+}
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+void dfs_precac_csa(struct wlan_dfs *dfs)
+{
+	uint32_t ieeechan = dfs->dfs_precac_preferred_chan;
+
+	/*
+	 * Save current chan into intermediate chan, so that
+	 * next time a DFS channel needs preCAC, there is no channel switch
+	 * until preCAC finishes.
+	 */
+	dfs->dfs_precac_inter_chan = dfs->dfs_precac_preferred_chan;
+
+	/* reset once the channel is used */
+	dfs->dfs_precac_preferred_chan = 0;
+
+	if (NULL != global_dfs_to_mlme.mlme_precac_chan_change_csa)
+		global_dfs_to_mlme.mlme_precac_chan_change_csa(
+						dfs->dfs_pdev_obj,
+						ieeechan);
+}
+#endif
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+/**
+ * dfs_precac_check_home_chan_change() - Change the home channel
+ *                                       after precac is done.
+ *
+ * @dfs: Pointer to dfs handler.
+ *
+ * If precac is done on the home channel, then change the channel from
+ * intermediate channel to home channel and continue precac on other channels.
+ *
+ * Return: Void.
+ */
+static void dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
+{
+	struct dfs_channel chan;
+	bool is_home_chan_changed = false;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (dfs->dfs_precac_preferred_chan) {
+		if (QDF_STATUS_SUCCESS !=
+		    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+						dfs->dfs_precac_preferred_chan,
+						0, WLAN_PHYMODE_11AC_VHT80,
+						&chan.dfs_ch_freq,
+						&chan.dfs_ch_flags,
+						&chan.dfs_ch_flagext,
+						&chan.dfs_ch_ieee,
+						&chan.dfs_ch_vhtop_ch_freq_seg1,
+						&chan.dfs_ch_vhtop_ch_freq_seg2)) {
+			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+				"Invalid preferred channel %d",
+				dfs->dfs_precac_preferred_chan);
+			return;
+		}
+		is_home_chan_changed = dfs_is_precac_done(dfs, &chan);
+	}
+	/*
+	 * Change home channel using CSA
+	 * and continue to next preCAC channel.
+	 */
+	if (is_home_chan_changed) {
+		dfs_precac_csa(dfs);
+	} else {
+		/*
+		 * Use same home channel, only change preCAC channel.
+		 */
+
+		/*
+		 * TO BE DONE xxx : Need to lock the channel change.
+		 */
+		dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
+	}
+}
+#else
+static inline void dfs_precac_check_home_chan_change(struct wlan_dfs *dfs)
+{
+	/*
+	 * Use same home channel, only change preCAC channel.
+	 */
+
+	/*
+	 * TO BE DONE xxx : Need to lock the channel change.
+	 */
+	dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
+}
+#endif
+
+/**
+ * dfs_precac_timeout() - Precac timeout.
+ *
+ * Removes the channel from precac_required list and adds it to the
+ * precac_done_list. Triggers a precac channel change.
+ */
+static os_timer_func(dfs_precac_timeout)
+{
+	struct dfs_precac_entry *precac_entry, *tmp_precac_entry;
+	struct wlan_dfs *dfs = NULL;
+
+	OS_GET_TIMER_ARG(dfs, struct wlan_dfs *);
+	dfs->dfs_precac_timer_running = 0;
+
+	/*
+	 * Remove the HVT80 freq from the precac-required-list and add it to the
+	 * precac-done-list
+	 */
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_required_list)) {
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_required_list,
+				pe_list,
+				tmp_precac_entry) {
+			if (dfs->dfs_precac_secondary_freq ==
+					precac_entry->vht80_freq) {
+				TAILQ_REMOVE(&dfs->dfs_precac_required_list,
+						precac_entry, pe_list);
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					"removing the the freq = %u from required list and adding to done list",
+					precac_entry->vht80_freq);
+				TAILQ_INSERT_TAIL(&dfs->dfs_precac_done_list,
+						precac_entry, pe_list);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Pre-cac expired, Precac Secondary chan %u curr time %d",
+		dfs->dfs_precac_secondary_freq,
+		(qdf_system_ticks_to_msecs(qdf_system_ticks()) / 1000));
+	/*
+	 * Do vdev restart so that we can change the secondary VHT80 channel.
+	 */
+	dfs_precac_check_home_chan_change(dfs);
+}
+
+void dfs_zero_cac_timer_init(struct wlan_dfs *dfs)
+{
+	qdf_timer_init(NULL,
+			&(dfs->dfs_precac_timer),
+			dfs_precac_timeout,
+			(void *) dfs,
+			QDF_TIMER_TYPE_WAKE_APPS);
+}
+
+void dfs_zero_cac_attach(struct wlan_dfs *dfs)
+{
+	dfs->dfs_precac_timeout_override = -1;
+	dfs_zero_cac_timer_init(dfs);
+	PRECAC_LIST_LOCK_CREATE(dfs);
+}
+
+/**
+ * dfs_precac_nol_timeout() - NOL timeout for precac channel.
+ *
+ * Removes the VHT80 channel from precac nol list and adds it to precac required
+ * list.
+ */
+static os_timer_func(dfs_precac_nol_timeout)
+{
+	struct dfs_precac_entry *precac_entry;
+	struct wlan_dfs *dfs = NULL;
+
+	OS_GET_TIMER_ARG(precac_entry, struct dfs_precac_entry *);
+	dfs = (struct wlan_dfs *)precac_entry->dfs;
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_nol_list)) {
+		/* Move the channel from precac-NOL to precac-required-list */
+		TAILQ_REMOVE(&dfs->dfs_precac_nol_list, precac_entry, pe_list);
+		dfs_debug(dfs, WLAN_DEBUG_DFS,
+			"removing the the freq = %u from PreCAC NOL-list and adding Precac-required list",
+			 precac_entry->vht80_freq);
+		TAILQ_INSERT_TAIL(&dfs->dfs_precac_required_list, precac_entry,
+				pe_list);
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	/* TO BE DONE xxx : Need to lock the channel change */
+	/* Do a channel change */
+	dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
+}
+
+void dfs_init_precac_list(struct wlan_dfs *dfs)
+{
+	u_int i;
+	uint8_t found;
+	struct dfs_precac_entry *tmp_precac_entry;
+	int nchans = 0;
+
+	/*
+	 * We need to prepare list of uniq VHT80 center frequencies. But at the
+	 * beginning we do not know how many uniq frequencies are present.
+	 * Therefore, we calculate the MAX size and allocate a temporary
+	 * list/array. However we fill the temporary array with uniq frequencies
+	 * and copy the uniq list of frequencies to the final list with exact
+	 * size.
+	 */
+	TAILQ_INIT(&dfs->dfs_precac_required_list);
+	TAILQ_INIT(&dfs->dfs_precac_done_list);
+	TAILQ_INIT(&dfs->dfs_precac_nol_list);
+	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans);
+
+	PRECAC_LIST_LOCK(dfs);
+	/* Fill the  precac-required-list with unique elements */
+	for (i = 0; i < nchans; i++) {
+		struct dfs_channel *ichan = NULL, lc;
+
+		ichan = &lc;
+		dfs_mlme_get_dfs_ch_channels(dfs->dfs_pdev_obj,
+				&(ichan->dfs_ch_freq),
+				&(ichan->dfs_ch_flags),
+				&(ichan->dfs_ch_flagext),
+				&(ichan->dfs_ch_ieee),
+				&(ichan->dfs_ch_vhtop_ch_freq_seg1),
+				&(ichan->dfs_ch_vhtop_ch_freq_seg2),
+				i);
+
+		if (WLAN_IS_CHAN_11AC_VHT80(ichan) &&
+				WLAN_IS_CHAN_DFS(ichan)) {
+			found = 0;
+			TAILQ_FOREACH(tmp_precac_entry,
+					&dfs->dfs_precac_required_list,
+					pe_list) {
+				if (tmp_precac_entry->vht80_freq ==
+						ichan->
+						dfs_ch_vhtop_ch_freq_seg1) {
+					found = 1;
+					break;
+				}
+			}
+			if (!found) {
+				struct dfs_precac_entry *precac_entry;
+
+				precac_entry = qdf_mem_malloc(
+					sizeof(*precac_entry));
+				if (!precac_entry) {
+					dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+						"entry alloc fail for : %d", i);
+					continue;
+				}
+				precac_entry->vht80_freq =
+					ichan->dfs_ch_vhtop_ch_freq_seg1;
+				precac_entry->dfs = dfs;
+
+				/*
+				 * Initialize per entry timer. Shall be used
+				 * when the entry moves to precac_nol_list.
+				 */
+				qdf_timer_init(NULL,
+					&(precac_entry->precac_nol_timer),
+					dfs_precac_nol_timeout,
+					(void *) (precac_entry),
+					QDF_TIMER_TYPE_WAKE_APPS);
+				TAILQ_INSERT_TAIL(
+						&dfs->dfs_precac_required_list,
+						precac_entry, pe_list);
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Print the list of VHT80 frequencies from linked list");
+	TAILQ_FOREACH(tmp_precac_entry,
+			&dfs->dfs_precac_required_list,
+			pe_list)
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "freq=%u",
+				tmp_precac_entry->vht80_freq);
+}
+
+void dfs_deinit_precac_list(struct wlan_dfs *dfs)
+{
+	struct dfs_precac_entry *tmp_precac_entry, *precac_entry;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Free the list of VHT80 frequencies from linked list(precac_required)"
+		);
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_required_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_required_list,
+				pe_list, tmp_precac_entry) {
+			TAILQ_REMOVE(&dfs->dfs_precac_required_list,
+					precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Free the list of VHT80 frequencies from linked list(precac_done)"
+		);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_done_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_done_list,
+				pe_list, tmp_precac_entry) {
+			TAILQ_REMOVE(&dfs->dfs_precac_done_list,
+					precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Free the list of VHT80 frequencies from linked list(precac_nol)"
+		);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_nol_list))
+		TAILQ_FOREACH_SAFE(precac_entry,
+				&dfs->dfs_precac_nol_list,
+				pe_list,
+				tmp_precac_entry) {
+			qdf_timer_stop(&precac_entry->precac_nol_timer);
+			TAILQ_REMOVE(&dfs->dfs_precac_nol_list,
+					precac_entry, pe_list);
+			qdf_mem_free(precac_entry);
+		}
+	PRECAC_LIST_UNLOCK(dfs);
+
+}
+
+void dfs_zero_cac_detach(struct wlan_dfs *dfs)
+{
+	dfs_deinit_precac_list(dfs);
+	PRECAC_LIST_LOCK_DESTROY(dfs);
+}
+
+uint8_t dfs_get_freq_from_precac_required_list(struct wlan_dfs *dfs,
+		uint8_t exclude_ieee_freq)
+{
+	struct dfs_precac_entry *precac_entry;
+	uint8_t ieee_freq = 0;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "exclude_ieee_freq = %u",
+		 exclude_ieee_freq);
+
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_required_list)) {
+		TAILQ_FOREACH(precac_entry, &dfs->dfs_precac_required_list,
+				pe_list) {
+			if (precac_entry->vht80_freq != exclude_ieee_freq) {
+				ieee_freq = precac_entry->vht80_freq;
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+	dfs_debug(dfs, WLAN_DEBUG_DFS, "ieee_freq = %u", ieee_freq);
+
+	return ieee_freq;
+}
+
+void dfs_cancel_precac_timer(struct wlan_dfs *dfs)
+{
+	qdf_timer_stop(&dfs->dfs_precac_timer);
+	dfs->dfs_precac_timer_running = 0;
+}
+
+void dfs_start_precac_timer(struct wlan_dfs *dfs, uint8_t precac_chan)
+{
+	struct dfs_channel *ichan, lc;
+	uint8_t first_primary_dfs_ch_ieee;
+	int primary_cac_timeout;
+	int secondary_cac_timeout;
+	int precac_timeout;
+
+#define EXTRA_TIME_IN_SEC 5
+	dfs->dfs_precac_timer_running = 1;
+
+	/*
+	 * Get the first primary ieee chan in the HT80 band and find the channel
+	 * pointer.
+	 */
+	first_primary_dfs_ch_ieee = precac_chan - VHT80_IEEE_FREQ_OFFSET;
+
+	primary_cac_timeout = dfs_mlme_get_cac_timeout(dfs->dfs_pdev_obj,
+			dfs->dfs_curchan->dfs_ch_freq,
+			dfs->dfs_curchan->dfs_ch_vhtop_ch_freq_seg2,
+			dfs->dfs_curchan->dfs_ch_flags);
+
+	ichan = &lc;
+	dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+			first_primary_dfs_ch_ieee, 0,
+			WLAN_PHYMODE_11AC_VHT80,
+			&(ichan->dfs_ch_freq),
+			&(ichan->dfs_ch_flags),
+			&(ichan->dfs_ch_flagext),
+			&(ichan->dfs_ch_ieee),
+			&(ichan->dfs_ch_vhtop_ch_freq_seg1),
+			&(ichan->dfs_ch_vhtop_ch_freq_seg2));
+
+	secondary_cac_timeout = (dfs->dfs_precac_timeout_override != -1) ?
+		dfs->dfs_precac_timeout_override :
+		dfs_mlme_get_cac_timeout(dfs->dfs_pdev_obj,
+				ichan->dfs_ch_freq,
+				ichan->dfs_ch_vhtop_ch_freq_seg2,
+				ichan->dfs_ch_flags);
+
+	/*
+	 * EXTRA time is needed so that if CAC and PreCAC is running
+	 * simultaneously, PreCAC expiry function may be called before CAC
+	 * expiry and PreCAC expiry does a channel change (vdev_restart) the
+	 * restart response calls CAC_start function(ieee80211_dfs_cac_start)
+	 * which cancels any previous CAC timer and starts a new CAC again.
+	 * So CAC expiry does not happen and moreover a new CAC is started.
+	 * Therefore do not disturb the CAC by channel restart (vdev_restart).
+	 *
+	 * If CAC/preCAC was already completed on primary, then we do not need
+	 * to calculate which CAC timeout is maximum.
+	 * For example: If primary's CAC is 600 seconds and secondary's CAC
+	 * is 60 seconds then maximum gives 600 seconds which is not needed
+	 * if CAC/preCAC was already completed on primary. It is to be noted
+	 * that etsi_precac/cac is done on primary segment.
+	 */
+	if (WLAN_IS_CHAN_DFS(dfs->dfs_curchan) &&
+	    !dfs_is_etsi_precac_done(dfs) &&
+	    !dfs_is_precac_done(dfs, dfs->dfs_curchan))
+		precac_timeout = QDF_MAX(primary_cac_timeout,
+					 secondary_cac_timeout) +
+				 EXTRA_TIME_IN_SEC;
+	else
+		precac_timeout = secondary_cac_timeout + EXTRA_TIME_IN_SEC;
+
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"precactimeout = %d", (precac_timeout)*1000);
+	qdf_timer_mod(&dfs->dfs_precac_timer, (precac_timeout) * 1000);
+}
+
+void dfs_print_precaclists(struct wlan_dfs *dfs)
+{
+	struct dfs_precac_entry *tmp_precac_entry;
+
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+
+	/* Print the Pre-CAC required List */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Pre-cac-required list of VHT80 frequencies");
+	TAILQ_FOREACH(tmp_precac_entry,
+			&dfs->dfs_precac_required_list,
+			pe_list) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+				"freq=%u", tmp_precac_entry->vht80_freq);
+	}
+
+	/* Print the Pre-CAC done List */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Pre-cac-done list of VHT80 frequencies");
+	TAILQ_FOREACH(tmp_precac_entry,
+			&dfs->dfs_precac_done_list,
+			pe_list) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "freq=%u",
+			 tmp_precac_entry->vht80_freq);
+	}
+
+	/* Print the Pre-CAC NOL List */
+	dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Pre-cac-NOL list of VHT80 frequencies");
+	TAILQ_FOREACH(tmp_precac_entry,
+			&dfs->dfs_precac_nol_list,
+			pe_list) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+				"freq=%u", tmp_precac_entry->vht80_freq);
+	}
+
+	PRECAC_LIST_UNLOCK(dfs);
+}
+
+void dfs_reset_precaclists(struct wlan_dfs *dfs)
+{
+	dfs_debug(dfs, WLAN_DEBUG_DFS,
+		"Reset precaclist of VHT80 frequencies");
+	dfs_deinit_precac_list(dfs);
+	dfs_init_precac_list(dfs);
+}
+
+void dfs_reset_precac_lists(struct wlan_dfs *dfs)
+{
+	if (!dfs) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
+		return;
+	}
+	dfs_reset_precaclists(dfs);
+}
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+void dfs_set_precac_preferred_channel(struct wlan_dfs *dfs,
+				      struct dfs_channel *chan)
+{
+	bool found = false;
+	struct dfs_precac_entry *precac_entry;
+
+	dfs->dfs_precac_preferred_chan = chan->dfs_ch_ieee;
+	if (dfs_is_precac_timer_running(dfs) &&
+	    (dfs->dfs_precac_secondary_freq == chan->dfs_ch_freq)) {
+		return;
+	}
+
+	/* Remove and insert into head, so that the user configured channel
+	 * is picked first for preCAC.
+	 */
+	PRECAC_LIST_LOCK(dfs);
+	if (!TAILQ_EMPTY(&dfs->dfs_precac_required_list)) {
+		TAILQ_FOREACH(precac_entry,
+			      &dfs->dfs_precac_required_list, pe_list) {
+			if (precac_entry->vht80_freq ==
+			    chan->dfs_ch_vhtop_ch_freq_seg1) {
+				found = true;
+				TAILQ_REMOVE(&dfs->dfs_precac_required_list,
+					     precac_entry, pe_list);
+				TAILQ_INSERT_HEAD(&dfs->dfs_precac_required_list,
+						  precac_entry, pe_list);
+				break;
+			}
+		}
+	}
+	PRECAC_LIST_UNLOCK(dfs);
+
+	if (!found) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"frequency %d not found in precac list",
+			chan->dfs_ch_vhtop_ch_freq_seg1);
+		return;
+	}
+}
+
+void dfs_decide_precac_preferred_chan(struct wlan_dfs *dfs, uint8_t *pref_chan)
+{
+	struct dfs_channel chan;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+					*pref_chan, 0,
+					WLAN_PHYMODE_11AC_VHT80,
+					&chan.dfs_ch_freq,
+					&chan.dfs_ch_flags,
+					&chan.dfs_ch_flagext,
+					&chan.dfs_ch_ieee,
+					&chan.dfs_ch_vhtop_ch_freq_seg1,
+					&chan.dfs_ch_vhtop_ch_freq_seg2))
+		return;
+	if (!dfs->dfs_precac_inter_chan)
+		return;
+
+	/*
+	 * If precac is done on this channel use it, else use a intermediate
+	 * non-DFS channel and trigger a precac on this channel.
+	 */
+	if (WLAN_IS_CHAN_DFS(&chan) && !dfs_is_precac_done(dfs, &chan)) {
+		dfs_set_precac_preferred_channel(dfs, &chan);
+		*pref_chan = dfs->dfs_precac_inter_chan;
+
+	} else {
+		dfs->dfs_precac_inter_chan = chan.dfs_ch_ieee;
+	}
+}
+
+enum precac_chan_state
+dfs_get_precac_chan_state(struct wlan_dfs *dfs, uint8_t precac_chan)
+{
+	struct dfs_channel chan;
+	struct dfs_precac_entry *tmp_precac_entry;
+	enum precac_chan_state ret = PRECAC_ERR;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+					precac_chan, 0,
+					WLAN_PHYMODE_11AC_VHT80,
+					&chan.dfs_ch_freq,
+					&chan.dfs_ch_flags,
+					&chan.dfs_ch_flagext,
+					&chan.dfs_ch_ieee,
+					&chan.dfs_ch_vhtop_ch_freq_seg1,
+					&chan.dfs_ch_vhtop_ch_freq_seg2)) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Intermediate channel not found");
+		return PRECAC_ERR;
+	}
+
+	if (!WLAN_IS_CHAN_DFS(&chan)) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"[%d] Not a DFS channel", precac_chan);
+		return PRECAC_ERR;
+	}
+
+	PRECAC_LIST_LOCK(dfs);
+	if (dfs_is_precac_timer_running(dfs)) {
+		tmp_precac_entry = TAILQ_FIRST(&dfs->dfs_precac_required_list);
+		if (tmp_precac_entry && (tmp_precac_entry->vht80_freq ==
+			chan.dfs_ch_vhtop_ch_freq_seg1)) {
+			ret = PRECAC_NOW;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_required_list, pe_list) {
+		if (tmp_precac_entry->vht80_freq ==
+		    chan.dfs_ch_vhtop_ch_freq_seg1) {
+			ret = PRECAC_REQUIRED;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_done_list, pe_list) {
+		if (tmp_precac_entry->vht80_freq ==
+		    chan.dfs_ch_vhtop_ch_freq_seg1) {
+			ret = PRECAC_DONE;
+			goto end;
+		}
+	}
+
+	TAILQ_FOREACH(tmp_precac_entry,
+		      &dfs->dfs_precac_nol_list, pe_list) {
+		if (tmp_precac_entry->vht80_freq ==
+		    chan.dfs_ch_vhtop_ch_freq_seg1) {
+			ret = PRECAC_NOL;
+			goto end;
+		}
+	}
+
+end:
+	PRECAC_LIST_UNLOCK(dfs);
+	return ret;
+}
+#endif
+
+void dfs_find_vht80_chan_for_precac(struct wlan_dfs *dfs,
+		uint32_t chan_mode,
+		uint8_t ch_freq_seg1,
+		uint32_t *cfreq1,
+		uint32_t *cfreq2,
+		uint32_t *phy_mode,
+		bool *dfs_set_cfreq2,
+		bool *set_agile)
+{
+	if (chan_mode == WLAN_PHYMODE_11AC_VHT80) {
+		uint8_t ieee_freq;
+
+		dfs_debug(dfs, WLAN_DEBUG_DFS,
+			"precac_secondary_freq = %u precac_running = %u",
+			dfs->dfs_precac_secondary_freq,
+			dfs->dfs_precac_timer_running);
+
+		/*
+		 * If Pre-CAC is enabled then find a center frequency for
+		 * the secondary VHT80 and Change the mode to
+		 * VHT80_80 or VHT160.
+		 */
+		if (dfs->dfs_precac_enable) {
+			/*
+			 * If precac timer is running then do not change the
+			 * secondary channel use the old secondary VHT80
+			 * channel. If precac timer is not running then try to
+			 * find a new channel from precac-required-list.
+			 */
+			if (dfs->dfs_precac_timer_running) {
+				/*
+				 * Primary and secondary VHT80 cannot be the
+				 * same. Therefore exclude the primary
+				 * frequency while getting new channel from
+				 * precac-required-list.
+				 */
+				if (ch_freq_seg1 ==
+						dfs->dfs_precac_secondary_freq)
+					ieee_freq =
+					dfs_get_freq_from_precac_required_list(
+							dfs,
+							ch_freq_seg1);
+				else
+					ieee_freq =
+						dfs->dfs_precac_secondary_freq;
+			} else
+				ieee_freq =
+					dfs_get_freq_from_precac_required_list(
+							dfs, ch_freq_seg1);
+
+			if (ieee_freq) {
+				if (ieee_freq == (ch_freq_seg1 +
+						VHT160_IEEE_FREQ_DIFF)) {
+					/*
+					 * Override the HW channel mode to
+					 * VHT160
+					 */
+					uint8_t ieee_160_cfreq;
+
+					ieee_160_cfreq =
+						(ieee_freq + ch_freq_seg1)/2;
+					chan_mode = WLAN_PHYMODE_11AC_VHT160;
+					*cfreq1 = dfs_mlme_ieee2mhz(
+							dfs->dfs_pdev_obj,
+							ch_freq_seg1,
+							WLAN_CHAN_5GHZ);
+					*cfreq2 = dfs_mlme_ieee2mhz(
+							dfs->dfs_pdev_obj,
+							ieee_160_cfreq,
+							WLAN_CHAN_5GHZ);
+				} else {
+					/*
+					 * Override the HW channel mode to
+					 * VHT80_80.
+					 */
+					chan_mode =
+						WLAN_PHYMODE_11AC_VHT80_80;
+					*cfreq2 = dfs_mlme_ieee2mhz(
+							dfs->dfs_pdev_obj,
+							ieee_freq,
+							WLAN_CHAN_5GHZ);
+				}
+				*phy_mode = lmac_get_phymode_info(
+						dfs->dfs_pdev_obj, chan_mode);
+				*dfs_set_cfreq2 = true;
+
+				/*
+				 * Finally set the agile flag.
+				 * When we want a full calibration of both
+				 * primary VHT80 and secondary VHT80 the agile
+				 * flag is set to FALSE else set to TRUE. When
+				 * a channel is being set for the first time
+				 * this flag must be FALSE because first time
+				 * the entire channel must be calibrated. All
+				 * subsequent times the flag must be set to TRUE
+				 * if we are changing only the secondary VHT80.
+				 */
+				if (dfs->dfs_precac_primary_freq ==
+						ch_freq_seg1)
+					*set_agile = true;
+				else
+					*set_agile = false;
+
+				dfs_debug(dfs, WLAN_DEBUG_DFS,
+					"cfreq1 = %u cfreq2 = %u ieee_freq = %u mode = %u set_agile = %d",
+					*cfreq1, *cfreq2, ieee_freq,
+					chan_mode, *set_agile);
+
+				dfs->dfs_precac_secondary_freq = ieee_freq;
+				dfs->dfs_precac_primary_freq = ch_freq_seg1;
+
+				/* Start the pre_cac_timer */
+				dfs_start_precac_timer(dfs,
+						dfs->dfs_precac_secondary_freq);
+			} else {
+				dfs->dfs_precac_secondary_freq = 0;
+			} /* End of if(ieee_freq) */
+		} /* End of if(dfs->dfs_precac_enable) */
+	}
+}
+
+void dfs_set_precac_enable(struct wlan_dfs *dfs, uint32_t value)
+{
+	struct wlan_objmgr_psoc *psoc;
+	struct wlan_lmac_if_target_tx_ops *tx_ops;
+	uint32_t target_type;
+
+	psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj);
+	if (!psoc) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "psoc is NULL");
+		dfs->dfs_precac_enable = 0;
+		return;
+	}
+
+	tx_ops = &psoc->soc_cb.tx_ops.target_tx_ops;
+	target_type = lmac_get_target_type(dfs->dfs_pdev_obj);
+
+	/*
+	 * If
+	 * 1) The chip is CASCADE,
+	 * 3) The user has enabled Pre-CAC and
+	 * 4) The regdomain the ETSI,
+	 * then enable preCAC.
+	 */
+	if ((1 == value) && tx_ops->tgt_is_tgt_type_qca9984(target_type) &&
+	    (utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_ETSI_DOMAIN)) {
+		dfs->dfs_precac_enable = value;
+	} else {
+		dfs->dfs_precac_enable = 0;
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "preCAC disabled");
+	}
+	if (dfs_is_precac_timer_running(dfs)) {
+		dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			 "Precac flag changed. Cancel the precac timer");
+		dfs_cancel_precac_timer(dfs);
+	}
+}
+
+uint32_t dfs_get_precac_enable(struct wlan_dfs *dfs)
+{
+	return dfs->dfs_precac_enable;
+}
+
+#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT
+int32_t dfs_set_precac_intermediate_chan(struct wlan_dfs *dfs, uint32_t value)
+{
+	struct dfs_channel chan;
+
+	qdf_mem_zero(&chan, sizeof(struct dfs_channel));
+	if (QDF_STATUS_SUCCESS !=
+	    dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj,
+					value, 0,
+					WLAN_PHYMODE_11AC_VHT80,
+					&chan.dfs_ch_freq,
+					&chan.dfs_ch_flags,
+					&chan.dfs_ch_flagext,
+					&chan.dfs_ch_ieee,
+					&chan.dfs_ch_vhtop_ch_freq_seg1,
+					&chan.dfs_ch_vhtop_ch_freq_seg2)) {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,
+			"Intermediate channel not found");
+		return -EINVAL;
+	}
+
+	/*Intermediate channel should be non-DFS channel*/
+	if (!WLAN_IS_CHAN_DFS(&chan)) {
+		dfs->dfs_precac_inter_chan = value;
+	} else {
+		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "intermediate channel %s",
+			(chan.dfs_ch_ieee == value) ?
+			"should not be DFS channel" : "is invalid");
+		dfs->dfs_precac_inter_chan = 0;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+uint32_t dfs_get_precac_intermediate_chan(struct wlan_dfs *dfs)
+{
+	return dfs->dfs_precac_inter_chan;
+}
+#endif