|
@@ -0,0 +1,888 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2016-2017 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 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 "../dfs_internal.h"
|
|
|
+
|
|
|
+static os_timer_func(dfs_precac_timeout);
|
|
|
+
|
|
|
+void dfs_zero_cac_attach(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ dfs->dfs_precac_timeout_override = -1;
|
|
|
+ dfs_init_precac_list(dfs);
|
|
|
+ qdf_timer_init(NULL,
|
|
|
+ &(dfs->dfs_precac_timer),
|
|
|
+ dfs_precac_timeout,
|
|
|
+ (void *) dfs,
|
|
|
+ QDF_TIMER_TYPE_WAKE_APPS);
|
|
|
+}
|
|
|
+
|
|
|
+void dfs_zero_cac_reset(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ dfs->dfs_precac_timeout_override = -1;
|
|
|
+ qdf_timer_stop(&dfs->dfs_precac_timer);
|
|
|
+ dfs->dfs_precac_primary_freq = 0;
|
|
|
+ dfs->dfs_precac_secondary_freq = 0;
|
|
|
+}
|
|
|
+
|
|
|
+int dfs_override_precac_timeout(struct wlan_dfs *dfs, int precac_timeout)
|
|
|
+{
|
|
|
+ if (dfs == NULL)
|
|
|
+ return -EIO;
|
|
|
+
|
|
|
+ dfs->dfs_precac_timeout_override = precac_timeout;
|
|
|
+ DFS_PRINTK("%s: PreCAC timeout is now %s (%d)\n",
|
|
|
+ __func__,
|
|
|
+ (precac_timeout == -1) ? "default" : "overridden",
|
|
|
+ precac_timeout);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int dfs_get_override_precac_timeout(struct wlan_dfs *dfs, int *precac_timeout)
|
|
|
+{
|
|
|
+ if (dfs == NULL)
|
|
|
+ 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_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(dfs->dfs_curchan->ic_ieee,
|
|
|
+ precac_entry->vht80_freq,
|
|
|
+ VHT80_OFFSET)) {
|
|
|
+ ret_val = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ PRECAC_LIST_UNLOCK(dfs);
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s : vht80_freq = %u ret_val = %d\n",
|
|
|
+ __func__, dfs->dfs_curchan->ic_ieee, ret_val);
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+bool dfs_is_ht80_80_chan_in_precac_done_list(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ 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 (IEEE80211_IS_CHAN_DFS(dfs->dfs_curchan)) {
|
|
|
+ TAILQ_FOREACH(precac_entry,
|
|
|
+ &dfs->dfs_precac_done_list,
|
|
|
+ pe_list) {
|
|
|
+ if (dfs->dfs_curchan->ic_vhtop_ch_freq_seg1
|
|
|
+ == precac_entry->vht80_freq) {
|
|
|
+ primary_found = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ primary_found = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if secondary DFS then search */
|
|
|
+ if (IEEE80211_IS_CHAN_DFS_CFREQ2(dfs->dfs_curchan) &&
|
|
|
+ primary_found) {
|
|
|
+ TAILQ_FOREACH(precac_entry,
|
|
|
+ &dfs->dfs_precac_done_list,
|
|
|
+ pe_list) {
|
|
|
+ if (dfs->dfs_curchan->ic_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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : freq_seg1 = %u freq_seq2 = %u ret_val = %d\n",
|
|
|
+ __func__,
|
|
|
+ dfs->dfs_curchan->ic_vhtop_ch_freq_seg1,
|
|
|
+ dfs->dfs_curchan->ic_vhtop_ch_freq_seg2,
|
|
|
+ ret_val);
|
|
|
+
|
|
|
+ return ret_val;
|
|
|
+}
|
|
|
+
|
|
|
+bool dfs_is_precac_done(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ bool ret_val = 0;
|
|
|
+
|
|
|
+ if (IEEE80211_IS_CHAN_11AC_VHT20(dfs->dfs_curchan) ||
|
|
|
+ IEEE80211_IS_CHAN_11AC_VHT40(dfs->dfs_curchan) ||
|
|
|
+ IEEE80211_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) {
|
|
|
+ ret_val = dfs_is_ht20_40_80_chan_in_precac_done_list(dfs);
|
|
|
+ } else if (IEEE80211_IS_CHAN_11AC_VHT80_80(dfs->dfs_curchan) ||
|
|
|
+ IEEE80211_IS_CHAN_11AC_VHT160(dfs->dfs_curchan)) {
|
|
|
+ ret_val = dfs_is_ht80_80_chan_in_precac_done_list(dfs);
|
|
|
+ }
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : ret_val = %d\n", __func__, 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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : is_radar_found_on_secondary_seg = %u secondary_freq = %u primary_freq = %u\n",
|
|
|
+ __func__,
|
|
|
+ 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->ic_ieee,
|
|
|
+ precac_entry->vht80_freq,
|
|
|
+ VHT80_IEEE_FREQ_OFFSET)) {
|
|
|
+ TAILQ_REMOVE(&dfs->dfs_precac_required_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : removing the freq = %u from required list and adding to NOL list\n",
|
|
|
+ __func__,
|
|
|
+ precac_entry->vht80_freq);
|
|
|
+ TAILQ_INSERT_TAIL(&dfs->dfs_precac_nol_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+ OS_SET_TIMER(&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->ic_ieee,
|
|
|
+ precac_entry->vht80_freq, 6)) {
|
|
|
+ TAILQ_REMOVE(&dfs->dfs_precac_done_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : removing the the freq = %u from done list and adding to NOL list\n",
|
|
|
+ __func__,
|
|
|
+ precac_entry->vht80_freq);
|
|
|
+ TAILQ_INSERT_TAIL(&dfs->dfs_precac_nol_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+ OS_SET_TIMER(&precac_entry->precac_nol_timer,
|
|
|
+ dfs_get_nol_timeout(dfs)*1000);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ PRECAC_LIST_UNLOCK(dfs);
|
|
|
+
|
|
|
+ /* TODO 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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Primary CAC is running, defer the channel change\n",
|
|
|
+ __func__);
|
|
|
+ } 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_ieee80211_channel **chan)
|
|
|
+{
|
|
|
+ uint8_t first_primary_ic_ieee;
|
|
|
+
|
|
|
+ first_primary_ic_ieee =
|
|
|
+ dfs->dfs_precac_secondary_freq - VHT80_IEEE_FREQ_OFFSET;
|
|
|
+
|
|
|
+ dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj, first_primary_ic_ieee, 0,
|
|
|
+ IEEE80211_MODE_11AC_VHT80,
|
|
|
+ &((*chan)->ic_freq),
|
|
|
+ &((*chan)->ic_flags),
|
|
|
+ &((*chan)->ic_flagext),
|
|
|
+ &((*chan)->ic_ieee),
|
|
|
+ &((*chan)->ic_vhtop_ch_freq_seg1),
|
|
|
+ &((*chan)->ic_vhtop_ch_freq_seg2));
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : removing the the freq = %u from required list and adding to done list\n",
|
|
|
+ __func__,
|
|
|
+ precac_entry->vht80_freq);
|
|
|
+ TAILQ_INSERT_TAIL(&dfs->dfs_precac_done_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ PRECAC_LIST_UNLOCK(dfs);
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Pre-cac expired, Precac Secondary chan %u curr time %d\n",
|
|
|
+ __func__,
|
|
|
+ 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. */
|
|
|
+
|
|
|
+ /* TODO xxx : Need to lock the channel change */
|
|
|
+ dfs_mlme_channel_change_by_precac(dfs->dfs_pdev_obj);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : removing the the freq = %u from PreCAC NOL-list and adding Precac-required list\n",
|
|
|
+ __func__, precac_entry->vht80_freq);
|
|
|
+ TAILQ_INSERT_TAIL(&dfs->dfs_precac_required_list, precac_entry,
|
|
|
+ pe_list);
|
|
|
+ }
|
|
|
+ PRECAC_LIST_UNLOCK(dfs);
|
|
|
+
|
|
|
+ /* TODO 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_ic_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_ieee80211_channel *ichan = NULL, lc;
|
|
|
+
|
|
|
+ ichan = &lc;
|
|
|
+ dfs_mlme_get_ic_channels(dfs->dfs_pdev_obj,
|
|
|
+ &(ichan->ic_freq),
|
|
|
+ &(ichan->ic_flags),
|
|
|
+ &(ichan->ic_flagext),
|
|
|
+ &(ichan->ic_ieee),
|
|
|
+ &(ichan->ic_vhtop_ch_freq_seg1),
|
|
|
+ &(ichan->ic_vhtop_ch_freq_seg2),
|
|
|
+ i);
|
|
|
+
|
|
|
+ if (IEEE80211_IS_CHAN_11AC_VHT80(ichan) &&
|
|
|
+ IEEE80211_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->ic_vhtop_ch_freq_seg1) {
|
|
|
+ found = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (!found) {
|
|
|
+ struct dfs_precac_entry *precac_entry;
|
|
|
+
|
|
|
+ precac_entry = qdf_mem_malloc(
|
|
|
+ sizeof(*precac_entry));
|
|
|
+ precac_entry->vht80_freq =
|
|
|
+ ichan->ic_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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Print the list of VHT80 frequencies from linked list\n",
|
|
|
+ __func__);
|
|
|
+ TAILQ_FOREACH(tmp_precac_entry,
|
|
|
+ &dfs->dfs_precac_required_list,
|
|
|
+ pe_list)
|
|
|
+ DFS_PRINTK("freq=%u\n", tmp_precac_entry->vht80_freq);
|
|
|
+}
|
|
|
+
|
|
|
+void dfs_deinit_precac_list(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ struct dfs_precac_entry *tmp_precac_entry, *precac_entry;
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Free the list of VHT80 frequencies from linked list(precac_required)\n",
|
|
|
+ __func__);
|
|
|
+ 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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Free the list of VHT80 frequencies from linked list(precac_done)\n",
|
|
|
+ __func__);
|
|
|
+ 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_required_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+ qdf_mem_free(precac_entry);
|
|
|
+ }
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Free the list of VHT80 frequencies from linked list(precac_nol)\n",
|
|
|
+ __func__);
|
|
|
+ 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_required_list,
|
|
|
+ precac_entry, pe_list);
|
|
|
+ qdf_mem_free(precac_entry);
|
|
|
+ }
|
|
|
+ PRECAC_LIST_UNLOCK(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_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s : exclude_ieee_freq = %u\n",
|
|
|
+ __func__, 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_DPRINTK(dfs, WLAN_DEBUG_DFS, "%s : ieee_freq = %u\n",
|
|
|
+ __func__, 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_ieee80211_channel *ichan, lc;
|
|
|
+ uint8_t first_primary_ic_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_ic_ieee = precac_chan - VHT80_IEEE_FREQ_OFFSET;
|
|
|
+
|
|
|
+ primary_cac_timeout = dfs_mlme_get_cac_timeout(dfs->dfs_pdev_obj,
|
|
|
+ dfs->dfs_curchan->ic_freq,
|
|
|
+ dfs->dfs_curchan->ic_vhtop_ch_freq_seg2,
|
|
|
+ dfs->dfs_curchan->ic_flags);
|
|
|
+
|
|
|
+ ichan = &lc;
|
|
|
+ dfs_mlme_find_dot11_channel(dfs->dfs_pdev_obj, first_primary_ic_ieee, 0,
|
|
|
+ IEEE80211_MODE_11AC_VHT80,
|
|
|
+ &(ichan->ic_freq),
|
|
|
+ &(ichan->ic_flags),
|
|
|
+ &(ichan->ic_flagext),
|
|
|
+ &(ichan->ic_ieee),
|
|
|
+ &(ichan->ic_vhtop_ch_freq_seg1),
|
|
|
+ &(ichan->ic_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->ic_freq,
|
|
|
+ ichan->ic_vhtop_ch_freq_seg2,
|
|
|
+ ichan->ic_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).
|
|
|
+ */
|
|
|
+ precac_timeout = QDF_MAX(primary_cac_timeout, secondary_cac_timeout) +
|
|
|
+ EXTRA_TIME_IN_SEC;
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : precactimeout = %d\n",
|
|
|
+ __func__, (precac_timeout)*1000);
|
|
|
+ OS_SET_TIMER(&dfs->dfs_precac_timer, (precac_timeout) * 1000);
|
|
|
+}
|
|
|
+
|
|
|
+void dfs_print_precaclists(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ struct dfs_precac_entry *tmp_precac_entry;
|
|
|
+
|
|
|
+ if (dfs == NULL) {
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_NOL,
|
|
|
+ "%s: sc_dfs is NULL\n", __func__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ PRECAC_LIST_LOCK(dfs);
|
|
|
+
|
|
|
+ /* Print the Pre-CAC required List */
|
|
|
+ DFS_PRINTK("%s : Pre-cac-required list of VHT80 frequencies\n",
|
|
|
+ __func__);
|
|
|
+ TAILQ_FOREACH(tmp_precac_entry,
|
|
|
+ &dfs->dfs_precac_required_list,
|
|
|
+ pe_list) {
|
|
|
+ DFS_PRINTK("%s : freq=%u\n", __func__,
|
|
|
+ tmp_precac_entry->vht80_freq);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Print the Pre-CAC done List */
|
|
|
+ DFS_PRINTK("%s : Pre-cac-done list of VHT80 frequencies\n", __func__);
|
|
|
+ TAILQ_FOREACH(tmp_precac_entry,
|
|
|
+ &dfs->dfs_precac_done_list,
|
|
|
+ pe_list) {
|
|
|
+ DFS_PRINTK("%s : freq=%u\n",
|
|
|
+ __func__, tmp_precac_entry->vht80_freq);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Print the Pre-CAC NOL List */
|
|
|
+ DFS_PRINTK("%s Pre-cac-NOL list of VHT80 frequencies\n", __func__);
|
|
|
+ TAILQ_FOREACH(tmp_precac_entry,
|
|
|
+ &dfs->dfs_precac_nol_list,
|
|
|
+ pe_list) {
|
|
|
+ DFS_PRINTK("%s : freq=%u\n", __func__,
|
|
|
+ tmp_precac_entry->vht80_freq);
|
|
|
+ }
|
|
|
+
|
|
|
+ PRECAC_LIST_UNLOCK(dfs);
|
|
|
+}
|
|
|
+
|
|
|
+void dfs_reset_precaclists(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : Reset precaclist of VHT80 frequencies\n", __func__);
|
|
|
+ dfs_deinit_precac_list(dfs);
|
|
|
+ dfs_init_precac_list(dfs);
|
|
|
+}
|
|
|
+
|
|
|
+void dfs_reset_precac_lists(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ if (dfs == NULL) {
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS_NOL,
|
|
|
+ "%s: sc_dfs is NULL\n", __func__);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ dfs_reset_precaclists(dfs);
|
|
|
+}
|
|
|
+
|
|
|
+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 == IEEE80211_MODE_11AC_VHT80) {
|
|
|
+ /*
|
|
|
+ * If
|
|
|
+ * 1) The chip is CASCADE
|
|
|
+ * 2) The user phy_mode is VHT80 and
|
|
|
+ * 3) The user has enabled Pre-CAC and
|
|
|
+ * 4) The regdomain the ETSI
|
|
|
+ * then find a center frequency for the secondary VHT80 and
|
|
|
+ * Change the mode to VHT80_80 or VHT160
|
|
|
+ */
|
|
|
+ uint8_t ieee_freq;
|
|
|
+
|
|
|
+ DFS_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : precac_secondary_freq = %u precac_running = %u\n",
|
|
|
+ __func__,
|
|
|
+ dfs->dfs_precac_secondary_freq,
|
|
|
+ dfs->dfs_precac_timer_running);
|
|
|
+
|
|
|
+ if (dfs->dfs_precac_enable &&
|
|
|
+ (lmac_get_target_type(dfs->dfs_pdev_obj) ==
|
|
|
+ TARGET_TYPE_QCA9984) &&
|
|
|
+ (lmac_get_dfsdomain(dfs->dfs_pdev_obj) ==
|
|
|
+ DFS_ETSI_DOMAIN)) {
|
|
|
+ /*
|
|
|
+ * 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 = IEEE80211_MODE_11AC_VHT160;
|
|
|
+ *cfreq1 = dfs_mlme_ieee2mhz(
|
|
|
+ dfs->dfs_pdev_obj,
|
|
|
+ ch_freq_seg1,
|
|
|
+ IEEE80211_CHAN_5GHZ);
|
|
|
+ *cfreq2 = dfs_mlme_ieee2mhz(
|
|
|
+ dfs->dfs_pdev_obj,
|
|
|
+ ieee_160_cfreq,
|
|
|
+ IEEE80211_CHAN_5GHZ);
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Override the HW channel mode to
|
|
|
+ * VHT80_80.
|
|
|
+ */
|
|
|
+ chan_mode =
|
|
|
+ IEEE80211_MODE_11AC_VHT80_80;
|
|
|
+ *cfreq2 = dfs_mlme_ieee2mhz(
|
|
|
+ dfs->dfs_pdev_obj,
|
|
|
+ ieee_freq,
|
|
|
+ IEEE80211_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_DPRINTK(dfs, WLAN_DEBUG_DFS,
|
|
|
+ "%s : cfreq1 = %u cfreq2 = %u ieee_freq = %u mode = %u set_agile = %d\n",
|
|
|
+ __func__,
|
|
|
+ *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);
|
|
|
+ } /* End of if(ieee_freq) */
|
|
|
+ } /* End of if(dfs->dfs_precac_enable) */
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void dfs_set_precac_enable(struct wlan_dfs *dfs, uint32_t value)
|
|
|
+{
|
|
|
+ dfs->dfs_precac_enable = value;
|
|
|
+}
|
|
|
+
|
|
|
+uint32_t dfs_get_precac_enable(struct wlan_dfs *dfs)
|
|
|
+{
|
|
|
+ return dfs->dfs_precac_enable;
|
|
|
+}
|