From 6ee85bd7d40f421fd6156b0f5f060a2a1e2c2434 Mon Sep 17 00:00:00 2001 From: Vijay Krishnan Date: Fri, 30 Oct 2020 10:59:15 +0530 Subject: [PATCH] qca-wifi: Split dfs_zero_cac.c into multiple files To enhance code-readability, split dfs_zero_cac.c file into multiple smaller and logically coherent files. The new files are: dfs_agile_sm.c, dfs_precac_list.c, dfs_precac_forest.h CRs-Fixed: 2822588 Change-Id: If3e599cc3d895b3fbc6b114de6b69dd3afff3b6f --- umac/dfs/core/src/dfs_precac_forest.h | 257 ++ umac/dfs/core/src/misc/dfs_agile_sm.c | 952 +++++++ umac/dfs/core/src/misc/dfs_precac_list.c | 1871 +++++++++++++ umac/dfs/core/src/misc/dfs_zero_cac.c | 3212 ++-------------------- 4 files changed, 3233 insertions(+), 3059 deletions(-) create mode 100644 umac/dfs/core/src/dfs_precac_forest.h create mode 100644 umac/dfs/core/src/misc/dfs_agile_sm.c create mode 100644 umac/dfs/core/src/misc/dfs_precac_list.c diff --git a/umac/dfs/core/src/dfs_precac_forest.h b/umac/dfs/core/src/dfs_precac_forest.h new file mode 100644 index 0000000000..dff6bc08f5 --- /dev/null +++ b/umac/dfs/core/src/dfs_precac_forest.h @@ -0,0 +1,257 @@ +/* + * Copyright (c) 2016-2020 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 the headers of Precac Tree structure feature. + * + */ + +#ifndef _DFS_PRECAC_FOREST_H_ +#define _DFS_PRECAC_FOREST_H_ +#include +#include +#include +#define N_OFFSETS 2 + +/** + * struct dfs_channel_bw - Structure to store the information about precac + * root's primary channel frequency, maximum bandwidth and the center frequency. + * + * @dfs_pri_ch_freq: Primary channel frequency of the root channel. + * @dfs_center_ch_freq: Center frequency of the 20/40/80/160 channel.In case of + * the 165MHz channel, it is 5730MHz. + * @dfs_max_bw: Maximum bandwidth of the channel available in the + * current channel list. + */ +struct dfs_channel_bw { + uint16_t dfs_pri_ch_freq; + uint16_t dfs_center_ch_freq; + uint16_t dfs_max_bw; +}; + +/** + * struct precac_tree_offset_for_different_bw - Bandwidth, tree depth and + * channel offsets information to build the precac tree. + * @bandwidth: Bandwidth of the the root node. + * @tree_depth: Tree depth of the precac tree. + * @initial_and_next_offsets: Offset to root node to find the initial and the + * next channels of the node. + */ +struct precac_tree_offset_for_different_bw { + int bandwidth; + int tree_depth; + int initial_and_next_offsets[TREE_DEPTH_MAX][N_OFFSETS]; +}; + +#define START_INDEX 0 +#define STEP_INDEX 1 + + +static const +struct precac_tree_offset_for_different_bw offset20 = {DFS_CHWIDTH_20_VAL, + TREE_DEPTH_20, + { + {0, NEXT_20_CHAN_FREQ_OFFSET} + } +}; + +static const +struct precac_tree_offset_for_different_bw offset40 = {DFS_CHWIDTH_40_VAL, + TREE_DEPTH_40, + { + {0, NEXT_40_CHAN_FREQ_OFFSET}, + {-10, NEXT_20_CHAN_FREQ_OFFSET} + } +}; + +static const +struct precac_tree_offset_for_different_bw offset80 = {DFS_CHWIDTH_80_VAL, + TREE_DEPTH_80, + { + {0, NEXT_80_CHAN_FREQ_OFFSET}, + {-20, NEXT_40_CHAN_FREQ_OFFSET}, + {-30, NEXT_20_CHAN_FREQ_OFFSET} + } +}; + +static const +struct precac_tree_offset_for_different_bw offset160 = {DFS_CHWIDTH_160_VAL, + TREE_DEPTH_160, + { + {INITIAL_160_CHAN_FREQ_OFFSET, NEXT_160_CHAN_FREQ_OFFSET}, + {INITIAL_80_CHAN_FREQ_OFFSET, NEXT_80_CHAN_FREQ_OFFSET}, + {INITIAL_40_CHAN_FREQ_OFFSET, NEXT_40_CHAN_FREQ_OFFSET}, + {INITIAL_20_CHAN_FREQ_OFFSET, NEXT_20_CHAN_FREQ_OFFSET} + } +}; + +static const +struct precac_tree_offset_for_different_bw default_offset = {0, 0}; + +/* Given a bandwidth, find the number of subchannels in that bandwidth */ +#define N_SUBCHS_FOR_BANDWIDTH(_bw) ((_bw) / MIN_DFS_SUBCHAN_BW) + +#define DFS_160MHZ_SECSEG_CHAN_OFFSET 40 + +#define VHT80_FREQ_OFFSET 30 +/* For any 160MHz channel, a frequency offset of 70MHz would have been enough + * to include the right edge and left edge channels. But, the restricted 80P80 + * or the 165MHz channel is also assumed to have a 160MHz root ie channel 146, + * so an offset of 75MHz is chosen. + */ +#define VHT160_FREQ_OFFSET 75 + +#define IS_WITHIN_RANGE(_A, _B, _C) \ + (((_A) >= ((_B)-(_C))) && ((_A) <= ((_B)+(_C)))) + +#define IS_WITHIN_RANGE_STRICT(_A, _B, _C) \ + (((_A) > ((_B)-(_C))) && ((_A) < ((_B)+(_C)))) + +#define MAX_PREFIX_CHAR 28 + +/** + * dfs_configure_deschan_for_precac() - API to prioritize user configured + * channel for preCAC. + * + * @dfs: Pointer to DFS of wlan_dfs structure. + * Return: frequency of type qdf_freq_t if configured, else 0. + */ +qdf_freq_t dfs_configure_deschan_for_precac(struct wlan_dfs *dfs); + +/** + * dfs_is_pcac_required_for_freq() - Find if given frequency is preCAC required. + * @node: Pointer to the preCAC tree Node in which the frequency is present. + * @freq: Frequency to be checked. + * + * Return: False if the frequency is not fully CAC done or in NOL, else true. + */ +bool dfs_is_pcac_required_for_freq(struct precac_tree_node *node, + uint16_t freq); + +/** + * dfs_find_subchannels_for_center_freq() - API to find the subchannels given + * the center frequencies and ch_width. + * @pri_center_freq: It is the center of 20/40/80/160Mhz band and for 80+80Mhz + * it is the center of the first 80Mhz band. + * @sec_center_freq: It is used only for 80+80Mhz and denotes the center + * of the second 80Mhz band. + * @ch_width: Channel width. + * @channels: List of subchannels. + * + * Return: Number of subchannels. + */ +uint8_t dfs_find_subchannels_for_center_freq(qdf_freq_t pri_center_freq, + qdf_freq_t sec_center_freq, + enum phy_ch_width ch_width, + qdf_freq_t *channels); + +/** + * dfs_find_precac_state_of_node() - Find the preCAC state of the given channel. + * @channel: Channel whose preCAC state is to be found. + * @precac_entry: PreCAC entry where the channel exists. + * + * Return, enum value of type precac_chan_state. + */ +enum precac_chan_state +dfs_find_precac_state_of_node(qdf_freq_t channel, + struct dfs_precac_entry *precac_entry); + +/* dfs_mark_adfs_chan_as_cac_done()- Mark the ADFS CAC completed channel as + * CAC done in the precac tree. + * @dfs: Pointer to struct wlan_dfs. + */ +void dfs_mark_adfs_chan_as_cac_done(struct wlan_dfs *dfs); + +/* dfs_descend_precac_tree_for_freq() - Descend into the precac BSTree based on + * the channel provided. If the channel is less than + * given node's channel, descend left, else right. + * @node: Precac BSTree node. + * @chan_freq: Channel freq whose node is to be found. + * + * Return: the next precac_tree_node (left child or right child of + * current node). + */ + +struct precac_tree_node * +dfs_descend_precac_tree_for_freq(struct precac_tree_node *node, + uint16_t chan_freq); + +/** + * dfs_unmark_rcac_done() - Unmark the CAC done channels from the RCAC list. + * @dfs: Pointer to wlan_dfs object. + */ +#ifdef QCA_SUPPORT_ADFS_RCAC +void dfs_unmark_rcac_done(struct wlan_dfs *dfs); +#else +static inline +void dfs_unmark_rcac_done(struct wlan_dfs *dfs) +{ +} +#endif +/** + * dfs_is_precac_completed_count_non_zero() - API to find if the preCAC + * completed channels count is zero/non_zero. + * @dfs: Pointer to DFS object. + * + * Return true, if there exists atleast one node/subchannel in the preCAC list + * that is CAC done, else return false. + */ +bool dfs_is_precac_completed_count_non_zero(struct wlan_dfs *dfs); + +/* + * dfs_fill_adfs_chan_params() - Fill the ADFS FW params. + * @dfs: Pointer to wlan_dfs. + * @adfs_param: Pointer to struct dfs_agile_cac_params. + * @ch_freq: Frequency in MHZ to be programmed to the agile detector. + */ +void dfs_fill_adfs_chan_params(struct wlan_dfs *dfs, + struct dfs_agile_cac_params *adfs_param); + +/* dfs_agile_precac_cleanup() - Reset parameters of wlan_dfs. + * + * @dfs: Pointer to struct wlan_dfs. + */ +void dfs_agile_precac_cleanup(struct wlan_dfs *dfs); + +#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 return true, else false. + * + * Return: true if precac done on home channel, else false. + */ +bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs); +#else +static inline bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs) +{ + return false; +} +#endif +#endif /* _DFS_PRECAC_FOREST_H_ */ diff --git a/umac/dfs/core/src/misc/dfs_agile_sm.c b/umac/dfs/core/src/misc/dfs_agile_sm.c new file mode 100644 index 0000000000..086f2f14d8 --- /dev/null +++ b/umac/dfs/core/src/misc/dfs_agile_sm.c @@ -0,0 +1,952 @@ +/* + * Copyright (c) 2016-2020 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 Agile State machine functions. + * + */ + +#include "../dfs_precac_forest.h" +#include "wlan_reg_services_api.h" + +#ifdef QCA_SUPPORT_AGILE_DFS +/* dfs_start_agile_engine() - Prepare ADFS params and program the agile + * engine sending agile config cmd to FW. + * @dfs: Pointer to struct wlan_dfs. + */ +void dfs_start_agile_engine(struct wlan_dfs *dfs) +{ + struct dfs_agile_cac_params adfs_param; + struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; + struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; + + /* Fill the RCAC ADFS params and send it to FW. + * FW does not use RCAC timeout values for RCAC feature. + * FW runs an infinite timer. + */ + dfs_fill_adfs_chan_params(dfs, &adfs_param); + adfs_param.min_precac_timeout = MIN_RCAC_DURATION; + adfs_param.max_precac_timeout = MAX_RCAC_DURATION; + adfs_param.ocac_mode = QUICK_RCAC_MODE; + + qdf_info("%s : %d RCAC channel request sent for pdev: %pK ch_freq: %d", + __func__, __LINE__, dfs->dfs_pdev_obj, + dfs->dfs_agile_precac_freq_mhz); + + dfs_tx_ops = wlan_psoc_get_dfs_txops(dfs_soc_obj->psoc); + + if (dfs_tx_ops && dfs_tx_ops->dfs_agile_ch_cfg_cmd) + dfs_tx_ops->dfs_agile_ch_cfg_cmd(dfs->dfs_pdev_obj, + &adfs_param); + else + dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS, + "dfs_tx_ops=%pK", dfs_tx_ops); +} + +/** + * --------------------- ROLLING CAC STATE MACHINE ---------------------- + * + * Rolling CAC is a feature where in, a separate hardware (Agile detector) + * will be brought up in a channel that is not the current operating channel + * and will continue to monitor the channel non-stop, until the next + * channel change or radar in this RCAC channel. + * + * Now if the Rolling CAC channel was radar free for a minimum duration + * (1 min.) and the device is now switching to this channel, no CAC is required. + * + * I.e. let's say the current operating channel is 64 HT80 and we are starting + * the agile detector in 100 HT80. After a minute of being up in 100 HT80, we + * switch the radio to 100 HT80. This operating channel change will not + * require CAC now since the channel was radar free for the last 1 minute, + * as determined by the agile detector. + * + * Introduction of a rolling CAC state machine: + * + * To acheive the rolling CAC feature using the agile detector, a trivial + * state machine is implemented, as represented below: + * + * _________________ + * | | + * |------------>| INIT |<-----------| + * | |_________________| | + * | | | + * | | | + * | [EV_RCAC_STOP] | [EV_RCAC_START] | [EV_RCAC_STOP] + * | [EV_ADFS_RADAR] | | [EV_ADFS_RADAR] + * | | | + * | | | + * ________|________ | ________|________ + * | | |----------->| | + * | COMPLETE | | RUNNING | + * |_________________|<-------------------------|_________________| + * [EV_RCAC_DONE] + * + * + * + * Legend: + * _________________ + * | | + * 1. | RCAC STATES | + * |_________________| + * + * 2. [RCAC_EVENTS] + * + * + * Event triggers and handlers description: + * + * EV_RCAC_START: + * Posted from vdev response and is handled by all three states. + * 1. INIT handler: + * a. Check if RCAC is already running, + * - If yes, do not transition. + * - If no, go to step b. + * b. Check if a new RCAC channel can be found, + * - If no, do not transition. + * - If yes, transition to RUNNING. + * + * EV_RCAC_STOP: + * Posted from last vap down or config disable, handled by RUNNING + * and COMPLETE. + * 1. RUNNING handler: + * a. Stop the HOST RCAC timer. + * b. Send wmi_adfs_abort_cmd to FW and transition to INIT. + * 2. COMPLETE handler: + * a. Send wmi_adfs_abort_cmd to FW and transition to INIT. + * + * EV_ADFS_RADAR: + * Posted from radar detection and is handled in RUNNING and COMPLETE. + * 1. RUNNING handler (same as EV_RCAC_START): + * a. Check if RCAC was running for this pdev, + * - If yes, transition to INIT and post EV_RCAC_START event. + * - If no, ignore. + * 2. COMPLETE handler (same as EV_RCAC_START): + * a. Check if RCAC was running for this pdev, + * - If yes, transition to INIT and post EV_RCAC_START event. + * - If no, ignore. + * + * Note: EV_ADFS_RADAR works same as EV_RCAC_START event right now, but + * will change in future, where, based on user preference, either + * a new RCAC channel will be picked (requiring the transition to + * INIT like present), or RCAC will be restarted on the same channel. + * + * EV_RCAC_DONE: + * Posted from host RCAC timer completion and is handled in RUNNING. + * 1. RUNNING handler: + * a. mark RCAC done and transition to COMPLETE. + * + * Epilogue: + * Rolling CAC state machine is for the entire psoc and since the + * agile detector can run for one pdev at a time, sharing of resource is + * required. + * In case of ETSI preCAC, sharing was done in a round robin fashion where + * each pdev runs ADFS for it's channels alternatively. However, in RCAC, the + * CAC period is not defined is continuous till the next channel change. + * + * Hence ADFS detector is shared as follows: + * 1. First come first serve: the pdev that is brought up first, i.e, for + * the first vdev response, an RCAC_START is posted and this pdev will + * hold the agile detector and run RCAC till it is stopped. + * 2. Stopping the RCAC can be either by disabling user config "rcac_en 0" + * or by bringing down all vaps, or if no channel is available. + * 3. Once RCAC is stopped for a pdev, it can be started in the other pdev + * by restarting it's vap (i.e. a vdev response). + * + * A working sequence of RCAC is as follows: + * - Consider that the channel configured during bring up is 52HT80. + * 1. The First VAP's vdev_start_resp posts an event EV_RCAC_START to the + * RCAC state machine. + * 2. The RCAC state machine which is in INIT state (default) receives the + * event, picks a channel to do rolling CAC on, e.g. channel 100HT80. + * The SM is then transitioned to RUNNING state. + * 3. In the entry of RUNNING state, a host timer is started and agile + * cfg cmd to FW is sent. + * 4. When the HOST timer expires, it posts the EV_RCAC_DONE event to + * the state machine. + * 5. EV_RCAC_DONE event received in RUNNING state, transitions the SM + * to COMPLETE. + * 6. In the entry of COMPLETE, the RCAC channel is marked as CAC done + * in the precac tree. + * 7. If radar is detected on primary channel, the new channel is the + * RCAC channel (100HT80) which does not require CAC if the preCAC + * tree is marked as CAC done. + * Before sending vdev_start, an EV_RCAC_STOP is posted + * which moves the SM to INIT state clearing all the params and + * bringing down the agile detector. + * (CAC decisions are taken before). + * 8. After vdev_resp, another EV_RCAC_START is sent to restart the + * RCAC SM with a new RCAC channel if available. + * + * A future enhancement will be triggering RCAC_START at user level. + */ + +/** + * dfs_agile_set_curr_state() - API to set the current state of Agile SM. + * @dfs_soc_obj: Pointer to DFS soc private object. + * @state: value of current state. + * + * Return: void. + */ +static void dfs_agile_set_curr_state(struct dfs_soc_priv_obj *dfs_soc_obj, + enum dfs_agile_sm_state state) +{ + if (state < DFS_AGILE_S_MAX) { + dfs_soc_obj->dfs_agile_sm_cur_state = state; + } else { + dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS, + "DFS RCAC state (%d) is invalid", state); + QDF_BUG(0); + } +} + +/** + * dfs_agile_get_curr_state() - API to get current state of Agile SM. + * @dfs_soc_obj: Pointer to DFS soc private object. + * + * Return: current state enum of type, dfs_rcac_sm_state. + */ +static enum dfs_agile_sm_state +dfs_agile_get_curr_state(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + return dfs_soc_obj->dfs_agile_sm_cur_state; +} + +/** + * dfs_rcac_sm_transition_to() - Wrapper API to transition the Agile SM state. + * @dfs_soc_obj: Pointer to dfs soc private object that hold the SM handle. + * @state: State to which the SM is transitioning to. + * + * Return: void. + */ +static void dfs_agile_sm_transition_to(struct dfs_soc_priv_obj *dfs_soc_obj, + enum dfs_agile_sm_state state) +{ + wlan_sm_transition_to(dfs_soc_obj->dfs_agile_sm_hdl, state); +} + +/** + * dfs_agile_sm_deliver_event() - API to post events to Agile SM. + * @dfs_soc_obj: Pointer to dfs soc private object. + * @event: Event to be posted to the RCAC SM. + * @event_data_len: Length of event data. + * @event_data: Pointer to event data. + * + * Return: QDF_STATUS_SUCCESS on handling the event, else failure. + * + * Note: This version of event posting API is not under lock and hence + * should only be called for posting events within the SM and not be + * under a dispatcher API without a lock. + */ +static +QDF_STATUS dfs_agile_sm_deliver_event(struct dfs_soc_priv_obj *dfs_soc_obj, + enum dfs_agile_sm_evt event, + uint16_t event_data_len, + void *event_data) +{ + return wlan_sm_dispatch(dfs_soc_obj->dfs_agile_sm_hdl, + event, + event_data_len, + event_data); +} + +#ifdef QCA_SUPPORT_ADFS_RCAC +/* dfs_start_agile_rcac_timer() - Start host agile RCAC timer. + * + * @dfs: Pointer to struct wlan_dfs. + */ +void dfs_start_agile_rcac_timer(struct wlan_dfs *dfs) +{ + struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; + uint32_t rcac_timeout = MIN_RCAC_DURATION; + + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Host RCAC timeout = %d ms", rcac_timeout); + + qdf_timer_mod(&dfs_soc_obj->dfs_rcac_timer, + rcac_timeout); +} + + +/* dfs_stop_agile_rcac_timer() - Cancel the RCAC timer. + * + * @dfs: Pointer to struct wlan_dfs. + */ +void dfs_stop_agile_rcac_timer(struct wlan_dfs *dfs) +{ + struct dfs_soc_priv_obj *dfs_soc_obj; + + dfs_soc_obj = dfs->dfs_soc_obj; + qdf_timer_sync_cancel(&dfs_soc_obj->dfs_rcac_timer); +} + + +/** + * dfs_abort_agile_rcac() - Send abort Agile RCAC to F/W. + * @dfs: Pointer to struct wlan_dfs. + */ +static void dfs_abort_agile_rcac(struct wlan_dfs *dfs) +{ + + struct wlan_objmgr_psoc *psoc; + struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; + + dfs_stop_agile_rcac_timer(dfs); + psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); + dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc); + if (dfs_tx_ops && dfs_tx_ops->dfs_ocac_abort_cmd) + dfs_tx_ops->dfs_ocac_abort_cmd(dfs->dfs_pdev_obj); + + qdf_mem_zero(&dfs->dfs_rcac_param, sizeof(struct dfs_rcac_params)); + dfs->dfs_agile_precac_freq_mhz = 0; + dfs->dfs_precac_chwidth = CH_WIDTH_INVALID; + dfs->dfs_soc_obj->cur_agile_dfs_index = DFS_PSOC_NO_IDX; +} +#else +static inline void dfs_abort_agile_rcac(struct wlan_dfs *dfs) +{ +} +#endif + +/* dfs_abort_agile_precac() - Reset parameters of wlan_dfs and send abort + * to F/W. + * @dfs: Pointer to struct wlan_dfs. + */ +static void dfs_abort_agile_precac(struct wlan_dfs *dfs) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; + + psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); + dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc); + + dfs_agile_precac_cleanup(dfs); + /*Send the abort to F/W as well */ + if (dfs_tx_ops && dfs_tx_ops->dfs_ocac_abort_cmd) + dfs_tx_ops->dfs_ocac_abort_cmd(dfs->dfs_pdev_obj); +} + +/** + * dfs_agile_state_init_entry() - Entry API for INIT state + * @ctx: DFS SoC private object + * + * API to perform operations on moving to INIT state + * + * Return: void + */ +static void dfs_agile_state_init_entry(void *ctx) +{ + struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; + + dfs_agile_set_curr_state(dfs_soc, DFS_AGILE_S_INIT); +} + +/** + * dfs_agile_state_init_exit() - Exit API for INIT state + * @ctx: DFS SoC private object + * + * API to perform operations on moving out of INIT state + * + * Return: void + */ +static void dfs_agile_state_init_exit(void *ctx) +{ + /* NO OPS */ +} + +/** + * dfs_init_agile_start_evt_handler() - Init state start event handler. + * @dfs: Instance of wlan_dfs structure. + * @dfs_soc: DFS SoC private object + * + * Return : True if PreCAC/RCAC chan is found. + */ +static bool dfs_init_agile_start_evt_handler(struct wlan_dfs *dfs, + struct dfs_soc_priv_obj *dfs_soc) +{ + bool is_chan_found = false; + + /*For RCAC */ + if (dfs_is_agile_rcac_enabled(dfs)) { + /* Check if feature is enabled for this DFS and if RCAC channel + * is valid, if those are true, send appropriate WMIs to FW + * and only then transition to the state as follows. + */ + dfs_prepare_agile_rcac_channel(dfs, &is_chan_found); + } + /*For PreCAC */ + else if (dfs_is_agile_precac_enabled(dfs)) { + dfs_soc->dfs_priv[dfs->dfs_psoc_idx].agile_precac_active + = true; + if (!dfs_soc->precac_state_started && + !dfs_soc->dfs_precac_timer_running) { + dfs_soc->precac_state_started = true; + dfs_prepare_agile_precac_chan(dfs, &is_chan_found); + } + } + + return is_chan_found; +} + +/** + * dfs_agile_state_init_event() - INIT State event handler + * @ctx: DFS SoC private object + * @event: Event posted to the SM. + * @event_data_len: Length of event data. + * @event_data: Pointer to event data. + * + * API to handle events in INIT state + * + * Return: TRUE: on handling event + * FALSE: on ignoring the event + */ +static bool dfs_agile_state_init_event(void *ctx, + uint16_t event, + uint16_t event_data_len, + void *event_data) +{ + struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; + bool status; + struct wlan_dfs *dfs; + bool is_chan_found; + + if (!event_data) + return false; + + dfs = (struct wlan_dfs *)event_data; + + switch (event) { + case DFS_AGILE_SM_EV_AGILE_START: + + if (dfs_soc->cur_agile_dfs_index != DFS_PSOC_NO_IDX) + return true; + + is_chan_found = dfs_init_agile_start_evt_handler(dfs, + dfs_soc); + if (is_chan_found) { + dfs_soc->cur_agile_dfs_index = dfs->dfs_psoc_idx; + dfs_agile_sm_transition_to(dfs_soc, + DFS_AGILE_S_RUNNING); + } else { + /* + * This happens when there is no preCAC chan + * in any of the radios + */ + dfs_agile_precac_cleanup(dfs); + /* Cleanup and wait */ + } + + status = true; + break; + default: + status = false; + break; + } + + return status; +} + +/** + * dfs_agile_state_running_entry() - Entry API for running state + * @ctx: DFS SoC private object + * + * API to perform operations on moving to running state + * + * Return: void + */ +static void dfs_agile_state_running_entry(void *ctx) +{ + struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; + struct wlan_dfs *dfs = + dfs_soc->dfs_priv[dfs_soc->cur_agile_dfs_index].dfs; + + dfs_agile_set_curr_state(dfs_soc, DFS_AGILE_S_RUNNING); + + /* RCAC */ + if (dfs_is_agile_rcac_enabled(dfs)) { + dfs_start_agile_rcac_timer(dfs); + dfs_start_agile_engine(dfs); + } +} + +/** + * dfs_agile_state_running_exit() - Exit API for RUNNING state + * @ctx: DFS SoC private object + * + * API to perform operations on moving out of RUNNING state + * + * Return: void + */ +static void dfs_agile_state_running_exit(void *ctx) +{ + /* NO OPS */ +} + +/** + * dfs_agile_state_running_event() - RUNNING State event handler + * @ctx: DFS SoC private object + * @event: Event posted to the SM. + * @event_data_len: Length of event data. + * @event_data: Pointer to event data. + * + * API to handle events in RUNNING state + * + * Return: TRUE: on handling event + * FALSE: on ignoring the event + */ +static bool dfs_agile_state_running_event(void *ctx, + uint16_t event, + uint16_t event_data_len, + void *event_data) +{ + struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; + bool status; + struct wlan_dfs *dfs; + bool is_cac_done_on_des_chan; + + if (!event_data) + return false; + + dfs = (struct wlan_dfs *)event_data; + + if (dfs->dfs_psoc_idx != dfs_soc->cur_agile_dfs_index) + return false; + + switch (event) { + case DFS_AGILE_SM_EV_ADFS_RADAR: + /* After radar is found on the Agile channel we need to find + * a new channel and then start Agile CAC on that. + * On receiving the "DFS_AGILE_SM_EV_ADFS_RADAR_FOUND" if + * we change the state from [RUNNING] -> [RUNNING] then + * [RUNNING] should handle case in which a channel is not found + * and bring the state machine back to INIT. + * Instead we move the state to INIT and post the event + * "DFS_AGILE_SM_EV_AGILE_START" so INIT handles the case of + * channel not found and stay in that state. + * Abort the existing RCAC and restart from INIT state. + */ + if (dfs_is_agile_rcac_enabled(dfs)) + dfs_abort_agile_rcac(dfs); + else if (dfs_is_agile_precac_enabled(dfs)) + dfs_abort_agile_precac(dfs); + + dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); + dfs_agile_sm_deliver_event(dfs_soc, + DFS_AGILE_SM_EV_AGILE_START, + event_data_len, + event_data); + + status = true; + break; + case DFS_AGILE_SM_EV_AGILE_STOP: + if (dfs_is_agile_rcac_enabled(dfs)) + dfs_abort_agile_rcac(dfs); + else if (dfs_is_agile_precac_enabled(dfs)) + dfs_abort_agile_precac(dfs); + + dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); + status = true; + break; + case DFS_AGILE_SM_EV_AGILE_DONE: + if (dfs_is_agile_precac_enabled(dfs)) { + if (dfs_soc->ocac_status == OCAC_SUCCESS) { + dfs_soc->ocac_status = OCAC_RESET; + dfs_mark_adfs_chan_as_cac_done(dfs); + } + dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); + dfs_agile_precac_cleanup(dfs); + is_cac_done_on_des_chan = + dfs_precac_check_home_chan_change(dfs); + if (!is_cac_done_on_des_chan) { + dfs_agile_sm_deliver_event(dfs_soc, + DFS_AGILE_SM_EV_AGILE_START, + event_data_len, + event_data); + } + } else if (dfs_is_agile_rcac_enabled(dfs)) { + dfs_agile_sm_transition_to(dfs_soc, + DFS_AGILE_S_COMPLETE); + } + status = true; + break; + default: + status = false; + break; + } + + return status; +} + +/** + * dfs_agile_state_complete_entry() - Entry API for complete state + * @ctx: DFS SoC private object + * + * API to perform operations on moving to complete state + * + * Return: void + */ +static void dfs_agile_state_complete_entry(void *ctx) +{ + struct dfs_soc_priv_obj *dfs_soc_obj = (struct dfs_soc_priv_obj *)ctx; + struct wlan_dfs *dfs; + + dfs_agile_set_curr_state(dfs_soc_obj, DFS_AGILE_S_COMPLETE); + + if (!(dfs_soc_obj->cur_agile_dfs_index < WLAN_UMAC_MAX_PDEVS)) + return; + + dfs = dfs_soc_obj->dfs_priv[dfs_soc_obj->cur_agile_dfs_index].dfs; + + /* Mark the RCAC channel as CAC done. */ + dfs_mark_adfs_chan_as_cac_done(dfs); +} + +/** + * dfs_agile_state_complete_exit() - Exit API for complete state + * @ctx: DFS SoC private object + * + * API to perform operations on moving out of complete state + * + * Return: void + */ +static void dfs_agile_state_complete_exit(void *ctx) +{ + /* NO OPs. */ +} + +/** + * dfs_agile_state_complete_event() - COMPLETE State event handler + * @ctx: DFS SoC private object + * @event: Event posted to the SM. + * @event_data_len: Length of event data. + * @event_data: Pointer to event data. + * + * API to handle events in COMPLETE state + * + * Return: TRUE: on handling event + * FALSE: on ignoring the event + */ +static bool dfs_agile_state_complete_event(void *ctx, + uint16_t event, + uint16_t event_data_len, + void *event_data) +{ + struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; + bool status; + struct wlan_dfs *dfs; + + if (!event_data) + return false; + + dfs = (struct wlan_dfs *)event_data; + + if (dfs->dfs_psoc_idx != dfs_soc->cur_agile_dfs_index) + return false; + + switch (event) { + case DFS_AGILE_SM_EV_ADFS_RADAR: + /* Reset the RCAC done state for this RCAC chan of this dfs. + * Unmark the channels for RCAC done before calling abort API as + * the abort API invalidates the cur_agile_dfs_index. + */ + dfs_unmark_rcac_done(dfs); + /* Abort the existing RCAC and restart from INIT state. */ + dfs_abort_agile_rcac(dfs); + dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); + dfs_agile_sm_deliver_event(dfs_soc, + DFS_AGILE_SM_EV_AGILE_START, + event_data_len, + event_data); + status = true; + break; + case DFS_AGILE_SM_EV_AGILE_STOP: + /* Reset the RCAC done state for this RCAC chan of this dfs. + * Unmark the channels for RCAC done before calling abort API as + * the abort API invalidates the cur_agile_dfs_index. + */ + dfs_unmark_rcac_done(dfs); + dfs_abort_agile_rcac(dfs); + dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); + status = true; + break; + default: + status = false; + break; + } + + return status; +} + +static struct wlan_sm_state_info dfs_agile_sm_info[] = { + { + (uint8_t)DFS_AGILE_S_INIT, + (uint8_t)WLAN_SM_ENGINE_STATE_NONE, + (uint8_t)WLAN_SM_ENGINE_STATE_NONE, + false, + "INIT", + dfs_agile_state_init_entry, + dfs_agile_state_init_exit, + dfs_agile_state_init_event + }, + { + (uint8_t)DFS_AGILE_S_RUNNING, + (uint8_t)WLAN_SM_ENGINE_STATE_NONE, + (uint8_t)WLAN_SM_ENGINE_STATE_NONE, + false, + "RUNNING", + dfs_agile_state_running_entry, + dfs_agile_state_running_exit, + dfs_agile_state_running_event + }, + { + (uint8_t)DFS_AGILE_S_COMPLETE, + (uint8_t)WLAN_SM_ENGINE_STATE_NONE, + (uint8_t)WLAN_SM_ENGINE_STATE_NONE, + false, + "COMPLETE", + dfs_agile_state_complete_entry, + dfs_agile_state_complete_exit, + dfs_agile_state_complete_event + }, +}; + +static const char *dfs_agile_sm_event_names[] = { + "EV_AGILE_START", + "EV_AGILE_STOP", + "EV_AGILE_DONE", + "EV_ADFS_RADAR_FOUND", +}; + +/** + * dfs_agile_sm_print_state() - API to log the current state. + * @dfs_soc_obj: Pointer to dfs soc private object. + * + * Return: void. + */ +static void dfs_agile_sm_print_state(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + enum dfs_agile_sm_state state; + + state = dfs_agile_get_curr_state(dfs_soc_obj); + if (!(state < DFS_AGILE_S_MAX)) + return; + + dfs_debug(NULL, WLAN_DEBUG_DFS_AGILE, "->[%s] %s", + dfs_soc_obj->dfs_agile_sm_hdl->name, + dfs_agile_sm_info[state].name); +} + +/** + * dfs_agile_sm_print_state_event() - API to log the current state and event + * received. + * @dfs_soc_obj: Pointer to dfs soc private object. + * @event: Event posted to RCAC SM. + * + * Return: void. + */ +static void dfs_agile_sm_print_state_event(struct dfs_soc_priv_obj *dfs_soc_obj, + enum dfs_agile_sm_evt event) +{ + enum dfs_agile_sm_state state; + + state = dfs_agile_get_curr_state(dfs_soc_obj); + if (!(state < DFS_AGILE_S_MAX)) + return; + + dfs_debug(NULL, WLAN_DEBUG_DFS_AGILE, "[%s]%s, %s", + dfs_soc_obj->dfs_agile_sm_hdl->name, + dfs_agile_sm_info[state].name, + dfs_agile_sm_event_names[event]); +} + +QDF_STATUS dfs_agile_sm_deliver_evt(struct dfs_soc_priv_obj *dfs_soc_obj, + enum dfs_agile_sm_evt event, + uint16_t event_data_len, + void *event_data) +{ + enum dfs_agile_sm_state old_state, new_state; + QDF_STATUS status; + + DFS_AGILE_SM_SPIN_LOCK(dfs_soc_obj); + old_state = dfs_agile_get_curr_state(dfs_soc_obj); + + /* Print current state and event received */ + dfs_agile_sm_print_state_event(dfs_soc_obj, event); + + status = dfs_agile_sm_deliver_event(dfs_soc_obj, event, + event_data_len, event_data); + + new_state = dfs_agile_get_curr_state(dfs_soc_obj); + + /* Print new state after event if transition happens */ + if (old_state != new_state) + dfs_agile_sm_print_state(dfs_soc_obj); + DFS_AGILE_SM_SPIN_UNLOCK(dfs_soc_obj); + + return status; +} + +QDF_STATUS dfs_agile_sm_create(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + struct wlan_sm *sm; + + sm = wlan_sm_create("DFS_AGILE", dfs_soc_obj, + DFS_AGILE_S_INIT, + dfs_agile_sm_info, + QDF_ARRAY_SIZE(dfs_agile_sm_info), + dfs_agile_sm_event_names, + QDF_ARRAY_SIZE(dfs_agile_sm_event_names)); + if (!sm) { + qdf_err("DFS AGILE SM allocation failed"); + return QDF_STATUS_E_FAILURE; + } + dfs_soc_obj->dfs_agile_sm_hdl = sm; + + qdf_spinlock_create(&dfs_soc_obj->dfs_agile_sm_lock); + + /* Initialize the RCAC DFS index to default (no index). */ + dfs_soc_obj->cur_agile_dfs_index = DFS_PSOC_NO_IDX; + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dfs_agile_sm_destroy(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + wlan_sm_delete(dfs_soc_obj->dfs_agile_sm_hdl); + qdf_spinlock_destroy(&dfs_soc_obj->dfs_agile_sm_lock); + + return QDF_STATUS_SUCCESS; +} + +#ifdef QCA_SUPPORT_ADFS_RCAC +QDF_STATUS dfs_set_rcac_enable(struct wlan_dfs *dfs, bool rcac_en) +{ + if (rcac_en == dfs->dfs_agile_rcac_ucfg) { + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Rolling CAC: %d is already configured", rcac_en); + return QDF_STATUS_SUCCESS; + } + dfs->dfs_agile_rcac_ucfg = rcac_en; + + /* RCAC config is changed. Reset the preCAC tree. */ + dfs_reset_precac_lists(dfs); + + if (!rcac_en) { + dfs_agile_sm_deliver_evt(dfs->dfs_soc_obj, + DFS_AGILE_SM_EV_AGILE_STOP, + 0, + (void *)dfs); + } + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "rolling cac is %d", rcac_en); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dfs_get_rcac_enable(struct wlan_dfs *dfs, bool *rcacen) +{ + *rcacen = dfs->dfs_agile_rcac_ucfg; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dfs_set_rcac_freq(struct wlan_dfs *dfs, qdf_freq_t rcac_freq) +{ + if (wlan_reg_is_5ghz_ch_freq(rcac_freq)) + dfs->dfs_agile_rcac_freq_ucfg = rcac_freq; + else + dfs->dfs_agile_rcac_freq_ucfg = 0; + + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "rolling cac freq %d", + dfs->dfs_agile_rcac_freq_ucfg); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dfs_get_rcac_freq(struct wlan_dfs *dfs, qdf_freq_t *rcac_freq) +{ + *rcac_freq = dfs->dfs_agile_rcac_freq_ucfg; + + return QDF_STATUS_SUCCESS; +} + +/* + * Rolling CAC Timer timeout function. Following actions are done + * on timer expiry: + * Timer running flag is cleared. + * If the rolling CAC state is completed, the RCAC freq and its sub-channels + * are marked as 'CAC Done' in the preCAC tree. + */ +static os_timer_func(dfs_rcac_timeout) +{ + struct wlan_dfs *dfs; + struct dfs_soc_priv_obj *dfs_soc_obj; + + OS_GET_TIMER_ARG(dfs_soc_obj, struct dfs_soc_priv_obj *); + + dfs = dfs_soc_obj->dfs_priv[dfs_soc_obj->cur_agile_dfs_index].dfs; + + dfs_agile_sm_deliver_evt(dfs_soc_obj, + DFS_AGILE_SM_EV_AGILE_DONE, + 0, + (void *)dfs); +} + +void dfs_rcac_timer_init(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + qdf_timer_init(NULL, &dfs_soc_obj->dfs_rcac_timer, + dfs_rcac_timeout, + (void *)dfs_soc_obj, + QDF_TIMER_TYPE_WAKE_APPS); +} + +void dfs_rcac_timer_deinit(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + qdf_timer_free(&dfs_soc_obj->dfs_rcac_timer); +} + +/* dfs_prepare_agile_rcac_channel() - Find a valid Rolling CAC channel if + * available. + * + * @dfs: Pointer to struct wlan_dfs. + * @is_rcac_chan_available: Flag to indicate if a valid RCAC channel is + * available. + */ +void dfs_prepare_agile_rcac_channel(struct wlan_dfs *dfs, + bool *is_rcac_chan_available) +{ + qdf_freq_t rcac_ch_freq = 0; + + /* Find out a valid rcac_ch_freq */ + dfs_set_agilecac_chan_for_freq(dfs, &rcac_ch_freq, 0, 0); + + /* If RCAC channel is available, the caller will start the timer and + * send RCAC config to FW. If channel not available, the caller takes + * care of sending RCAC abort and moving SM to INIT, resetting the RCAC + * variables. + */ + *is_rcac_chan_available = rcac_ch_freq ? true : false; + dfs_debug(dfs, WLAN_DEBUG_DFS_AGILE, "Chosen rcac channel: %d", + rcac_ch_freq); +} +#endif +#endif diff --git a/umac/dfs/core/src/misc/dfs_precac_list.c b/umac/dfs/core/src/misc/dfs_precac_list.c new file mode 100644 index 0000000000..57eb887e0d --- /dev/null +++ b/umac/dfs/core/src/misc/dfs_precac_list.c @@ -0,0 +1,1871 @@ +/* + * Copyright (c) 2016-2020 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 Precac Tree structure functions. + * + */ + +#include "../dfs_precac_forest.h" +#include +#include +#include +#include + +/* dfs_calculate_bw_for_same_pri_ch() - When the primary channel is a new + * channel initialize the center channel frequency and bandwidth and when the + * primary is same as previous channel update the center frequency and the + * bandwidth. + * @dfs: WLAN DFS structure + * @dfs_max_bw_info: Structure to store precac tree root channel's + * information. + * @index: The index that is manipulated. + * @ichan: The DFS channel structure that holds the primary channel + * number, center frquency and channel bandwidth. + * @delimiter: Band gap in MHz from the current primary channel to next + * primary channel. + */ +static void +dfs_calculate_bw_for_same_pri_ch(struct wlan_dfs *dfs, + struct dfs_channel_bw *dfs_max_bw_info, + int index, + struct dfs_channel *ichan, + int *delimiter) +{ + uint8_t temp_bw = 0; + uint16_t tmp_center_freq; + + dfs_max_bw_info[index].dfs_pri_ch_freq = ichan->dfs_ch_freq; + tmp_center_freq = ichan->dfs_ch_mhz_freq_seg1; + + if (WLAN_IS_CHAN_MODE_20(ichan)) { + temp_bw = DFS_CHWIDTH_20_VAL; + } else if (WLAN_IS_CHAN_MODE_40(ichan)) { + temp_bw = DFS_CHWIDTH_40_VAL; + } else if (WLAN_IS_CHAN_MODE_80(ichan) || + WLAN_IS_CHAN_MODE_80_80(ichan)) { + temp_bw = DFS_CHWIDTH_80_VAL; + if (dfs_is_restricted_80p80mhz_supported(dfs) && + WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(ichan) && + (ichan->dfs_ch_vhtop_ch_freq_seg1 == + RESTRICTED_80P80_LEFT_80_CENTER_CHAN) && + (ichan->dfs_ch_vhtop_ch_freq_seg2 == + RESTRICTED_80P80_RIGHT_80_CENTER_CHAN)) { + temp_bw = DFS_CHWIDTH_165_VAL; + tmp_center_freq = RESTRICTED_80P80_CHAN_CENTER_FREQ; + } + } else if (WLAN_IS_CHAN_MODE_160(ichan)) { + temp_bw = DFS_CHWIDTH_160_VAL; + tmp_center_freq = ichan->dfs_ch_mhz_freq_seg2; + } + if (temp_bw > dfs_max_bw_info[index].dfs_max_bw) { + dfs_max_bw_info[index].dfs_max_bw = temp_bw; + *delimiter = dfs_max_bw_info[index].dfs_pri_ch_freq + + dfs_max_bw_info[index].dfs_max_bw; + dfs_max_bw_info[index].dfs_center_ch_freq = tmp_center_freq; + } +} + +/* dfs_init_precac_tree_node() - Initialise the preCAC BSTree node with the + * provided values. + * @node: Precac_tree_node to be filled. + * @freq: IEEE channel freq value. + * @bandwidth: Bandwidth of the channel. + * @depth: Depth of the tree. The depth of the tree when the root is 160MHz + * channel is 4, 80MHz is 3, 40MHz is 2 and 20MHz is 1. + */ +static inline void +dfs_init_precac_tree_node_for_freq(struct precac_tree_node *node, + uint16_t freq, + uint8_t bandwidth, + uint8_t depth) +{ + node->left_child = NULL; + node->right_child = NULL; + node->ch_freq = freq; + node->ch_ieee = utils_dfs_freq_to_chan(freq); + node->n_caced_subchs = 0; + node->n_nol_subchs = 0; + node->n_valid_subchs = N_SUBCHS_FOR_BANDWIDTH(bandwidth); + node->bandwidth = bandwidth; + node->depth = depth; + +} + +/* dfs_descend_precac_tree_for_freq() - Descend into the precac BSTree based on + * the channel provided. If the channel is less than + * given node's channel, descend left, else right. + * @node: Precac BSTree node. + * @chan_freq: Channel freq whose node is to be found. + * + * Return: the next precac_tree_node (left child or right child of + * current node). + */ + +struct precac_tree_node * +dfs_descend_precac_tree_for_freq(struct precac_tree_node *node, + uint16_t chan_freq) +{ + if (!node) + return NULL; + + if (chan_freq < node->ch_freq) + return node->left_child; + else + return node->right_child; +} + +/*dfs_insert_node_into_bstree_for_freq() - Insert a new preCAC BSTree node. + * @root: The preCAC BSTree root pointer. + * @chan: IEEE freq of the new node. + * @bandwidth: Bandwidth of the channel. + * @depth: Depth of the tree. The depth of the tree when the root is 160MHz + * channel is 4, 80MHz is 3, 40MHz is 2 and 20MHz is 1. + * + * Return: EOK if new node is allocated, else return ENOMEM. + */ +static QDF_STATUS +dfs_insert_node_into_bstree_for_freq(struct precac_tree_node **root, + uint16_t chan_freq, + uint8_t bandwidth, + uint8_t depth) +{ + struct precac_tree_node *new_node = NULL; + struct precac_tree_node *curr_node, *prev_node = NULL; + QDF_STATUS status = EOK; + + new_node = qdf_mem_malloc(sizeof(*new_node)); + if (!new_node) + return -ENOMEM; + dfs_init_precac_tree_node_for_freq(new_node, + chan_freq, + bandwidth, + depth); + + /* If root node is null, assign the newly allocated node + * to this node and return. + */ + if (!(*root)) { + *root = new_node; + return status; + } + + curr_node = *root; + /* Find the leaf node which will be the new node's parent */ + while (curr_node) { + prev_node = curr_node; + curr_node = dfs_descend_precac_tree_for_freq(curr_node, + chan_freq); + } + + /* Add to the leaf node */ + if (chan_freq < prev_node->ch_freq) + prev_node->left_child = new_node; + else + prev_node->right_child = new_node; + + return status; +} + +/* dfs_find_leftmost_leaf_of_precac_tree() - Find the leftmost leaf node of + * BSTree rooted by the given node. + * @node: PreCAC BSTree node whose leftmost leaf is required. + * + * Return: Pointer of struct precac_tree_node. + */ +static inline struct precac_tree_node * +dfs_find_leftmost_leaf_of_precac_tree(struct precac_tree_node *node) +{ + if (!node) + return NULL; + + while (node->left_child) + node = node->left_child; + + return node; +} + +/* + * dfs_free_precac_tree_nodes() - Free the tree nodes starting from + * the root node. + * NOTE: This changes tree structure, hence + * caller should be in a lock. + * @dfs: Pointer to WLAN DFS structure. + * @precac_entry: Precac list entry whose BSTree is to be freed. + * + * Consider the below Binary tree, + * + * A + * / \ + * B C + * \ + * D + * + * Steps for freeing this tree, + * + * 1. Find the leftmost leaf node of the Binary Tree. + * 2. Set current node as root node. + * 3. If current node has right child, add right child of current node as left + * child of leftmost leaf. + * 4. Update the leftmost leaf. + * 5. Update current node to left child and remove the node. + * 6. Repeat steps 3 to 5 till current node is NULL. + * + * The above Binary Tree structure during the afore mentioned steps: + * + * A A + * / \ / + * B C --> B --> B --> B --> C --> D --> . + * \ / \ / \ / / + * D C D C D C D + * / + * D + * + */ + +static void dfs_free_precac_tree_nodes(struct wlan_dfs *dfs, + struct dfs_precac_entry *precac_entry) +{ + struct precac_tree_node *root_node, *left_most_leaf, *prev_root_node; + + root_node = precac_entry->tree_root; + if (!root_node) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tree root is null"); + return; + } + + /* Find leftmost leaf node */ + left_most_leaf = root_node; + left_most_leaf = dfs_find_leftmost_leaf_of_precac_tree(left_most_leaf); + if (!left_most_leaf) { + /* should've been caught in previous check, assert here */ + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Could not find leaf, deletion failed! Asserting"); + QDF_ASSERT(0); + return; + } + + while (root_node) { + if (root_node->right_child) { + /* Add the right subtree as the left child of the + * leftmost leaf + */ + left_most_leaf->left_child = root_node->right_child; + /* Update left most leaf */ + left_most_leaf = dfs_find_leftmost_leaf_of_precac_tree( + left_most_leaf); + if (!left_most_leaf) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Could not find leaf, deletion failed"); + QDF_ASSERT(0); + return; + } + } + /* Free current node */ + prev_root_node = root_node; + root_node = root_node->left_child; + qdf_mem_free(prev_root_node); + } +} + +/** + * dfs_get_num_cur_subchans_in_node_freq() - Get number of excluded channels + * inside the current node. + * @dfs: Pointer to wlan_dfs structure. + * @node: Node to be checked. + * + * Return: uint8_t. + * Return the number of excluded (current operating channels in CAC) that are in + * the given tree node range. + */ +static uint8_t +dfs_get_num_cur_subchans_in_node_freq(struct wlan_dfs *dfs, + struct precac_tree_node *node) +{ + uint16_t exclude_pri_ch_freq, exclude_sec_ch_freq; + uint8_t chwidth_val = DFS_CHWIDTH_80_VAL; + uint8_t n_exclude_subchs = 0; + + exclude_pri_ch_freq = + dfs->dfs_curchan->dfs_ch_mhz_freq_seg1; + exclude_sec_ch_freq = + dfs->dfs_curchan->dfs_ch_mhz_freq_seg2; + if (WLAN_IS_CHAN_MODE_160(dfs->dfs_curchan)) { + if (exclude_sec_ch_freq < exclude_pri_ch_freq) + exclude_sec_ch_freq -= + DFS_160MHZ_SECSEG_CHAN_OFFSET; + else + exclude_sec_ch_freq += + DFS_160MHZ_SECSEG_CHAN_OFFSET; + } + + if (WLAN_IS_CHAN_MODE_20(dfs->dfs_curchan)) + chwidth_val = DFS_CHWIDTH_20_VAL; + else if (WLAN_IS_CHAN_MODE_40(dfs->dfs_curchan)) + chwidth_val = DFS_CHWIDTH_40_VAL; + + /* Check if the channel is a subset of the tree node and if it's + * currently in CAC period. This is to avoid excluding channels twice, + * one below and one in the already CACed channels exclusion (in the + * caller API). + */ + if (IS_WITHIN_RANGE(exclude_pri_ch_freq, + node->ch_freq, + (node->bandwidth / 2)) && + dfs_is_pcac_required_for_freq(node, exclude_pri_ch_freq)) + n_exclude_subchs += N_SUBCHS_FOR_BANDWIDTH(chwidth_val); + if (IS_WITHIN_RANGE(exclude_sec_ch_freq, + node->ch_freq, + (node->bandwidth / 2)) && + dfs_is_pcac_required_for_freq(node, exclude_sec_ch_freq)) + n_exclude_subchs += N_SUBCHS_FOR_BANDWIDTH(chwidth_val); + return n_exclude_subchs; +} + +/* dfs_is_cac_needed_for_bst_node_for_freq() - For a requested bandwidth, find + * if the current preCAC BSTree + * node needs CAC. + * @dfs: Pointer to wlan_dfs struct. + * @node: Node to be checked. + * @req_bandwidth: bandwidth of channel requested. + * + * Return: TRUE/FALSE. + * Return true if there exists a channel of the requested bandwidth + * for the node which is not CAC done, else false. + */ +static bool +dfs_is_cac_needed_for_bst_node_for_freq(struct wlan_dfs *dfs, + struct precac_tree_node *node, + uint8_t req_bandwidth) +{ + uint8_t n_subchs_for_req_bw, n_allowed_subchs, n_excluded_subchs; + + if (!node) + return false; + + /* Find the number of subchannels for the requested bandwidth */ + n_excluded_subchs = dfs_get_num_cur_subchans_in_node_freq(dfs, node); + n_subchs_for_req_bw = N_SUBCHS_FOR_BANDWIDTH(req_bandwidth); + n_allowed_subchs = node->n_valid_subchs - + (node->n_nol_subchs + n_excluded_subchs); + + /* Return false if, + * 1. Number of allowed subchannels (all subchannels other than + * current operating sub-channels and NOL sub-channels) in the + * current node is less than the requested number of subchannels. + * 3. If the number CAC done subchannels + NOL subchannels + current + * operating subchannels in the current node is equal to number of + * valid subchannels in the node. + * else, return true. + */ + if ((n_allowed_subchs < n_subchs_for_req_bw) || + ((node->n_caced_subchs + node->n_nol_subchs + n_excluded_subchs) == + node->n_valid_subchs)) + return false; + + return true; +} + +/* dfs_create_precac_tree_for_freq() - Fill precac entry tree (level insertion). + * @dfs: WLAN DFS structure + * @ch_freq: root_node freq. + * @root: Pointer to the node that will be filled and inserted as tree + * root. + * @bandwidth: Bandwidth value of the root. + */ +static QDF_STATUS +dfs_create_precac_tree_for_freq(struct wlan_dfs *dfs, + uint16_t ch_freq, + struct precac_tree_node **root, + int bandwidth) +{ + int chan_freq, i; + QDF_STATUS status = EOK; + struct precac_tree_offset_for_different_bw current_mode; + uint8_t top_lvl_step; + bool is_node_part_of_165_tree = false; + + if (ch_freq == RESTRICTED_80P80_LEFT_80_CENTER_FREQ || + ch_freq == RESTRICTED_80P80_RIGHT_80_CENTER_FREQ) + is_node_part_of_165_tree = true; + + switch (bandwidth) { + case DFS_CHWIDTH_160_VAL: + current_mode = offset160; + break; + case DFS_CHWIDTH_80_VAL: + current_mode = offset80; + break; + case DFS_CHWIDTH_40_VAL: + current_mode = offset40; + break; + case DFS_CHWIDTH_20_VAL: + current_mode = offset20; + break; + default: + current_mode = default_offset; + break; + } + top_lvl_step = current_mode.initial_and_next_offsets[0][1]; + for (i = 0; i < current_mode.tree_depth; i++) { + /* In offset array, + * column 0 is initial chan offset, + * column 1 is next chan offset. + * Boundary offset is initial offset and next offset + * of root level (since root level can have only 1 node) + */ + int offset = + current_mode.initial_and_next_offsets[i][START_INDEX]; + int step = current_mode.initial_and_next_offsets[i][STEP_INDEX]; + int boundary_offset = offset + top_lvl_step; + uint8_t depth = is_node_part_of_165_tree ? i + 1 : i; + + for (; offset < boundary_offset; offset += step) { + chan_freq = (int)ch_freq + offset; + status = + dfs_insert_node_into_bstree_for_freq(root, + chan_freq, + bandwidth, + depth); + if (status) + return status; + } + bandwidth /= 2; + } + + return status; +} + +static QDF_STATUS +dfs_precac_create_165mhz_precac_entry(struct wlan_dfs *dfs, + struct dfs_precac_entry *precac_entry) +{ + QDF_STATUS status; + + precac_entry->center_ch_freq = + RESTRICTED_80P80_CHAN_CENTER_FREQ; + precac_entry->center_ch_ieee = + utils_dfs_freq_to_chan(precac_entry->center_ch_freq); + precac_entry->bw = DFS_CHWIDTH_160_VAL; + /* non_dfs_subch_count will be updated once the channels are marked. */ + precac_entry->non_dfs_subch_count = 0; + precac_entry->dfs = dfs; + dfs_insert_node_into_bstree_for_freq(&precac_entry->tree_root, + RESTRICTED_80P80_CHAN_CENTER_FREQ, + DFS_CHWIDTH_160_VAL, + DEPTH_160_ROOT); + status = + dfs_create_precac_tree_for_freq + (dfs, + RESTRICTED_80P80_LEFT_80_CENTER_FREQ, + &precac_entry->tree_root->left_child, + DFS_CHWIDTH_80_VAL); + if (!status) + status = + dfs_create_precac_tree_for_freq( + dfs, + RESTRICTED_80P80_RIGHT_80_CENTER_FREQ, + &precac_entry->tree_root->right_child, + DFS_CHWIDTH_80_VAL); + TAILQ_INSERT_TAIL( + &dfs->dfs_precac_list, + precac_entry, pe_list); + return status; +} + +/* dfs_is_tree_node_marked_as_cac_for_freq() - Check if preCAC BSTree node is + * marked as CAC. + * @root: Pointer to root node of the preCAC BSTree. + * @freq: 20MHz channel to be checked if marked as CAC done already. + * + * Return: True if already marked, else false. + */ +static bool +dfs_is_tree_node_marked_as_cac_for_freq(struct precac_tree_node *root, + uint16_t freq) +{ + struct precac_tree_node *curr_node = root; + + while (curr_node) { + if (!curr_node->n_caced_subchs) + return false; + if (curr_node->ch_freq == freq) + return curr_node->n_caced_subchs; + curr_node = dfs_descend_precac_tree_for_freq(curr_node, + freq); + } + return false; +} + +/* dfs_unmark_tree_node_as_nol_for_freq() - Unmark the preCAC BSTree node as + * NOL. + * @dfs: Pointer to WLAN DFS structure. + * @precac_entry: Precac_list entry pointer. + * @chan_freq IEEE channel freq to be marked. + * + * Note: The input channel is always of 20MHz bandwidth. + */ + +static void +dfs_unmark_tree_node_as_nol_for_freq(struct wlan_dfs *dfs, + struct dfs_precac_entry *precac_entry, + uint16_t chan_freq) +{ + struct precac_tree_node *curr_node; + + if (!precac_entry->tree_root) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Precac tree root pointer is NULL!"); + return; + } + curr_node = precac_entry->tree_root; + while (curr_node) { + if (curr_node->n_nol_subchs) + curr_node->n_nol_subchs--; + else + return; + curr_node = dfs_descend_precac_tree_for_freq(curr_node, + chan_freq); + } +} + +/** + * dfs_update_non_dfs_subchannel_count() - API to update the preCAC entry + * with the given non DFS subchannel count. + * @dfs: Pointer to DFS object. + * @frequency: Frequency whose corresponding preCAC entry needs to be updated. + * @count: Non DFS subchannel count for the preCAC entry. + */ +static void +dfs_update_non_dfs_subchannel_count(struct wlan_dfs *dfs, + qdf_freq_t frequency, + uint8_t count) +{ + struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; + + PRECAC_LIST_LOCK(dfs); + TAILQ_FOREACH_SAFE(precac_entry, + &dfs->dfs_precac_list, + pe_list, + tmp_precac_entry) { + if (IS_WITHIN_RANGE_STRICT(frequency, + precac_entry->center_ch_freq, + (precac_entry->bw/2))) { + precac_entry->non_dfs_subch_count = count; + break; + } + } + PRECAC_LIST_UNLOCK(dfs); +} + +static void +dfs_mark_non_dfs_as_precac_done(struct wlan_dfs *dfs, + uint16_t dfs_pri_ch_freq, + enum wlan_phymode mode) +{ + struct dfs_channel *ichan, lc; + + ichan = &lc; + dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj, + dfs_pri_ch_freq, + 0, + mode, + &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, + &ichan->dfs_ch_mhz_freq_seg1, + &ichan->dfs_ch_mhz_freq_seg2); + if (!WLAN_IS_CHAN_DFS(ichan)) { + PRECAC_LIST_UNLOCK(dfs); + dfs_mark_precac_done_for_freq(dfs, + ichan->dfs_ch_mhz_freq_seg1, + 0, + CH_WIDTH_80MHZ); + dfs_update_non_dfs_subchannel_count(dfs, + ichan->dfs_ch_mhz_freq_seg1, + N_SUBCHANS_FOR_80BW); + PRECAC_LIST_LOCK(dfs); + } else if (!WLAN_IS_CHAN_DFS_CFREQ2(ichan)) { + PRECAC_LIST_UNLOCK(dfs); + dfs_mark_precac_done_for_freq(dfs, + ichan->dfs_ch_mhz_freq_seg2, + 0, + CH_WIDTH_80MHZ); + dfs_update_non_dfs_subchannel_count(dfs, + ichan->dfs_ch_mhz_freq_seg2, + N_SUBCHANS_FOR_80BW); + PRECAC_LIST_LOCK(dfs); + } +} + +static QDF_STATUS +dfs_precac_create_precac_entry(struct wlan_dfs *dfs, + struct dfs_precac_entry *precac_entry, + struct dfs_channel_bw *dfs_max_bw_info, + int index) +{ + QDF_STATUS status; + uint16_t precac_center_freq = + dfs_max_bw_info[index].dfs_center_ch_freq; + + precac_entry->center_ch_freq = precac_center_freq; + precac_entry->center_ch_ieee = + utils_dfs_freq_to_chan(precac_center_freq); + precac_entry->bw = dfs_max_bw_info[index].dfs_max_bw; + /* non_dfs_subch_count will be updated once the channels are marked. */ + precac_entry->non_dfs_subch_count = 0; + precac_entry->dfs = dfs; + status = + dfs_create_precac_tree_for_freq(dfs, + precac_entry->center_ch_freq, + &precac_entry->tree_root, + precac_entry->bw); + if (status) + dfs_debug(dfs, WLAN_DEBUG_DFS, + "PreCAC entry for channel %d not created", + precac_entry->center_ch_ieee); + else + TAILQ_INSERT_TAIL(&dfs->dfs_precac_list, precac_entry, pe_list); + + return status; +} + +/* dfs_mark_tree_node_as_cac_done_for_freq() - Mark the preCAC BSTree node as + * CAC done. + * @dfs: Pointer to WLAN DFS structure. + * @precac_entry: Precac_list entry pointer. + * @chan_freq: IEEE channel freq to be marked. + * + * Note: The input channel is always of 20MHz bandwidth. + */ +static void +dfs_mark_tree_node_as_cac_done_for_freq(struct wlan_dfs *dfs, + struct dfs_precac_entry *precac_entry, + uint16_t chan_freq) +{ + struct precac_tree_node *curr_node; + + if (!precac_entry->tree_root) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Precac tree root pointer is NULL!"); + return; + } + + curr_node = precac_entry->tree_root; + /** + * Check if the channel is already marked and return if true. + * This will happen in scenarios like the following: + * preCAC is running on channel 128 in HT20 mode (note: 124 is already + * marked. Now if the mode is switched to HT40, preCAC is restarted + * and the new channel picked for preCAC is 126 HT40. Here, 124 + * will be already marked since it was completed in HT20 mode. + * This may happen for any mode switches (20<->40<->80 MHz). + */ + if (dfs_is_tree_node_marked_as_cac_for_freq(curr_node, chan_freq)) + return; + + while (curr_node) { + /* Update the current node's CACed subchannels count only + * if it's less than maximum subchannels, else return. + */ + if (curr_node->n_caced_subchs < + N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth)) + curr_node->n_caced_subchs++; + curr_node = dfs_descend_precac_tree_for_freq(curr_node, + chan_freq); + } +} + +/* dfs_unmark_tree_node_as_cac_done_for_freq() - Unmark the preCAC BSTree + * node as CAC done. + * @precac_entry: Precac_list entry pointer. + * @chan_freq: IEEE channel freq to be marked. + * + * Note: The input channel is always of 20MHz bandwidth. + */ +static void +dfs_unmark_tree_node_as_cac_done_for_freq(struct dfs_precac_entry + *precac_entry, uint16_t chan_freq) +{ + struct precac_tree_node *curr_node = precac_entry->tree_root; + + while (curr_node) { + if (curr_node->n_caced_subchs) + curr_node->n_caced_subchs--; + else + return; + curr_node = dfs_descend_precac_tree_for_freq(curr_node, + chan_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_LIST_LOCK(dfs); + if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) + TAILQ_FOREACH_SAFE(precac_entry, + &dfs->dfs_precac_list, + pe_list, tmp_precac_entry) { + dfs_free_precac_tree_nodes(dfs, precac_entry); + TAILQ_REMOVE(&dfs->dfs_precac_list, + precac_entry, pe_list); + qdf_mem_free(precac_entry); + } + PRECAC_LIST_UNLOCK(dfs); + +} + +/* dfs_mark_tree_node_as_nol_for_freq() - Mark the preCAC BSTree node as NOL. + * @dfs: Pointer to WLAN DFS structure. + * @precac_entry: Precac_list entry pointer. + * @freq: IEEE channel freq to be marked. + * + * Note: The input channel is always of 20MHz bandwidth. + */ +static void +dfs_mark_tree_node_as_nol_for_freq(struct wlan_dfs *dfs, + struct dfs_precac_entry *pcac, + uint16_t freq) +{ + struct precac_tree_node *curr_node; + + if (!pcac->tree_root) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Precac tree root pointer is NULL!"); + return; + } + curr_node = pcac->tree_root; + while (curr_node) { + if (curr_node->n_nol_subchs < + N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth)) { + curr_node->n_nol_subchs++; + } else { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "Radarfound on an already marked NOL channel!"); + return; + } + if (freq == curr_node->ch_freq) { + if (curr_node->n_caced_subchs) { + /* remove cac done status for this node + * and it's parents, since this node + * now requires cac (after NOL expiry) + */ + dfs_unmark_tree_node_as_cac_done_for_freq(pcac, + freq); + } + } + curr_node = dfs_descend_precac_tree_for_freq(curr_node, + freq); + } +} + +/* dfs_fill_max_bw_for_chan() - Finds unique precac tree node in the channel + * list and stores the primary channel frequency, maximum bandwidth and the + * center frequency. The algorithm is based on the data structure ic_channels + * where the channels are organized as 36HT20, 36HT40, 36HT80,... and so on.. + * @dfs: WLAN DFS structure + * @dfs_max_bw_info: Structure to store precac tree root channel's + * information. + * @num_precac_roots: Number of unique. + */ +static void dfs_fill_max_bw_for_chan(struct wlan_dfs *dfs, + struct dfs_channel_bw *dfs_max_bw_info, + int *num_precac_roots) +{ + int i; + int n_total_chans = 0; + int n_chanseg_found = 0; + int prev_ch_freq = 0; + int delimiter = 0; + + dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &n_total_chans); + for (i = 0; i < n_total_chans; i++) { + struct dfs_channel *ichan = NULL, lc; + /* The array index of the bandwidth list that needs to be + * updated. + */ + int index_to_update; + + ichan = &lc; + dfs_mlme_get_dfs_channels_for_freq + (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, + &ichan->dfs_ch_mhz_freq_seg1, + &ichan->dfs_ch_mhz_freq_seg2, + i); + if (!WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(ichan)) + continue; + if (ichan->dfs_ch_freq == prev_ch_freq) { + /* When the primary channels are common for consecutive + * channels, for example 36HT20, 36HT40, 36HT80,..., + * only the center frequecy and the bandwidth have to be + * updated. + */ + index_to_update = n_chanseg_found - 1; + dfs_calculate_bw_for_same_pri_ch(dfs, + dfs_max_bw_info, + index_to_update, + ichan, + &delimiter); + } else if (ichan->dfs_ch_freq < delimiter) { + continue; + } else { + prev_ch_freq = ichan->dfs_ch_freq; + /* When the primary channels are unique and consecutive + * like 149HT20, 153HT20, 157HT20,..., the new element + * has to be initialized here. + */ + index_to_update = n_chanseg_found; + dfs_calculate_bw_for_same_pri_ch(dfs, + dfs_max_bw_info, + n_chanseg_found, + ichan, + &delimiter); + n_chanseg_found++; + } + } + *num_precac_roots = n_chanseg_found; + for (i = 0; i < *num_precac_roots; i++) + dfs_debug(dfs, WLAN_DEBUG_DFS, + "index = %d pri: %d centr: %d bw: %d", + i, + dfs_max_bw_info[i].dfs_pri_ch_freq, + dfs_max_bw_info[i].dfs_center_ch_freq, + dfs_max_bw_info[i].dfs_max_bw); +} + +/* dfs_find_cac_status_for_chan_for_freq() - Find CAC-done status for the + * channel in the precac Binary Search Tree. + * Return true if CAC done, else false. + * @dfs_precac_entry: Precac entry which has the root of the precac BSTree. + * @chan_freq: IEEE channel freq. This is the center of a + * 20/40/80/160/165 MHz channel and the center channel is + * unique irrespective of the bandwidth + * (20/40/80/160/165 MHz). + */ +static bool +dfs_find_cac_status_for_chan_for_freq(struct dfs_precac_entry *precac_entry, + uint16_t chan_freq) +{ + struct precac_tree_node *node = precac_entry->tree_root; + uint8_t n_cur_lvl_subchs = node->n_valid_subchs; + + while (node) { + if (node->ch_freq == chan_freq) + return (node->n_caced_subchs == n_cur_lvl_subchs) ? + true : false; + + n_cur_lvl_subchs /= 2; + node = dfs_descend_precac_tree_for_freq(node, chan_freq); + } + + return false; +} + +/* dfs_find_ieee_ch_from_precac_tree_for_freq() - from the given preCAC tree, + * find a IEEE freq of the given bandwidth + * which is valid and needs CAC. + * @root: PreCAC BSTree root pointer. + * @req_bw: Bandwidth of channel requested. + * + * Return: IEEE channel frequency. + * Return a valid freq value which needs CAC for the given bandwidth, else + * return 0. + */ +static uint16_t +dfs_find_ieee_ch_from_precac_tree_for_freq(struct wlan_dfs *dfs, + struct precac_tree_node *root, + uint8_t req_bw) +{ + struct precac_tree_node *curr_node; + + if (!dfs_is_cac_needed_for_bst_node_for_freq(dfs, root, req_bw)) + return 0; + + curr_node = root; + while (curr_node) { + if (curr_node->bandwidth == req_bw) { + /* find if current node in valid state (req.) */ + if (dfs_is_cac_needed_for_bst_node_for_freq(dfs, + curr_node, + req_bw)) + return curr_node->ch_freq; + else + return 0; + } + + /* Find if we need to go to left or right subtree. + * Note: If both are available, go to left. + */ + if (!dfs_is_cac_needed_for_bst_node_for_freq( + dfs, + curr_node->left_child, + req_bw)) + curr_node = curr_node->right_child; + else + curr_node = curr_node->left_child; + } + /* If requested bandwidth is invalid, return 0 here */ + return 0; +} + +/** + * dfs_find_precac_state_of_node() - Find the preCAC state of the given channel. + * @channel: Channel whose preCAC state is to be found. + * @precac_entry: PreCAC entry where the channel exists. + * + * Return, enum value of type precac_chan_state. + */ +enum precac_chan_state +dfs_find_precac_state_of_node(qdf_freq_t channel, + struct dfs_precac_entry *precac_entry) +{ + struct precac_tree_node *node = precac_entry->tree_root; + + while (node) { + if (node->ch_freq == channel) { + if (node->n_nol_subchs) + return PRECAC_NOL; + if (node->n_caced_subchs == + N_SUBCHS_FOR_BANDWIDTH(node->bandwidth)) + return PRECAC_DONE; + return PRECAC_REQUIRED; + } + node = dfs_descend_precac_tree_for_freq(node, channel); + } + return PRECAC_ERR; +} + +/* + * dfs_get_ieeechan_for_precac_for_freq() - Get chan frequency for preCAC. + * @dfs: Pointer to wlan_dfs. + * @exclude_pri_ch_freq: Primary frequency to be excluded. + * @exclude_sec_ch_freq: Secondary freqeuncy to be excluded. + * @bandwidth: Bandwidth. + */ +uint16_t dfs_get_ieeechan_for_precac_for_freq(struct wlan_dfs *dfs, + uint16_t exclude_pri_ch_freq, + uint16_t exclude_sec_ch_freq, + uint8_t bw) +{ + struct dfs_precac_entry *precac_entry; + struct precac_tree_node *root = NULL; + uint16_t ieee_chan_freq = 0; + + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, + "current operating channel(s) to be excluded = [%u] [%u]", + exclude_pri_ch_freq, + exclude_sec_ch_freq); + + /* If interCAC is enabled, prioritize the desired channel first before + * using the normal logic to find a channel for preCAC. + */ + ieee_chan_freq = dfs_configure_deschan_for_precac(dfs); + + if (ieee_chan_freq) + goto exit; + + PRECAC_LIST_LOCK(dfs); + if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) { + TAILQ_FOREACH(precac_entry, &dfs->dfs_precac_list, + pe_list) { + root = precac_entry->tree_root; + ieee_chan_freq = + dfs_find_ieee_ch_from_precac_tree_for_freq(dfs, + root, + bw); + if (ieee_chan_freq) + break; + } + } + PRECAC_LIST_UNLOCK(dfs); + +exit: + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "Channel picked for preCAC = %u", + ieee_chan_freq); + + return ieee_chan_freq; +} + +#ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT +#ifdef CONFIG_CHAN_FREQ_API +enum precac_chan_state +dfs_get_precac_chan_state_for_freq(struct wlan_dfs *dfs, uint16_t pcac_freq) +{ + struct dfs_channel chan; + struct dfs_precac_entry *tmp_precac_entry; + struct precac_tree_node *root = NULL; + enum precac_chan_state ret = PRECAC_ERR; + + qdf_mem_zero(&chan, sizeof(struct dfs_channel)); + if (QDF_STATUS_SUCCESS != + dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj, + pcac_freq, 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, + &chan.dfs_ch_mhz_freq_seg1, + &chan.dfs_ch_mhz_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", pcac_freq); + return PRECAC_ERR; + } + + PRECAC_LIST_LOCK(dfs); + if (dfs_is_precac_timer_running(dfs)) { + tmp_precac_entry = TAILQ_FIRST(&dfs->dfs_precac_list); + if (tmp_precac_entry && (tmp_precac_entry->vht80_ch_freq == + chan.dfs_ch_mhz_freq_seg1)) { + ret = PRECAC_NOW; + goto end; + } + } + + TAILQ_FOREACH(tmp_precac_entry, + &dfs->dfs_precac_list, pe_list) { + if (tmp_precac_entry->vht80_ch_freq == + chan.dfs_ch_mhz_freq_seg1) { + root = tmp_precac_entry->tree_root; + if (root->n_nol_subchs) + ret = PRECAC_NOL; + else if (root->n_caced_subchs == + N_SUBCHS_FOR_BANDWIDTH(root->bandwidth)) + ret = PRECAC_DONE; + else + ret = PRECAC_REQUIRED; + goto end; + } + } +end: + PRECAC_LIST_UNLOCK(dfs); + return ret; +} +#endif +#endif + +/* + * dfs_init_precac_list() - Initialize preCAC lists. + * @dfs: Pointer to wlan_dfs. + */ +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; + QDF_STATUS status; + struct dfs_channel_bw *dfs_max_bw_info; + int num_precac_roots; + + /* + * We need to prepare list of uniquee center frequencies of maximum + * possible bandwidths. 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. + */ + dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans); + dfs_max_bw_info = qdf_mem_malloc(nchans * + sizeof(struct dfs_channel_bw)); + if (!dfs_max_bw_info) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, + "memory allocation failed"); + return; + } + dfs_fill_max_bw_for_chan(dfs, dfs_max_bw_info, &num_precac_roots); + + TAILQ_INIT(&dfs->dfs_precac_list); + + PRECAC_LIST_LOCK(dfs); + for (i = 0; i < num_precac_roots; i++) { + uint16_t pri_chan_cfreq = dfs_max_bw_info[i].dfs_center_ch_freq; + + found = 0; + TAILQ_FOREACH(tmp_precac_entry, + &dfs->dfs_precac_list, + pe_list) { + if (tmp_precac_entry->center_ch_freq == + pri_chan_cfreq) { + found = 1; + break; + } + } + if (!found && pri_chan_cfreq) { + 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; + } + if (dfs_max_bw_info[i].dfs_max_bw == + DFS_CHWIDTH_165_VAL) { + status = dfs_precac_create_165mhz_precac_entry( + dfs, + precac_entry); + if (status) { + dfs_debug(dfs, + WLAN_DEBUG_DFS, + "PreCAC entry for channel 146 not created"); + continue; + } + /* The restricted 80p80 or the 165MHz channel might + * have a non DFS part with center frequency 5775. + * Mark the non DFS portion as precac done. + */ + dfs_mark_non_dfs_as_precac_done( + dfs, + dfs_max_bw_info[i].dfs_pri_ch_freq, + WLAN_PHYMODE_11AC_VHT80_80); + } else { + status = + dfs_precac_create_precac_entry(dfs, + precac_entry, + dfs_max_bw_info, + i); + if (status) + continue; + /* Some channels like 36HT160 might have a non DFS + * part. Mark the non DFS portion as precac done. + */ + dfs_mark_non_dfs_as_precac_done( + dfs, + dfs_max_bw_info[i].dfs_pri_ch_freq, + WLAN_PHYMODE_11AC_VHT160); + } + } + } + PRECAC_LIST_UNLOCK(dfs); + qdf_mem_free(dfs_max_bw_info); + + dfs_debug(dfs, WLAN_DEBUG_DFS, + "Print the list of PreCAC ieee chan from linked list"); + TAILQ_FOREACH(tmp_precac_entry, + &dfs->dfs_precac_list, + pe_list) { + uint8_t ch_ieee, bw; + + ch_ieee = utils_dfs_freq_to_chan(tmp_precac_entry->center_ch_freq); + bw = tmp_precac_entry->bw; + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "ieee=%u bw=%u", ch_ieee, bw); + } +} + +/* + * dfs_is_precac_done() - Verify if preCAC is done. + * @dfs: Pointer to wlan_dfs. + * @chan: Pointer to dfs_channel. + */ +bool dfs_is_precac_done(struct wlan_dfs *dfs, struct dfs_channel *chan) +{ + bool ret_val = 0; + uint16_t cfreq; + + if (!WLAN_IS_CHAN_5GHZ(chan)) { + dfs_debug(dfs, WLAN_DEBUG_DFS, + "Channel %d not a 5GHz channel", + chan->dfs_ch_ieee); + return 0; + } + + if (WLAN_IS_CHAN_MODE_160(chan)) + cfreq = chan->dfs_ch_mhz_freq_seg2; + else if (WLAN_IS_CHAN_MODE_165(dfs, chan)) + cfreq = RESTRICTED_80P80_CHAN_CENTER_FREQ; + else + /* Center frequency of 20/40/80MHz is given + * by dfs_ch_mhz_freq_seg1. + */ + cfreq = chan->dfs_ch_mhz_freq_seg1; + + if (WLAN_IS_CHAN_MODE_20(chan) || + WLAN_IS_CHAN_MODE_40(chan) || + WLAN_IS_CHAN_MODE_80(chan) || + WLAN_IS_CHAN_MODE_160(chan) || + WLAN_IS_CHAN_MODE_165(dfs, chan)) { + ret_val = + dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq( + dfs, + cfreq); + } else if (WLAN_IS_CHAN_MODE_80_80(chan)) { + ret_val = dfs_is_precac_done_on_ht8080_chan(dfs, chan); + } + + dfs_debug(dfs, WLAN_DEBUG_DFS, "precac_done_status = %d", ret_val); + return ret_val; +} + +/* + * dfs_is_precac_done_on_ht20_40_80_chan_for_freq() - Find if preCAC is done + * for the given frequency. + * @dfs: Pointer to wlan_dfs. + * @chan_freq: Channel frequency in MHZ. + */ +bool +dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq(struct wlan_dfs *dfs, + uint16_t chan_freq) +{ + 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_list)) + TAILQ_FOREACH(precac_entry, + &dfs->dfs_precac_list, + pe_list) { + /* Find if the channel frequency is + * in this precac_list. + */ + if (IS_WITHIN_RANGE_STRICT(chan_freq, + precac_entry->center_ch_freq, + (precac_entry->bw/2))) { + ret_val = dfs_find_cac_status_for_chan_for_freq( + precac_entry, chan_freq); + break; + } + } + PRECAC_LIST_UNLOCK(dfs); + + dfs_debug(dfs, WLAN_DEBUG_DFS, "ch_freq = %u cac_done = %d", + chan_freq, ret_val); + + return ret_val; +} + +/* + * dfs_is_precac_done_on_ht8080_chan - Find if preCAC is done + * for the given frequency. + * @dfs: Pointer to wlan_dfs. + * @chan: Pointer to dfs_channel. + */ +bool dfs_is_precac_done_on_ht8080_chan(struct wlan_dfs *dfs, + struct dfs_channel *chan) +{ + bool ret_val = 0, primary_found = 0; + uint16_t cfreq1, cfreq2 = 0; + + cfreq1 = chan->dfs_ch_mhz_freq_seg1; + cfreq2 = chan->dfs_ch_mhz_freq_seg2; + + /* Check if primary is DFS then search */ + if (WLAN_IS_CHAN_DFS(chan)) + primary_found = + dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq( + dfs, + cfreq1); + else + primary_found = 1; + + /* Check if secondary DFS then search */ + if (WLAN_IS_CHAN_DFS_CFREQ2(chan) && primary_found) { + ret_val = + dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq( + dfs, + cfreq2); + + } else { + if (primary_found) + ret_val = 1; + } + dfs_debug(dfs, WLAN_DEBUG_DFS, + "seg1_freq = %u seg2_freq = %u ret_val = %d", + cfreq1, + cfreq2, + ret_val); + + return ret_val; +} + +/* dfs_mark_adfs_chan_as_cac_done()- Mark the ADFS CAC completed channel as + * CAC done in the precac tree. + * @dfs: Pointer to struct wlan_dfs. + */ +void dfs_mark_adfs_chan_as_cac_done(struct wlan_dfs *dfs) +{ + qdf_freq_t pri_chan_freq, sec_chan_freq; + enum phy_ch_width chan_width; + + if (dfs->dfs_agile_precac_freq_mhz == + RESTRICTED_80P80_CHAN_CENTER_FREQ) { + pri_chan_freq = RESTRICTED_80P80_LEFT_80_CENTER_FREQ; + sec_chan_freq = RESTRICTED_80P80_RIGHT_80_CENTER_FREQ; + chan_width = CH_WIDTH_80P80MHZ; + } else { + pri_chan_freq = dfs->dfs_agile_precac_freq_mhz; + sec_chan_freq = 0; + chan_width = dfs->dfs_precac_chwidth; + } + dfs_mark_precac_done_for_freq(dfs, pri_chan_freq, sec_chan_freq, + chan_width); +} + +/* + * dfs_mark_precac_done_for_freq() - Mark a frequency as preCAC done. + * @dfs: Pointer to wlan_dfs. + * @pri_ch_freq: Primary 80MHZ center frequency. + * @sec_ch_freq: Secondary 80MHZ center frequency. + * @ch_width: Channel width. + */ +void dfs_mark_precac_done_for_freq(struct wlan_dfs *dfs, + uint16_t pri_ch_freq, + uint16_t sec_ch_freq, + enum phy_ch_width ch_width) +{ + struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; + uint16_t channels[NUM_CHANNELS_160MHZ]; + uint8_t i, nchannels = 0; + + if (!pri_ch_freq) + return; + + nchannels = dfs_find_subchannels_for_center_freq(pri_ch_freq, + sec_ch_freq, + ch_width, + channels); + if (!nchannels) + return; + + PRECAC_LIST_LOCK(dfs); + if (TAILQ_EMPTY(&dfs->dfs_precac_list)) { + PRECAC_LIST_UNLOCK(dfs); + return; + } + for (i = 0; i < nchannels; i++) { + TAILQ_FOREACH_SAFE(precac_entry, + &dfs->dfs_precac_list, + pe_list, + tmp_precac_entry) { + if (IS_WITHIN_RANGE_STRICT(channels[i], + precac_entry->center_ch_freq, + (precac_entry->bw/2))) { + dfs_mark_tree_node_as_cac_done_for_freq + (dfs, precac_entry, channels[i]); + break; + } + } + } + PRECAC_LIST_UNLOCK(dfs); +} + +/* + * dfs_mark_precac_nol_for_freq() - Mark a channel as preCAC NOL. + * @dfs: Pointer to wlan_dfs. + * @is_radar_found_on_secondary_seg: Flag to indicate second segment radar. + * @detector_id: Detector ID. + * @freq_list: frequency list. + * @num_channels: Number of channels. + */ +void dfs_mark_precac_nol_for_freq(struct wlan_dfs *dfs, + uint8_t is_radar_found_on_secondary_seg, + uint8_t detector_id, + uint16_t *freq_lst, + uint8_t num_channels) +{ + struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; + struct wlan_objmgr_psoc *psoc; + uint8_t i; + struct dfs_soc_priv_obj *dfs_soc_obj; + struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; + struct wlan_objmgr_pdev *pdev; + + dfs_debug(dfs, WLAN_DEBUG_DFS, + "is_radar_found_on_secondary_seg = %u subchannel_marking = %u detector_id = %u", + is_radar_found_on_secondary_seg, + dfs->dfs_use_nol_subchannel_marking, + detector_id); + + dfs_debug(dfs, WLAN_DEBUG_DFS, + "agile detector freq = %u primary_freq = %u secondary_freq = %u", + dfs->dfs_agile_precac_freq_mhz, + dfs->dfs_precac_secondary_freq_mhz, + dfs->dfs_precac_primary_freq_mhz); + + /* + * 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_list)) { + PRECAC_LIST_UNLOCK(dfs); + return; + } + for (i = 0; i < num_channels; i++) { + TAILQ_FOREACH_SAFE(precac_entry, + &dfs->dfs_precac_list, + pe_list, + tmp_precac_entry) { + if (IS_WITHIN_RANGE_STRICT(freq_lst[i], + precac_entry->center_ch_freq, + (precac_entry->bw/2))) { + dfs_mark_tree_node_as_nol_for_freq(dfs, + precac_entry, + freq_lst[i]); + break; + } + } + } + PRECAC_LIST_UNLOCK(dfs); + + psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); + dfs_soc_obj = dfs->dfs_soc_obj; + + dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc); + pdev = dfs->dfs_pdev_obj; + /* PreCAC timer is not running, no need to restart preCAC */ + if (!dfs_soc_obj->dfs_precac_timer_running) + return; + + if (dfs_is_legacy_precac_enabled(dfs)) { + qdf_timer_sync_cancel(&dfs_soc_obj->dfs_precac_timer); + dfs_soc_obj->dfs_precac_timer_running = 0; + /* + * If radar is found on primary channel, no need to + * restart VAP's channels since channel change will happen + * after RANDOM channel selection anyway. + */ + if (is_radar_found_on_secondary_seg) { + /* + * 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 (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); + } + } + } +} + +#ifdef ATH_SUPPORT_ZERO_CAC_DFS +enum precac_status_for_chan +dfs_precac_status_for_channel(struct wlan_dfs *dfs, + struct dfs_channel *deschan) +{ + if (!dfs_is_precac_completed_count_non_zero(dfs)) + return DFS_NO_PRECAC_COMPLETED_CHANS; + + if (dfs_is_precac_done(dfs, deschan)) + return DFS_PRECAC_COMPLETED_CHAN; + + return DFS_PRECAC_REQUIRED_CHAN; +} +#endif + +/* dfs_print_node_data() - Print the precac tree node data. + * @dfs: Pointer to WLAN DFS structure. + * @node: Precac tree node pointer. + * + * Sample print for below tree: + * + * A A(C,N) + * / \ | + * B C |------- B(C,N) + * / \ | | + * D E | |------- D(C,N) + * | | + * | |------- E(C,N) + * | + * |------- E(C,N) + * + * Where C is number of CACed subchannels, and N is number of NOL subchannels. + * For each node, the prefix and previous line prefix to be printed will be + * based on the level (and by our logic, bandwidth) of the current node. + * + */ +/*Retaining IEEE to print node data */ +static void dfs_print_node_data(struct wlan_dfs *dfs, + struct precac_tree_node *node) +{ + char prefix[MAX_PREFIX_CHAR] = ""; + char prev_line_prefix[MAX_PREFIX_CHAR] = ""; + char inv[4] = "inv"; + + switch (node->depth) { + case DEPTH_160_ROOT: + break; + case DEPTH_80_ROOT: + qdf_str_lcopy(prev_line_prefix, "|", MAX_PREFIX_CHAR); + qdf_str_lcopy(prefix, "|------- ", MAX_PREFIX_CHAR); + break; + case DEPTH_40_ROOT: + qdf_str_lcopy(prev_line_prefix, "| |", MAX_PREFIX_CHAR); + qdf_str_lcopy(prefix, "| |------- ", MAX_PREFIX_CHAR); + break; + case DEPTH_20_ROOT: + qdf_str_lcopy(prev_line_prefix, + "| | |", + MAX_PREFIX_CHAR); + qdf_str_lcopy(prefix, + "| | |------- ", + MAX_PREFIX_CHAR); + break; + default: + return; + } + + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s", prev_line_prefix); + /* if current node is not a valid ic channel, print invalid */ + if (node->n_valid_subchs != N_SUBCHS_FOR_BANDWIDTH(node->bandwidth)) + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s%s", prefix, inv); + else + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s%u(%u,%u)", + prefix, + node->ch_ieee, + node->n_caced_subchs, + node->n_nol_subchs); +} + +/* dfs_print_precac_tree_nodes() - print the precac tree nodes using + * preorder traversal. (Root-Left-Right) + * @dfs: WLAN DFS structure. + * @precac_entry: A single entry in the precac list. + * + * Algorithm used - Morris preorder tree traversal (iterative). + * + * Description of Algorithm: + * Consider the below tree, Preorder sequence (A, B, D, E, C) + * + * A + * / \ + * B C + * / \ + * D E + * + * What is inorder predecessor? + * + * For a given node, the inorder predecessor of the node is + * the rightmost node of the left subtree. + * For e.g.: In our tree, E is the predecessor of A, + * D is the predecessor of B. + * + * Steps for Algorithm: + * Starting from the root node as the current node, + * 1) If there is no left child, print current node data and go to right child. + * 2) If the left child exists, + * 2.1) Find the inorder predecessor of the current node. + * 2.2) If the predecessor's right child is + * 2.2.1) NULL, then + * A) Print current node. + * B) Make the predecessor's right child as the current node. + * C) Go to left child. + * 2.2.2) Current node, then + * A) Make the predecessor's right child as NULL. + * B) Go to the right child. + * 3) Repeat 1 & 2 till current node is NULL. + * + * The above Binary Tree structure during the afore mentioned steps: + * Note: Nodes with '[]' are printed. + * + * A [A] [A] [A] [A] [A] [A] + * / \ /|\ /|\ /|\ /|\ / \ / \ + * B C --> B | C --> [B] | C --> [B] | C --> [B]| C --> [B] C --> [B] [C] + * / \ / \| // \| // \| / \| / \ / \ + * D E D E D E [D] E [D] E [D] [E] [D] [E] + * + */ +static void dfs_print_precac_tree_nodes(struct wlan_dfs *dfs, + struct dfs_precac_entry *precac_entry) +{ + struct precac_tree_node *root = precac_entry->tree_root; + struct precac_tree_node *curr_node, *inorder_predecessor; + + if (!root) + return; + curr_node = root; + while (curr_node) { + if (!curr_node->left_child) { + dfs_print_node_data(dfs, curr_node); + curr_node = curr_node->right_child; + } else { + /* Find the right most leaf node of the left subtree. */ + inorder_predecessor = curr_node->left_child; + while (inorder_predecessor->right_child && + inorder_predecessor->right_child != curr_node) + inorder_predecessor = + inorder_predecessor->right_child; + + /* If the right most child of left subtree already + * is linked to current node. We have traversed + * left subtree. Remove the link and go to right + * subtree + */ + if (inorder_predecessor->right_child == curr_node) { + inorder_predecessor->right_child = NULL; + curr_node = curr_node->right_child; + } else { + /* Print current node data, make current node + * as predecessor's right child, and move to left child. + */ + dfs_print_node_data(dfs, curr_node); + inorder_predecessor->right_child = curr_node; + curr_node = curr_node->left_child; + } + } + } +} + +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, + "Precac status of all nodes in the list:"); + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, + "NOTE: Syntax for each node: (,)"); + TAILQ_FOREACH(tmp_precac_entry, + &dfs->dfs_precac_list, + pe_list) { + dfs_print_precac_tree_nodes(dfs, tmp_precac_entry); + } + PRECAC_LIST_UNLOCK(dfs); +} + +void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs, + struct wlan_dfs *dest_dfs, + uint16_t low_5g_freq, + uint16_t high_5g_freq) +{ + struct dfs_precac_entry *tmp_precac_entry, *tmp_precac_entry2; + + /* If the destination DFS is not adhering ETSI (or) + * if the source DFS does not have any lists, return (nothing to do). + */ + if (utils_get_dfsdomain(dest_dfs->dfs_pdev_obj) != DFS_ETSI_DOMAIN || + TAILQ_EMPTY(&src_dfs->dfs_precac_list)) + return; + + /* If dest_dfs and src_dfs are same it will cause dead_lock. */ + if (dest_dfs == src_dfs) + return; + + PRECAC_LIST_LOCK(dest_dfs); + if (TAILQ_EMPTY(&dest_dfs->dfs_precac_list)) + TAILQ_INIT(&dest_dfs->dfs_precac_list); + PRECAC_LIST_LOCK(src_dfs); + TAILQ_FOREACH(tmp_precac_entry, + &src_dfs->dfs_precac_list, + pe_list) { + if (low_5g_freq <= tmp_precac_entry->vht80_ch_freq && + high_5g_freq >= tmp_precac_entry->vht80_ch_freq) { + /* If the destination DFS already have the entries for + * some reason, remove them and update with the active + * entry in the source DFS list. + */ + TAILQ_FOREACH(tmp_precac_entry2, + &dest_dfs->dfs_precac_list, + pe_list) { + if (tmp_precac_entry2->vht80_ch_freq == + tmp_precac_entry->vht80_ch_freq) + TAILQ_REMOVE(&dest_dfs->dfs_precac_list, + tmp_precac_entry2, + pe_list); + } + TAILQ_REMOVE(&src_dfs->dfs_precac_list, + tmp_precac_entry, + pe_list); + tmp_precac_entry->dfs = dest_dfs; + TAILQ_INSERT_TAIL(&dest_dfs->dfs_precac_list, + tmp_precac_entry, + pe_list); + } + } + PRECAC_LIST_UNLOCK(src_dfs); + PRECAC_LIST_UNLOCK(dest_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); +} + +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); +} + +/* + * dfs_unmark_precac_nol_for_freq() - Unmark a channel frequency as NOL. + * @dfs: Pointer to wlan_dfs. + * @chan_freq: Channel frequency in MHZ. + */ +void dfs_unmark_precac_nol_for_freq(struct wlan_dfs *dfs, uint16_t chan_freq) +{ + struct dfs_precac_entry *pcac_entry = NULL, + *tmp_precac_entry = NULL; + uint16_t pri_ch_freq = 0, chwidth_80 = DFS_CHWIDTH_80_VAL; + + PRECAC_LIST_LOCK(dfs); + if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) { + TAILQ_FOREACH_SAFE(pcac_entry, &dfs->dfs_precac_list, + pe_list, tmp_precac_entry) { + if (IS_WITHIN_RANGE_STRICT(chan_freq, + pcac_entry->center_ch_freq, + (pcac_entry->bw/2))) { + dfs_unmark_tree_node_as_nol_for_freq(dfs, + pcac_entry, + chan_freq); + break; + } + } + } + PRECAC_LIST_UNLOCK(dfs); + + /* If preCAC / agile CAC is not running, restart the timer + * to check if the NOL expired channels can be CACed again. + */ + dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, + "NOL expired for chan_freq %u, trying to start preCAC", + chan_freq); + if (!dfs->dfs_soc_obj->dfs_precac_timer_running) { + if (dfs_is_legacy_precac_enabled(dfs)) { + 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, deferred" + ); + } else if (WLAN_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) { + pri_ch_freq = dfs->dfs_curchan-> + dfs_ch_mhz_freq_seg1; + + /* Check if there is a new channel to preCAC + * and only then do vdev restart. + */ + if (!dfs_get_ieeechan_for_precac_for_freq + (dfs, pri_ch_freq, 0, chwidth_80)) + return; + dfs_mlme_channel_change_by_precac( + dfs->dfs_pdev_obj); + } + } + } +} + +#ifdef QCA_SUPPORT_ADFS_RCAC +/** + * dfs_unmark_rcac_done() - Unmark the CAC done channels from the RCAC list. + * @dfs: Pointer to wlan_dfs object. + */ +void dfs_unmark_rcac_done(struct wlan_dfs *dfs) +{ + struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; + qdf_freq_t channels[NUM_CHANNELS_160MHZ]; + uint8_t i, nchannels = 0; + qdf_freq_t pri_ch_freq = + dfs->dfs_rcac_param.rcac_ch_params.mhz_freq_seg0; + qdf_freq_t sec_ch_freq = + dfs->dfs_rcac_param.rcac_ch_params.mhz_freq_seg1; + enum phy_ch_width ch_width = + dfs->dfs_rcac_param.rcac_ch_params.ch_width; + + if (ch_width == CH_WIDTH_160MHZ) { + pri_ch_freq = sec_ch_freq; + sec_ch_freq = 0; + } + + if (!pri_ch_freq) + return; + + nchannels = dfs_find_subchannels_for_center_freq(pri_ch_freq, + sec_ch_freq, + ch_width, + channels); + if (!nchannels) + return; + + PRECAC_LIST_LOCK(dfs); + if (TAILQ_EMPTY(&dfs->dfs_precac_list)) { + PRECAC_LIST_UNLOCK(dfs); + return; + } + for (i = 0; i < nchannels; i++) { + TAILQ_FOREACH_SAFE(precac_entry, + &dfs->dfs_precac_list, + pe_list, + tmp_precac_entry) { + if (IS_WITHIN_RANGE(channels[i], + precac_entry->center_ch_freq, + VHT160_FREQ_OFFSET)) { + dfs_unmark_tree_node_as_cac_done_for_freq + (precac_entry, channels[i]); + break; + } + } + } + PRECAC_LIST_UNLOCK(dfs); +} +#endif diff --git a/umac/dfs/core/src/misc/dfs_zero_cac.c b/umac/dfs/core/src/misc/dfs_zero_cac.c index 6f3fa15d0a..8bbc1d2be8 100644 --- a/umac/dfs/core/src/misc/dfs_zero_cac.c +++ b/umac/dfs/core/src/misc/dfs_zero_cac.c @@ -249,7 +249,6 @@ * can be used to mark the preCAC lists aswell. */ -#include "dfs_zero_cac.h" #include "wlan_dfs_lmac_api.h" #include "wlan_dfs_mlme_api.h" #include "wlan_dfs_utils_api.h" @@ -257,15 +256,13 @@ #include "dfs_process_radar_found_ind.h" #include "target_if.h" #include "wlan_dfs_init_deinit_api.h" - -/* Given a bandwidth, find the number of subchannels in that bandwidth */ -#define N_SUBCHS_FOR_BANDWIDTH(_bw) ((_bw) / MIN_DFS_SUBCHAN_BW) - -#define DFS_160MHZ_SECSEG_CHAN_OFFSET 40 +#include "../dfs_precac_forest.h" +#include "dfs_zero_cac.h" /*dfs_zero_cac_reset() - Reset zero cac variables. *@dfs: Pointer to wlan_dfs */ +#if defined(WLAN_DFS_PARTIAL_OFFLOAD) && !defined(QCA_MCL_DFS_SUPPORT) void dfs_zero_cac_reset(struct wlan_dfs *dfs) { dfs->dfs_precac_timeout_override = -1; @@ -273,13 +270,6 @@ void dfs_zero_cac_reset(struct wlan_dfs *dfs) dfs->dfs_precac_secondary_freq_mhz = 0; } -#if defined(ATH_SUPPORT_ZERO_CAC_DFS) && !defined(QCA_MCL_DFS_SUPPORT) -void dfs_zero_cac_timer_detach(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - qdf_timer_free(&dfs_soc_obj->dfs_precac_timer); -} -#endif - int dfs_override_precac_timeout(struct wlan_dfs *dfs, int precac_timeout) { if (!dfs) @@ -308,6 +298,127 @@ bool dfs_is_legacy_precac_enabled(struct wlan_dfs *dfs) return dfs->dfs_legacy_precac_ucfg; } +bool dfs_is_precac_timer_running(struct wlan_dfs *dfs) +{ + return dfs->dfs_soc_obj->dfs_precac_timer_running ? true : false; +} + +void dfs_zero_cac_attach(struct wlan_dfs *dfs) +{ + dfs->dfs_precac_timeout_override = -1; + PRECAC_LIST_LOCK_CREATE(dfs); + if (dfs_is_true_160mhz_supported(dfs)) + dfs->dfs_agile_detector_id = AGILE_DETECTOR_ID_TRUE_160MHZ; + else + dfs->dfs_agile_detector_id = AGILE_DETECTOR_ID_80P80; +} + +void dfs_zero_cac_detach(struct wlan_dfs *dfs) +{ + dfs_deinit_precac_list(dfs); + PRECAC_LIST_LOCK_DESTROY(dfs); +} + +void dfs_cancel_precac_timer(struct wlan_dfs *dfs) +{ + struct dfs_soc_priv_obj *dfs_soc_obj; + + dfs_soc_obj = dfs->dfs_soc_obj; + qdf_timer_sync_cancel(&dfs_soc_obj->dfs_precac_timer); + dfs_soc_obj->dfs_precac_timer_running = 0; +} + +void dfs_set_precac_enable(struct wlan_dfs *dfs, uint32_t value) +{ + struct wlan_objmgr_psoc *psoc; + struct wlan_lmac_if_target_tx_ops *tgt_tx_ops; + uint32_t target_type; + struct target_psoc_info *tgt_hdl; + struct tgt_info *info; + struct wlan_lmac_if_tx_ops *tx_ops; + + psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); + if (!psoc) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "psoc is NULL"); + dfs->dfs_legacy_precac_ucfg = 0; + dfs->dfs_agile_precac_ucfg = 0; + return; + } + + tx_ops = wlan_psoc_get_lmac_if_txops(psoc); + if (!tx_ops) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tx_ops is NULL"); + return; + } + + tgt_tx_ops = &tx_ops->target_tx_ops; + target_type = lmac_get_target_type(dfs->dfs_pdev_obj); + + tgt_hdl = wlan_psoc_get_tgt_if_handle(psoc); + if (!tgt_hdl) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "target_psoc_info is null"); + return; + } + + info = (struct tgt_info *)(&tgt_hdl->info); + + if (!info) { + dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tgt_info is null"); + return; + } + + /* + * If + * 1) The chip is CASCADE, + * 2) The user has enabled Pre-CAC and + * 3) The regdomain the ETSI, + * then enable preCAC. + * + * OR + * + * If + * 1) The chip has agile_capability enabled + * 2) The user has enabled Pre-CAC and + * 3) The regdomain the ETSI, + * then enable Agile preCAC. + */ + + if ((1 == value) && + (utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_ETSI_DOMAIN)) { + if (tgt_tx_ops->tgt_is_tgt_type_qca9984(target_type)) + dfs->dfs_legacy_precac_ucfg = value; + else + dfs->dfs_agile_precac_ucfg = value; + } else { + dfs->dfs_agile_precac_ucfg = 0; + dfs->dfs_legacy_precac_ucfg = 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"); + if (tgt_tx_ops->tgt_is_tgt_type_qca9984(target_type)) { + dfs_cancel_precac_timer(dfs); + dfs->dfs_soc_obj->precac_state_started = 0; + } else { +#ifdef QCA_SUPPORT_AGILE_DFS + dfs_agile_sm_deliver_evt(dfs->dfs_soc_obj, + DFS_AGILE_SM_EV_AGILE_STOP, + 0, (void *)dfs); +#endif + } + } +} +#endif + +#if defined(ATH_SUPPORT_ZERO_CAC_DFS) && !defined(QCA_MCL_DFS_SUPPORT) +void dfs_zero_cac_timer_detach(struct dfs_soc_priv_obj *dfs_soc_obj) +{ + qdf_timer_free(&dfs_soc_obj->dfs_precac_timer); +} +#endif + #ifdef QCA_SUPPORT_AGILE_DFS bool dfs_is_agile_precac_enabled(struct wlan_dfs *dfs) { @@ -315,29 +426,7 @@ bool dfs_is_agile_precac_enabled(struct wlan_dfs *dfs) } #endif -/* dfs_descend_precac_tree_for_freq() - Descend into the precac BSTree based on - * the channel provided. If the channel is less than - * given node's channel, descend left, else right. - * @node: Precac BSTree node. - * @chan_freq: Channel freq whose node is to be found. - * - * Return: the next precac_tree_node (left child or right child of - * current node). - */ - -static struct precac_tree_node * -dfs_descend_precac_tree_for_freq(struct precac_tree_node *node, - uint16_t chan_freq) -{ - if (!node) - return NULL; - - if (chan_freq < node->ch_freq) - return node->left_child; - else - return node->right_child; -} - +#ifdef CONFIG_CHAN_FREQ_API /* dfs_find_curchwidth_and_center_chan_for_freq() - Find channel width and * center channelfrequency. * @dfs: Pointer to wlan_dfs. @@ -378,177 +467,7 @@ dfs_find_curchwidth_and_center_chan_for_freq(struct wlan_dfs *dfs, curchan->dfs_ch_mhz_freq_seg2; } } - -/* dfs_find_cac_status_for_chan_for_freq() - Find CAC-done status for the - * channel in the precac Binary Search Tree. - * Return true if CAC done, else false. - * @dfs_precac_entry: Precac entry which has the root of the precac BSTree. - * @chan_freq: IEEE channel freq. This is the center of a - * 20/40/80/160/165 MHz channel and the center channel is - * unique irrespective of the bandwidth - * (20/40/80/160/165 MHz). - */ -static bool -dfs_find_cac_status_for_chan_for_freq(struct dfs_precac_entry *precac_entry, - uint16_t chan_freq) -{ - struct precac_tree_node *node = precac_entry->tree_root; - uint8_t n_cur_lvl_subchs = node->n_valid_subchs; - - while (node) { - if (node->ch_freq == chan_freq) - return (node->n_caced_subchs == n_cur_lvl_subchs) ? - true : false; - - n_cur_lvl_subchs /= 2; - node = dfs_descend_precac_tree_for_freq(node, chan_freq); - } - - return false; -} - -#define VHT80_FREQ_OFFSET 30 -/* For any 160MHz channel, a frequency offset of 70MHz would have been enough - * to include the right edge and left edge channels. But, the restricted 80P80 - * or the 165MHz channel is also assumed to have a 160MHz root ie channel 146, - * so an offset of 75MHz is chosen. - */ -#define VHT160_FREQ_OFFSET 75 - -#define IS_WITHIN_RANGE(_A, _B, _C) \ - (((_A) >= ((_B)-(_C))) && ((_A) <= ((_B)+(_C)))) - -#define IS_WITHIN_RANGE_STRICT(_A, _B, _C) \ - (((_A) > ((_B)-(_C))) && ((_A) < ((_B)+(_C)))) - -/* - * dfs_is_precac_done_on_ht20_40_80_chan_for_freq() - Find if preCAC is done - * for the given frequency. - * @dfs: Pointer to wlan_dfs. - * @chan_freq: Channel frequency in MHZ. - */ -bool -dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq(struct wlan_dfs *dfs, - uint16_t chan_freq) -{ - 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_list)) - TAILQ_FOREACH(precac_entry, - &dfs->dfs_precac_list, - pe_list) { - /* Find if the channel frequency is - * in this precac_list. - */ - if (IS_WITHIN_RANGE_STRICT(chan_freq, - precac_entry->center_ch_freq, - (precac_entry->bw/2))) { - ret_val = dfs_find_cac_status_for_chan_for_freq( - precac_entry, chan_freq); - break; - } - } - PRECAC_LIST_UNLOCK(dfs); - - dfs_debug(dfs, WLAN_DEBUG_DFS, "ch_freq = %u cac_done = %d", - chan_freq, ret_val); - - return ret_val; -} - -/* - * dfs_is_precac_done_on_ht8080_chan - Find if preCAC is done - * for the given frequency. - * @dfs: Pointer to wlan_dfs. - * @chan: Pointer to dfs_channel. - */ -bool dfs_is_precac_done_on_ht8080_chan(struct wlan_dfs *dfs, - struct dfs_channel *chan) -{ - bool ret_val = 0, primary_found = 0; - uint16_t cfreq1, cfreq2 = 0; - - cfreq1 = chan->dfs_ch_mhz_freq_seg1; - cfreq2 = chan->dfs_ch_mhz_freq_seg2; - - /* Check if primary is DFS then search */ - if (WLAN_IS_CHAN_DFS(chan)) - primary_found = - dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq( - dfs, - cfreq1); - else - primary_found = 1; - - /* Check if secondary DFS then search */ - if (WLAN_IS_CHAN_DFS_CFREQ2(chan) && primary_found) { - ret_val = - dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq( - dfs, - cfreq2); - - } else { - if (primary_found) - ret_val = 1; - } - dfs_debug(dfs, WLAN_DEBUG_DFS, - "seg1_freq = %u seg2_freq = %u ret_val = %d", - cfreq1, - cfreq2, - ret_val); - - return ret_val; -} - -/* - * dfs_is_precac_done() - Verify if preCAC is done. - * @dfs: Pointer to wlan_dfs. - * @chan: Pointer to dfs_channel. - */ -bool dfs_is_precac_done(struct wlan_dfs *dfs, struct dfs_channel *chan) -{ - bool ret_val = 0; - uint16_t cfreq; - - if (!WLAN_IS_CHAN_5GHZ(chan)) { - dfs_debug(dfs, WLAN_DEBUG_DFS, - "Channel %d not a 5GHz channel", - chan->dfs_ch_ieee); - return 0; - } - - if (WLAN_IS_CHAN_MODE_160(chan)) - cfreq = chan->dfs_ch_mhz_freq_seg2; - else if (WLAN_IS_CHAN_MODE_165(dfs, chan)) - cfreq = RESTRICTED_80P80_CHAN_CENTER_FREQ; - else - /* Center frequency of 20/40/80MHz is given - * by dfs_ch_mhz_freq_seg1. - */ - cfreq = chan->dfs_ch_mhz_freq_seg1; - - if (WLAN_IS_CHAN_MODE_20(chan) || - WLAN_IS_CHAN_MODE_40(chan) || - WLAN_IS_CHAN_MODE_80(chan) || - WLAN_IS_CHAN_MODE_160(chan) || - WLAN_IS_CHAN_MODE_165(dfs, chan)) { - ret_val = - dfs_is_precac_done_on_ht20_40_80_160_165_chan_for_freq( - dfs, - cfreq); - } else if (WLAN_IS_CHAN_MODE_80_80(chan)) { - ret_val = dfs_is_precac_done_on_ht8080_chan(dfs, chan); - } - - dfs_debug(dfs, WLAN_DEBUG_DFS, "precac_done_status = %d", ret_val); - return ret_val; -} +#endif #ifdef QCA_SUPPORT_AGILE_DFS void dfs_find_pdev_for_agile_precac(struct wlan_objmgr_pdev *pdev, @@ -567,14 +486,8 @@ void dfs_find_pdev_for_agile_precac(struct wlan_objmgr_pdev *pdev, (dfs_soc_obj->cur_agile_dfs_index + 1) % dfs_soc_obj->num_dfs_privs; } -/* - * dfs_fill_adfs_chan_params() - Fill the ADFS FW params. - * @dfs: Pointer to wlan_dfs. - * @adfs_param: Pointer to struct dfs_agile_cac_params. - * @ch_freq: Frequency in MHZ to be programmed to the agile detector. - */ -static void dfs_fill_adfs_chan_params(struct wlan_dfs *dfs, - struct dfs_agile_cac_params *adfs_param) +void dfs_fill_adfs_chan_params(struct wlan_dfs *dfs, + struct dfs_agile_cac_params *adfs_param) { qdf_freq_t ch_freq = dfs->dfs_agile_precac_freq_mhz; @@ -588,11 +501,7 @@ static void dfs_fill_adfs_chan_params(struct wlan_dfs *dfs, adfs_param->precac_chwidth = dfs->dfs_precac_chwidth; } -/* dfs_agile_precac_cleanup() - Reset parameters of wlan_dfs. - * - * @dfs: Pointer to struct wlan_dfs. - */ -static void dfs_agile_precac_cleanup(struct wlan_dfs *dfs) +void dfs_agile_precac_cleanup(struct wlan_dfs *dfs) { struct dfs_soc_priv_obj *dfs_soc_obj; @@ -687,116 +596,10 @@ void dfs_prepare_agile_precac_chan(struct wlan_dfs *dfs, bool *is_chan_found) } #endif -/* dfs_is_tree_node_marked_as_cac_for_freq() - Check if preCAC BSTree node is - * marked as CAC. - * @root: Pointer to root node of the preCAC BSTree. - * @freq: 20MHz channel to be checked if marked as CAC done already. - * - * Return: True if already marked, else false. - */ -static bool -dfs_is_tree_node_marked_as_cac_for_freq(struct precac_tree_node *root, - uint16_t freq) -{ - struct precac_tree_node *curr_node = root; - - while (curr_node) { - if (!curr_node->n_caced_subchs) - return false; - if (curr_node->ch_freq == freq) - return curr_node->n_caced_subchs; - curr_node = dfs_descend_precac_tree_for_freq(curr_node, - freq); - } - return false; -} - -/* dfs_mark_tree_node_as_cac_done_for_freq() - Mark the preCAC BSTree node as - * CAC done. - * @dfs: Pointer to WLAN DFS structure. - * @precac_entry: Precac_list entry pointer. - * @chan_freq: IEEE channel freq to be marked. - * - * Note: The input channel is always of 20MHz bandwidth. - */ -static void -dfs_mark_tree_node_as_cac_done_for_freq(struct wlan_dfs *dfs, - struct dfs_precac_entry *precac_entry, - uint16_t chan_freq) -{ - struct precac_tree_node *curr_node; - - if (!precac_entry->tree_root) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Precac tree root pointer is NULL!"); - return; - } - - curr_node = precac_entry->tree_root; - /** - * Check if the channel is already marked and return if true. - * This will happen in scenarios like the following: - * preCAC is running on channel 128 in HT20 mode (note: 124 is already - * marked. Now if the mode is switched to HT40, preCAC is restarted - * and the new channel picked for preCAC is 126 HT40. Here, 124 - * will be already marked since it was completed in HT20 mode. - * This may happen for any mode switches (20<->40<->80 MHz). - */ - if (dfs_is_tree_node_marked_as_cac_for_freq(curr_node, chan_freq)) - return; - - while (curr_node) { - /* Update the current node's CACed subchannels count only - * if it's less than maximum subchannels, else return. - */ - if (curr_node->n_caced_subchs < - N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth)) - curr_node->n_caced_subchs++; - curr_node = dfs_descend_precac_tree_for_freq(curr_node, - chan_freq); - } -} - -/* dfs_unmark_tree_node_as_cac_done_for_freq() - Unmark the preCAC BSTree - * node as CAC done. - * @precac_entry: Precac_list entry pointer. - * @chan_freq: IEEE channel freq to be marked. - * - * Note: The input channel is always of 20MHz bandwidth. - */ -static void -dfs_unmark_tree_node_as_cac_done_for_freq(struct dfs_precac_entry - *precac_entry, uint16_t chan_freq) -{ - struct precac_tree_node *curr_node = precac_entry->tree_root; - - while (curr_node) { - if (curr_node->n_caced_subchs) - curr_node->n_caced_subchs--; - else - return; - curr_node = dfs_descend_precac_tree_for_freq(curr_node, - chan_freq); - } -} - -/** - * dfs_find_subchannels_for_center_freq() - API to find the subchannels given - * the center frequencies and ch_width. - * @pri_center_freq: It is the center of 20/40/80/160Mhz band and for 80+80Mhz - * it is the center of the first 80Mhz band. - * @sec_center_freq: It is used only for 80+80Mhz and denotes the center - * of the second 80Mhz band. - * @ch_width: Channel width. - * @channels: List of subchannels. - * - * Return: Number of subchannels. - */ -static uint8_t -dfs_find_subchannels_for_center_freq(qdf_freq_t pri_center_freq, - qdf_freq_t sec_center_freq, - enum phy_ch_width ch_width, - qdf_freq_t *channels) +uint8_t dfs_find_subchannels_for_center_freq(qdf_freq_t pri_center_freq, + qdf_freq_t sec_center_freq, + enum phy_ch_width ch_width, + qdf_freq_t *channels) { uint8_t nchannels = 0; @@ -848,361 +651,24 @@ dfs_find_subchannels_for_center_freq(qdf_freq_t pri_center_freq, return nchannels; } -#ifdef QCA_SUPPORT_ADFS_RCAC -/** - * dfs_unmark_rcac_done() - Unmark the CAC done channels from the RCAC list. - * @dfs: Pointer to wlan_dfs object. - */ -static void dfs_unmark_rcac_done(struct wlan_dfs *dfs) -{ - struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; - qdf_freq_t channels[NUM_CHANNELS_160MHZ]; - uint8_t i, nchannels = 0; - qdf_freq_t pri_ch_freq = - dfs->dfs_rcac_param.rcac_ch_params.mhz_freq_seg0; - qdf_freq_t sec_ch_freq = - dfs->dfs_rcac_param.rcac_ch_params.mhz_freq_seg1; - enum phy_ch_width ch_width = - dfs->dfs_rcac_param.rcac_ch_params.ch_width; - - if (ch_width == CH_WIDTH_160MHZ) { - pri_ch_freq = sec_ch_freq; - sec_ch_freq = 0; - } - - if (!pri_ch_freq) - return; - - nchannels = dfs_find_subchannels_for_center_freq(pri_ch_freq, - sec_ch_freq, - ch_width, - channels); - if (!nchannels) - return; - - PRECAC_LIST_LOCK(dfs); - if (TAILQ_EMPTY(&dfs->dfs_precac_list)) { - PRECAC_LIST_UNLOCK(dfs); - return; - } - for (i = 0; i < nchannels; i++) { - TAILQ_FOREACH_SAFE(precac_entry, - &dfs->dfs_precac_list, - pe_list, - tmp_precac_entry) { - if (IS_WITHIN_RANGE(channels[i], - precac_entry->center_ch_freq, - VHT160_FREQ_OFFSET)) { - dfs_unmark_tree_node_as_cac_done_for_freq - (precac_entry, channels[i]); - break; - } - } - } - PRECAC_LIST_UNLOCK(dfs); -} -#else -static inline void dfs_unmark_rcac_done(struct wlan_dfs *dfs) -{ -} -#endif - -/* - * dfs_mark_precac_done_for_freq() - Mark a frequency as preCAC done. - * @dfs: Pointer to wlan_dfs. - * @pri_ch_freq: Primary 80MHZ center frequency. - * @sec_ch_freq: Secondary 80MHZ center frequency. - * @ch_width: Channel width. - */ -void dfs_mark_precac_done_for_freq(struct wlan_dfs *dfs, - uint16_t pri_ch_freq, - uint16_t sec_ch_freq, - enum phy_ch_width ch_width) -{ - struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; - uint16_t channels[NUM_CHANNELS_160MHZ]; - uint8_t i, nchannels = 0; - - if (!pri_ch_freq) - return; - - nchannels = dfs_find_subchannels_for_center_freq(pri_ch_freq, - sec_ch_freq, - ch_width, - channels); - if (!nchannels) - return; - - PRECAC_LIST_LOCK(dfs); - if (TAILQ_EMPTY(&dfs->dfs_precac_list)) { - PRECAC_LIST_UNLOCK(dfs); - return; - } - for (i = 0; i < nchannels; i++) { - TAILQ_FOREACH_SAFE(precac_entry, - &dfs->dfs_precac_list, - pe_list, - tmp_precac_entry) { - if (IS_WITHIN_RANGE_STRICT(channels[i], - precac_entry->center_ch_freq, - (precac_entry->bw/2))) { - dfs_mark_tree_node_as_cac_done_for_freq - (dfs, precac_entry, channels[i]); - break; - } - } - } - PRECAC_LIST_UNLOCK(dfs); -} - -/* dfs_mark_tree_node_as_nol_for_freq() - Mark the preCAC BSTree node as NOL. - * @dfs: Pointer to WLAN DFS structure. - * @precac_entry: Precac_list entry pointer. - * @freq: IEEE channel freq to be marked. - * - * Note: The input channel is always of 20MHz bandwidth. - */ -static void -dfs_mark_tree_node_as_nol_for_freq(struct wlan_dfs *dfs, - struct dfs_precac_entry *pcac, - uint16_t freq) -{ - struct precac_tree_node *curr_node; - - if (!pcac->tree_root) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Precac tree root pointer is NULL!"); - return; - } - curr_node = pcac->tree_root; - while (curr_node) { - if (curr_node->n_nol_subchs < - N_SUBCHS_FOR_BANDWIDTH(curr_node->bandwidth)) { - curr_node->n_nol_subchs++; - } else { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Radarfound on an already marked NOL channel!"); - return; - } - if (freq == curr_node->ch_freq) { - if (curr_node->n_caced_subchs) { - /* remove cac done status for this node - * and it's parents, since this node - * now requires cac (after NOL expiry) - */ - dfs_unmark_tree_node_as_cac_done_for_freq(pcac, - freq); - } - } - curr_node = dfs_descend_precac_tree_for_freq(curr_node, - freq); - } -} - -/* dfs_unmark_tree_node_as_nol_for_freq() - Unmark the preCAC BSTree node as - * NOL. - * @dfs: Pointer to WLAN DFS structure. - * @precac_entry: Precac_list entry pointer. - * @chan_freq IEEE channel freq to be marked. - * - * Note: The input channel is always of 20MHz bandwidth. - */ - -static void -dfs_unmark_tree_node_as_nol_for_freq(struct wlan_dfs *dfs, - struct dfs_precac_entry *precac_entry, - uint16_t chan_freq) -{ - struct precac_tree_node *curr_node; - - if (!precac_entry->tree_root) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Precac tree root pointer is NULL!"); - return; - } - curr_node = precac_entry->tree_root; - while (curr_node) { - if (curr_node->n_nol_subchs) - curr_node->n_nol_subchs--; - else - return; - curr_node = dfs_descend_precac_tree_for_freq(curr_node, - chan_freq); - } -} - -/* - * dfs_unmark_precac_nol_for_freq() - Unmark a channel frequency as NOL. - * @dfs: Pointer to wlan_dfs. - * @chan_freq: Channel frequency in MHZ. - */ -void dfs_unmark_precac_nol_for_freq(struct wlan_dfs *dfs, uint16_t chan_freq) -{ - struct dfs_precac_entry *pcac_entry = NULL, - *tmp_precac_entry = NULL; - uint16_t pri_ch_freq = 0, chwidth_80 = DFS_CHWIDTH_80_VAL; - - PRECAC_LIST_LOCK(dfs); - if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) { - TAILQ_FOREACH_SAFE(pcac_entry, &dfs->dfs_precac_list, - pe_list, tmp_precac_entry) { - if (IS_WITHIN_RANGE_STRICT(chan_freq, - pcac_entry->center_ch_freq, - (pcac_entry->bw/2))) { - dfs_unmark_tree_node_as_nol_for_freq(dfs, - pcac_entry, - chan_freq); - break; - } - } - } - PRECAC_LIST_UNLOCK(dfs); - - /* If preCAC / agile CAC is not running, restart the timer - * to check if the NOL expired channels can be CACed again. - */ - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, - "NOL expired for chan_freq %u, trying to start preCAC", - chan_freq); - if (!dfs->dfs_soc_obj->dfs_precac_timer_running) { - if (dfs_is_legacy_precac_enabled(dfs)) { - 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, deferred" - ); - } else if (WLAN_IS_CHAN_11AC_VHT80(dfs->dfs_curchan)) { - pri_ch_freq = dfs->dfs_curchan-> - dfs_ch_mhz_freq_seg1; - - /* Check if there is a new channel to preCAC - * and only then do vdev restart. - */ - if (!dfs_get_ieeechan_for_precac_for_freq - (dfs, pri_ch_freq, 0, chwidth_80)) - return; - dfs_mlme_channel_change_by_precac( - dfs->dfs_pdev_obj); - } - } - } -} - -/* - * dfs_mark_precac_nol_for_freq() - Mark a channel as preCAC NOL. - * @dfs: Pointer to wlan_dfs. - * @is_radar_found_on_secondary_seg: Flag to indicate second segment radar. - * @detector_id: Detector ID. - * @freq_list: frequency list. - * @num_channels: Number of channels. - */ -void dfs_mark_precac_nol_for_freq(struct wlan_dfs *dfs, - uint8_t is_radar_found_on_secondary_seg, - uint8_t detector_id, - uint16_t *freq_lst, - uint8_t num_channels) -{ - struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; - struct wlan_objmgr_psoc *psoc; - uint8_t i; - struct dfs_soc_priv_obj *dfs_soc_obj; - struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; - struct wlan_objmgr_pdev *pdev; - - dfs_debug(dfs, WLAN_DEBUG_DFS, - "is_radar_found_on_secondary_seg = %u subchannel_marking = %u detector_id = %u", - is_radar_found_on_secondary_seg, - dfs->dfs_use_nol_subchannel_marking, - detector_id); - - dfs_debug(dfs, WLAN_DEBUG_DFS, - "agile detector freq = %u primary_freq = %u secondary_freq = %u", - dfs->dfs_agile_precac_freq_mhz, - dfs->dfs_precac_secondary_freq_mhz, - dfs->dfs_precac_primary_freq_mhz); - - /* - * 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_list)) { - PRECAC_LIST_UNLOCK(dfs); - return; - } - for (i = 0; i < num_channels; i++) { - TAILQ_FOREACH_SAFE(precac_entry, - &dfs->dfs_precac_list, - pe_list, - tmp_precac_entry) { - if (IS_WITHIN_RANGE_STRICT(freq_lst[i], - precac_entry->center_ch_freq, - (precac_entry->bw/2))) { - dfs_mark_tree_node_as_nol_for_freq(dfs, - precac_entry, - freq_lst[i]); - break; - } - } - } - PRECAC_LIST_UNLOCK(dfs); - - psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); - dfs_soc_obj = dfs->dfs_soc_obj; - - dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc); - pdev = dfs->dfs_pdev_obj; - /* PreCAC timer is not running, no need to restart preCAC */ - if (!dfs_soc_obj->dfs_precac_timer_running) - return; - - if (dfs_is_legacy_precac_enabled(dfs)) { - qdf_timer_sync_cancel(&dfs_soc_obj->dfs_precac_timer); - dfs_soc_obj->dfs_precac_timer_running = 0; - /* - * If radar is found on primary channel, no need to - * restart VAP's channels since channel change will happen - * after RANDOM channel selection anyway. - */ - if (is_radar_found_on_secondary_seg) { - /* - * 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 (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); - } - } - } -} - #ifdef QCA_SUPPORT_AGILE_DFS #ifdef CONFIG_CHAN_FREQ_API /* Checks the Host side agile configurations. ie if agile channel * is configured as 5730MHz and the agile channel width is 80P80/165MHz. */ #define IS_HOST_AGILE_CURCHAN_165MHZ(_x) \ - ((_x)->dfs_agile_precac_freq_mhz == \ + (((_x)->dfs_agile_precac_freq_mhz == \ RESTRICTED_80P80_CHAN_CENTER_FREQ) && \ - ((_x)->dfs_precac_chwidth == CH_WIDTH_80P80MHZ) + ((_x)->dfs_precac_chwidth == CH_WIDTH_80P80MHZ)) /* Checks if the FW Agile operation was on the restricited 80P80MHz, * by comparing the center frequency 1 with 5690MHz, center frequency 2 * with 5775MHz and the channel width was 80P80/165MHz. */ #define IS_OCAC_EVENT_ON_165_MHZ_CHAN(_x, _y, _z) \ - ((_x) == RESTRICTED_80P80_LEFT_80_CENTER_FREQ) && \ + (((_x) == RESTRICTED_80P80_LEFT_80_CENTER_FREQ) && \ ((_y) == RESTRICTED_80P80_RIGHT_80_CENTER_FREQ) && \ - ((_z) == CH_WIDTH_80P80MHZ) + ((_z) == CH_WIDTH_80P80MHZ)) /* * dfs_is_ocac_complete_event_for_cur_agile_chan() - Check if the OCAC @@ -1226,15 +692,15 @@ dfs_is_ocac_complete_event_for_cur_agile_chan(struct wlan_dfs *dfs, uint32_t center_freq_mhz2, enum phy_ch_width chwidth) { - if (IS_HOST_AGILE_CURCHAN_165MHZ(dfs) && - IS_OCAC_EVENT_ON_165_MHZ_CHAN(center_freq_mhz1, - center_freq_mhz2, - chwidth)) - return true; - else if (dfs->dfs_agile_precac_freq_mhz == center_freq_mhz1) - return true; - else - return false; + if (IS_HOST_AGILE_CURCHAN_165MHZ(dfs) && + IS_OCAC_EVENT_ON_165_MHZ_CHAN(center_freq_mhz1, + center_freq_mhz2, + chwidth)) + return true; + else if (dfs->dfs_agile_precac_freq_mhz == center_freq_mhz1) + return true; + else + return false; } /* @@ -1309,11 +775,6 @@ void dfs_process_ocac_complete(struct wlan_objmgr_pdev *pdev, #endif #endif -bool dfs_is_precac_timer_running(struct wlan_dfs *dfs) -{ - return dfs->dfs_soc_obj->dfs_precac_timer_running ? true : false; -} - /* * dfs_find_precac_secondary_vht80_chan() - Find preCAC secondary VHT80 channel. * @dfs: Pointer to wlan_dfs. @@ -1369,17 +830,7 @@ void dfs_precac_csa(struct wlan_dfs *dfs) #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 return true, else false. - * - * Return: true if precac done on home channel, else false. - */ -static bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs) +bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs) { struct dfs_channel *chan = dfs->dfs_autoswitch_chan; @@ -1390,36 +841,8 @@ static bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs) } return false; } -#else -static inline bool dfs_precac_check_home_chan_change(struct wlan_dfs *dfs) -{ - return false; -} #endif -/* dfs_mark_adfs_chan_as_cac_done()- Mark the ADFS CAC completed channel as - * CAC done in the precac tree. - * @dfs: Pointer to struct wlan_dfs. - */ -static void dfs_mark_adfs_chan_as_cac_done(struct wlan_dfs *dfs) -{ - qdf_freq_t pri_chan_freq, sec_chan_freq; - enum phy_ch_width chan_width; - - if (dfs->dfs_agile_precac_freq_mhz == - RESTRICTED_80P80_CHAN_CENTER_FREQ) { - pri_chan_freq = RESTRICTED_80P80_LEFT_80_CENTER_FREQ; - sec_chan_freq = RESTRICTED_80P80_RIGHT_80_CENTER_FREQ; - chan_width = CH_WIDTH_80P80MHZ; - } else { - pri_chan_freq = dfs->dfs_agile_precac_freq_mhz; - sec_chan_freq = 0; - chan_width = dfs->dfs_precac_chwidth; - } - dfs_mark_precac_done_for_freq(dfs, pri_chan_freq, sec_chan_freq, - chan_width); -} - /** * dfs_precac_timeout() - Precac timeout. * @@ -1494,94 +917,6 @@ void dfs_zero_cac_timer_init(struct dfs_soc_priv_obj *dfs_soc_obj) } #endif -void dfs_zero_cac_attach(struct wlan_dfs *dfs) -{ - dfs->dfs_precac_timeout_override = -1; - PRECAC_LIST_LOCK_CREATE(dfs); - if (dfs_is_true_160mhz_supported(dfs)) - dfs->dfs_agile_detector_id = AGILE_DETECTOR_ID_TRUE_160MHZ; - else - dfs->dfs_agile_detector_id = AGILE_DETECTOR_ID_80P80; -} - -/* dfs_init_precac_tree_node() - Initialise the preCAC BSTree node with the - * provided values. - * @node: Precac_tree_node to be filled. - * @freq: IEEE channel freq value. - * @bandwidth: Bandwidth of the channel. - * @depth: Depth of the tree. The depth of the tree when the root is 160MHz - * channel is 4, 80MHz is 3, 40MHz is 2 and 20MHz is 1. - */ -static inline void -dfs_init_precac_tree_node_for_freq(struct precac_tree_node *node, - uint16_t freq, - uint8_t bandwidth, - uint8_t depth) -{ - node->left_child = NULL; - node->right_child = NULL; - node->ch_freq = freq; - node->ch_ieee = utils_dfs_freq_to_chan(freq); - node->n_caced_subchs = 0; - node->n_nol_subchs = 0; - node->n_valid_subchs = N_SUBCHS_FOR_BANDWIDTH(bandwidth); - node->bandwidth = bandwidth; - node->depth = depth; - -} - -/* dfs_insert_node_into_bstree_for_freq() - Insert a new preCAC BSTree node. - * @root: The preCAC BSTree root pointer. - * @chan: IEEE freq of the new node. - * @bandwidth: Bandwidth of the channel. - * @depth: Depth of the tree. The depth of the tree when the root is 160MHz - * channel is 4, 80MHz is 3, 40MHz is 2 and 20MHz is 1. - * - * Return: EOK if new node is allocated, else return ENOMEM. - */ -static QDF_STATUS -dfs_insert_node_into_bstree_for_freq(struct precac_tree_node **root, - uint16_t chan_freq, - uint8_t bandwidth, - uint8_t depth) -{ - struct precac_tree_node *new_node = NULL; - struct precac_tree_node *curr_node, *prev_node = NULL; - QDF_STATUS status = EOK; - - new_node = qdf_mem_malloc(sizeof(*new_node)); - if (!new_node) - return -ENOMEM; - dfs_init_precac_tree_node_for_freq(new_node, - chan_freq, - bandwidth, - depth); - - /* If root node is null, assign the newly allocated node - * to this node and return. - */ - if (!(*root)) { - *root = new_node; - return status; - } - - curr_node = *root; - /* Find the leaf node which will be the new node's parent */ - while (curr_node) { - prev_node = curr_node; - curr_node = dfs_descend_precac_tree_for_freq(curr_node, - chan_freq); - } - - /* Add to the leaf node */ - if (chan_freq < prev_node->ch_freq) - prev_node->left_child = new_node; - else - prev_node->right_child = new_node; - - return status; -} - /* dfs fill_precac_tree_for_entry() - Fill precac entry tree (level insertion). * @dfs: WLAN DFS structure * @ch_ieee: root_node ieee channel. @@ -1600,659 +935,6 @@ dfs_insert_node_into_bstree_for_freq(struct precac_tree_node **root, * */ -#define N_OFFSETS 2 -#define START_INDEX 0 -#define STEP_INDEX 1 - - -/** - * struct precac_tree_offset_for_different_bw - Bandwidth, tree depth and - * channel offsets information to build the precac tree. - * @bandwidth: Bandwidth of the the root node. - * @tree_depth: Tree depth of the precac tree. - * @initial_and_next_offsets: Offset to root node to find the initial and the - * next channels of the node. - */ -struct precac_tree_offset_for_different_bw { - int bandwidth; - int tree_depth; - int initial_and_next_offsets[TREE_DEPTH_MAX][N_OFFSETS]; -}; - -static const -struct precac_tree_offset_for_different_bw offset20 = {DFS_CHWIDTH_20_VAL, - TREE_DEPTH_20, - { - {0, NEXT_20_CHAN_FREQ_OFFSET} - } -}; - -static const -struct precac_tree_offset_for_different_bw offset40 = {DFS_CHWIDTH_40_VAL, - TREE_DEPTH_40, - { - {0, NEXT_40_CHAN_FREQ_OFFSET}, - {-10, NEXT_20_CHAN_FREQ_OFFSET} - } -}; - -static const -struct precac_tree_offset_for_different_bw offset80 = {DFS_CHWIDTH_80_VAL, - TREE_DEPTH_80, - { - {0, NEXT_80_CHAN_FREQ_OFFSET}, - {-20, NEXT_40_CHAN_FREQ_OFFSET}, - {-30, NEXT_20_CHAN_FREQ_OFFSET} - } -}; - -static const -struct precac_tree_offset_for_different_bw offset160 = {DFS_CHWIDTH_160_VAL, - TREE_DEPTH_160, - { - {INITIAL_160_CHAN_FREQ_OFFSET, NEXT_160_CHAN_FREQ_OFFSET}, - {INITIAL_80_CHAN_FREQ_OFFSET, NEXT_80_CHAN_FREQ_OFFSET}, - {INITIAL_40_CHAN_FREQ_OFFSET, NEXT_40_CHAN_FREQ_OFFSET}, - {INITIAL_20_CHAN_FREQ_OFFSET, NEXT_20_CHAN_FREQ_OFFSET} - } -}; - -static const -struct precac_tree_offset_for_different_bw default_offset = {0, 0}; - -/* dfs_create_precac_tree_for_freq() - Fill precac entry tree (level insertion). - * @dfs: WLAN DFS structure - * @ch_freq: root_node freq. - * @root: Pointer to the node that will be filled and inserted as tree - * root. - * @bandwidth: Bandwidth value of the root. - */ -static QDF_STATUS -dfs_create_precac_tree_for_freq(struct wlan_dfs *dfs, - uint16_t ch_freq, - struct precac_tree_node **root, - int bandwidth) -{ - int chan_freq, i; - QDF_STATUS status = EOK; - struct precac_tree_offset_for_different_bw current_mode; - uint8_t top_lvl_step; - bool is_node_part_of_165_tree = false; - - if (ch_freq == RESTRICTED_80P80_LEFT_80_CENTER_FREQ || - ch_freq == RESTRICTED_80P80_RIGHT_80_CENTER_FREQ) - is_node_part_of_165_tree = true; - - switch (bandwidth) { - case DFS_CHWIDTH_160_VAL: - current_mode = offset160; - break; - case DFS_CHWIDTH_80_VAL: - current_mode = offset80; - break; - case DFS_CHWIDTH_40_VAL: - current_mode = offset40; - break; - case DFS_CHWIDTH_20_VAL: - current_mode = offset20; - break; - default: - current_mode = default_offset; - break; - } - top_lvl_step = current_mode.initial_and_next_offsets[0][1]; - for (i = 0; i < current_mode.tree_depth; i++) { - /* In offset array, - * column 0 is initial chan offset, - * column 1 is next chan offset. - * Boundary offset is initial offset and next offset - * of root level (since root level can have only 1 node) - */ - int offset = - current_mode.initial_and_next_offsets[i][START_INDEX]; - int step = current_mode.initial_and_next_offsets[i][STEP_INDEX]; - int boundary_offset = offset + top_lvl_step; - uint8_t depth = is_node_part_of_165_tree ? i + 1 : i; - - for (; offset < boundary_offset; offset += step) { - chan_freq = (int)ch_freq + offset; - status = - dfs_insert_node_into_bstree_for_freq(root, - chan_freq, - bandwidth, - depth); - if (status) - return status; - } - bandwidth /= 2; - } - - return status; -} - -/** - * struct dfs_channel_bw - Structure to store the information about precac - * root's primary channel frequency, maximum bandwidth and the center frequency. - * - * @dfs_pri_ch_freq: Primary channel frequency of the root channel. - * @dfs_center_ch_freq: Center frequency of the 20/40/80/160 channel.In case of - * the 165MHz channel, it is 5730MHz. - * @dfs_max_bw: Maximum bandwidth of the channel available in the - * current channel list. - */ -struct dfs_channel_bw { - uint16_t dfs_pri_ch_freq; - uint16_t dfs_center_ch_freq; - uint16_t dfs_max_bw; -}; - -/* dfs_calculate_bw_for_same_pri_ch() - When the primary channel is a new - * channel initialize the center channel frequency and bandwidth and when the - * primary is same as previous channel update the center frequency and the - * bandwith. - * @dfs: WLAN DFS structure - * @dfs_max_bw_info: Structure to store precac tree root channel's - * information. - * @index: The index that is manipulated. - * @ichan: The DFS channel structure that holds the primary channel - * number, center frquency and channel bandwidth. - * @delimiter: Band gap in MHz from the current primary channel to next - * primary channel. - */ -static void -dfs_calculate_bw_for_same_pri_ch(struct wlan_dfs *dfs, - struct dfs_channel_bw *dfs_max_bw_info, - int index, - struct dfs_channel *ichan, - int *delimiter) -{ - uint8_t temp_bw = 0; - uint16_t tmp_center_freq; - - dfs_max_bw_info[index].dfs_pri_ch_freq = ichan->dfs_ch_freq; - tmp_center_freq = ichan->dfs_ch_mhz_freq_seg1; - - if (WLAN_IS_CHAN_MODE_20(ichan)) { - temp_bw = DFS_CHWIDTH_20_VAL; - } else if (WLAN_IS_CHAN_MODE_40(ichan)) { - temp_bw = DFS_CHWIDTH_40_VAL; - } else if (WLAN_IS_CHAN_MODE_80(ichan) || - WLAN_IS_CHAN_MODE_80_80(ichan)) { - temp_bw = DFS_CHWIDTH_80_VAL; - if (dfs_is_restricted_80p80mhz_supported(dfs) && - WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(ichan) && - (ichan->dfs_ch_vhtop_ch_freq_seg1 == - RESTRICTED_80P80_LEFT_80_CENTER_CHAN) && - (ichan->dfs_ch_vhtop_ch_freq_seg2 == - RESTRICTED_80P80_RIGHT_80_CENTER_CHAN)) { - temp_bw = DFS_CHWIDTH_165_VAL; - tmp_center_freq = RESTRICTED_80P80_CHAN_CENTER_FREQ; - } - } else if (WLAN_IS_CHAN_MODE_160(ichan)) { - temp_bw = DFS_CHWIDTH_160_VAL; - tmp_center_freq = ichan->dfs_ch_mhz_freq_seg2; - } - if (temp_bw > dfs_max_bw_info[index].dfs_max_bw) { - dfs_max_bw_info[index].dfs_max_bw = temp_bw; - *delimiter = dfs_max_bw_info[index].dfs_pri_ch_freq + - dfs_max_bw_info[index].dfs_max_bw; - dfs_max_bw_info[index].dfs_center_ch_freq = tmp_center_freq; - } -} - -/* dfs_fill_max_bw_for_chan() - Finds unique precac tree node in the channel - * list and stores the primary channel frequency, maximum bandwidth and the - * center frequency. The algorithm is based on the data structure ic_channels - * where the channels are organized as 36HT20, 36HT40, 36HT80,... and so on.. - * @dfs: WLAN DFS structure - * @dfs_max_bw_info: Structure to store precac tree root channel's - * information. - * @num_precac_roots: Number of unique. - */ -static void dfs_fill_max_bw_for_chan(struct wlan_dfs *dfs, - struct dfs_channel_bw *dfs_max_bw_info, - int *num_precac_roots) -{ - int i; - int n_total_chans = 0; - int n_chanseg_found = 0; - int prev_ch_freq = 0; - int delimiter = 0; - - dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &n_total_chans); - for (i = 0; i < n_total_chans; i++) { - struct dfs_channel *ichan = NULL, lc; - /* The array index of the bandwidth list that needs to be - * updated. - */ - int index_to_update; - - ichan = &lc; - dfs_mlme_get_dfs_channels_for_freq - (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, - &ichan->dfs_ch_mhz_freq_seg1, - &ichan->dfs_ch_mhz_freq_seg2, - i); - if (!WLAN_IS_PRIMARY_OR_SECONDARY_CHAN_DFS(ichan)) - continue; - if (ichan->dfs_ch_freq == prev_ch_freq) { - /* When the primary channels are common for consecutive - * channels, for example 36HT20, 36HT40, 36HT80,..., - * only the center frequecy and the bandwidth have to be - * updated. - */ - index_to_update = n_chanseg_found - 1; - dfs_calculate_bw_for_same_pri_ch(dfs, - dfs_max_bw_info, - index_to_update, - ichan, - &delimiter); - } else if (ichan->dfs_ch_freq < delimiter) { - continue; - } else { - prev_ch_freq = ichan->dfs_ch_freq; - /* When the primary channels are unique and consecutive - * like 149HT20, 153HT20, 157HT20,..., the new element - * has to be initialized here. - */ - index_to_update = n_chanseg_found; - dfs_calculate_bw_for_same_pri_ch(dfs, - dfs_max_bw_info, - n_chanseg_found, - ichan, - &delimiter); - n_chanseg_found++; - } - } - *num_precac_roots = n_chanseg_found; - for (i = 0; i < *num_precac_roots; i++) - dfs_debug(dfs, WLAN_DEBUG_DFS, - "index = %d pri: %d centr: %d bw: %d", - i, - dfs_max_bw_info[i].dfs_pri_ch_freq, - dfs_max_bw_info[i].dfs_center_ch_freq, - dfs_max_bw_info[i].dfs_max_bw); -} - -static QDF_STATUS -dfs_precac_create_precac_entry(struct wlan_dfs *dfs, - struct dfs_precac_entry *precac_entry, - struct dfs_channel_bw *dfs_max_bw_info, - int index) -{ - QDF_STATUS status; - uint16_t precac_center_freq = - dfs_max_bw_info[index].dfs_center_ch_freq; - - precac_entry->center_ch_freq = precac_center_freq; - precac_entry->center_ch_ieee = - utils_dfs_freq_to_chan(precac_center_freq); - precac_entry->bw = dfs_max_bw_info[index].dfs_max_bw; - /* non_dfs_subch_count will be updated once the channels are marked. */ - precac_entry->non_dfs_subch_count = 0; - precac_entry->dfs = dfs; - status = - dfs_create_precac_tree_for_freq(dfs, - precac_entry->center_ch_freq, - &precac_entry->tree_root, - precac_entry->bw); - if (status) - dfs_debug(dfs, WLAN_DEBUG_DFS, - "PreCAC entry for channel %d not created", - precac_entry->center_ch_ieee); - else - TAILQ_INSERT_TAIL(&dfs->dfs_precac_list, precac_entry, pe_list); - - return status; -} - -static QDF_STATUS -dfs_precac_create_165mhz_precac_entry(struct wlan_dfs *dfs, - struct dfs_precac_entry *precac_entry) -{ - QDF_STATUS status; - - precac_entry->center_ch_freq = - RESTRICTED_80P80_CHAN_CENTER_FREQ; - precac_entry->center_ch_ieee = - utils_dfs_freq_to_chan(precac_entry->center_ch_freq); - precac_entry->bw = DFS_CHWIDTH_160_VAL; - /* non_dfs_subch_count will be updated once the channels are marked. */ - precac_entry->non_dfs_subch_count = 0; - precac_entry->dfs = dfs; - dfs_insert_node_into_bstree_for_freq(&precac_entry->tree_root, - RESTRICTED_80P80_CHAN_CENTER_FREQ, - DFS_CHWIDTH_160_VAL, - DEPTH_160_ROOT); - status = - dfs_create_precac_tree_for_freq - (dfs, - RESTRICTED_80P80_LEFT_80_CENTER_FREQ, - &precac_entry->tree_root->left_child, - DFS_CHWIDTH_80_VAL); - if (!status) - status = - dfs_create_precac_tree_for_freq( - dfs, - RESTRICTED_80P80_RIGHT_80_CENTER_FREQ, - &precac_entry->tree_root->right_child, - DFS_CHWIDTH_80_VAL); - TAILQ_INSERT_TAIL( - &dfs->dfs_precac_list, - precac_entry, pe_list); - return status; -} - -/** - * dfs_update_non_dfs_subchannel_count() - API to update the preCAC entry - * with the given non DFS subchannel count. - * @dfs: Pointer to DFS object. - * @frequency: Frequency whose corresponding preCAC entry needs to be updated. - * @count: Non DFS subchannel count for the preCAC entry. - */ -static void -dfs_update_non_dfs_subchannel_count(struct wlan_dfs *dfs, - qdf_freq_t frequency, - uint8_t count) -{ - struct dfs_precac_entry *precac_entry = NULL, *tmp_precac_entry = NULL; - - PRECAC_LIST_LOCK(dfs); - TAILQ_FOREACH_SAFE(precac_entry, - &dfs->dfs_precac_list, - pe_list, - tmp_precac_entry) { - if (IS_WITHIN_RANGE_STRICT(frequency, - precac_entry->center_ch_freq, - (precac_entry->bw/2))) { - precac_entry->non_dfs_subch_count = count; - break; - } - } - PRECAC_LIST_UNLOCK(dfs); -} - -static void -dfs_mark_non_dfs_as_precac_done(struct wlan_dfs *dfs, - uint16_t dfs_pri_ch_freq, - enum wlan_phymode mode) -{ - struct dfs_channel *ichan, lc; - - ichan = &lc; - dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj, - dfs_pri_ch_freq, - 0, - mode, - &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, - &ichan->dfs_ch_mhz_freq_seg1, - &ichan->dfs_ch_mhz_freq_seg2); - if (!WLAN_IS_CHAN_DFS(ichan)) { - PRECAC_LIST_UNLOCK(dfs); - dfs_mark_precac_done_for_freq(dfs, - ichan->dfs_ch_mhz_freq_seg1, - 0, - CH_WIDTH_80MHZ); - dfs_update_non_dfs_subchannel_count(dfs, - ichan->dfs_ch_mhz_freq_seg1, - N_SUBCHANS_FOR_80BW); - PRECAC_LIST_LOCK(dfs); - } else if (!WLAN_IS_CHAN_DFS_CFREQ2(ichan)) { - PRECAC_LIST_UNLOCK(dfs); - dfs_mark_precac_done_for_freq(dfs, - ichan->dfs_ch_mhz_freq_seg2, - 0, - CH_WIDTH_80MHZ); - dfs_update_non_dfs_subchannel_count(dfs, - ichan->dfs_ch_mhz_freq_seg2, - N_SUBCHANS_FOR_80BW); - PRECAC_LIST_LOCK(dfs); - } -} - -/* - * dfs_init_precac_list() - Initialize preCAC lists. - * @dfs: Pointer to wlan_dfs. - */ -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; - QDF_STATUS status; - struct dfs_channel_bw *dfs_max_bw_info; - int num_precac_roots; - - /* - * We need to prepare list of uniquee center frequencies of maximum - * possible bandwidths. 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. - */ - dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans); - dfs_max_bw_info = qdf_mem_malloc(nchans * - sizeof(struct dfs_channel_bw)); - if (!dfs_max_bw_info) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "memory allocation failed"); - return; - } - dfs_fill_max_bw_for_chan(dfs, dfs_max_bw_info, &num_precac_roots); - - TAILQ_INIT(&dfs->dfs_precac_list); - - PRECAC_LIST_LOCK(dfs); - for (i = 0; i < num_precac_roots; i++) { - uint16_t pri_chan_cfreq = dfs_max_bw_info[i].dfs_center_ch_freq; - - found = 0; - TAILQ_FOREACH(tmp_precac_entry, - &dfs->dfs_precac_list, - pe_list) { - if (tmp_precac_entry->center_ch_freq == - pri_chan_cfreq) { - found = 1; - break; - } - } - if (!found && pri_chan_cfreq) { - 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; - } - if (dfs_max_bw_info[i].dfs_max_bw == - DFS_CHWIDTH_165_VAL) { - status = dfs_precac_create_165mhz_precac_entry( - dfs, - precac_entry); - if (status) { - dfs_debug(dfs, - WLAN_DEBUG_DFS, - "PreCAC entry for channel 146 not created"); - continue; - } - /* The restricted 80p80 or the 165MHz channel might - * have a non DFS part with center frequency 5775. - * Mark the non DFS portion as precac done. - */ - dfs_mark_non_dfs_as_precac_done( - dfs, - dfs_max_bw_info[i].dfs_pri_ch_freq, - WLAN_PHYMODE_11AC_VHT80_80); - } else { - status = - dfs_precac_create_precac_entry(dfs, - precac_entry, - dfs_max_bw_info, - i); - if (status) - continue; - /* Some channels like 36HT160 might have a non DFS - * part. Mark the non DFS portion as precac done. - */ - dfs_mark_non_dfs_as_precac_done( - dfs, - dfs_max_bw_info[i].dfs_pri_ch_freq, - WLAN_PHYMODE_11AC_VHT160); - } - } - } - PRECAC_LIST_UNLOCK(dfs); - qdf_mem_free(dfs_max_bw_info); - - dfs_debug(dfs, WLAN_DEBUG_DFS, - "Print the list of PreCAC ieee chan from linked list"); - TAILQ_FOREACH(tmp_precac_entry, - &dfs->dfs_precac_list, - pe_list) { - uint8_t ch_ieee, bw; - - ch_ieee = utils_dfs_freq_to_chan(tmp_precac_entry->center_ch_freq); - bw = tmp_precac_entry->bw; - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "ieee=%u bw=%u", ch_ieee, bw); - } -} - -/* dfs_find_leftmost_leaf_of_precac_tree() - Find the leftmost leaf node of - * BSTree rooted by the given node. - * @node: PreCAC BSTree node whose leftmost leaf is required. - * - * Return: Pointer of struct precac_tree_node. - */ -static inline struct precac_tree_node * -dfs_find_leftmost_leaf_of_precac_tree(struct precac_tree_node *node) -{ - if (!node) - return NULL; - - while (node->left_child) - node = node->left_child; - - return node; -} - -/* - * dfs_free_precac_tree_nodes() - Free the tree nodes starting from - * the root node. - * NOTE: This changes tree structure, hence - * caller should be in a lock. - * @dfs: Pointer to WLAN DFS structure. - * @precac_entry: Precac list entry whose BSTree is to be freed. - * - * Consider the below Binary tree, - * - * A - * / \ - * B C - * \ - * D - * - * Steps for freeing this tree, - * - * 1. Find the leftmost leaf node of the Binary Tree. - * 2. Set current node as root node. - * 3. If current node has right child, add right child of current node as left - * child of leftmost leaf. - * 4. Update the leftmost leaf. - * 5. Update current node to left child and remove the node. - * 6. Repeat steps 3 to 5 till current node is NULL. - * - * The above Binary Tree structure during the afore mentioned steps: - * - * A A - * / \ / - * B C --> B --> B --> B --> C --> D --> . - * \ / \ / \ / / - * D C D C D C D - * / - * D - * - */ - -static void dfs_free_precac_tree_nodes(struct wlan_dfs *dfs, - struct dfs_precac_entry *precac_entry) -{ - struct precac_tree_node *root_node, *left_most_leaf, *prev_root_node; - - root_node = precac_entry->tree_root; - if (!root_node) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tree root is null"); - return; - } - - /* Find leftmost leaf node */ - left_most_leaf = root_node; - left_most_leaf = dfs_find_leftmost_leaf_of_precac_tree(left_most_leaf); - if (!left_most_leaf) { - /* should've been caught in previous check, assert here */ - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Could not find leaf, deletion failed! Asserting"); - QDF_ASSERT(0); - return; - } - - while (root_node) { - if (root_node->right_child) { - /* Add the right subtree as the left child of the - * leftmost leaf - */ - left_most_leaf->left_child = root_node->right_child; - /* Update left most leaf */ - left_most_leaf = dfs_find_leftmost_leaf_of_precac_tree( - left_most_leaf); - if (!left_most_leaf) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Could not find leaf, deletion failed"); - QDF_ASSERT(0); - return; - } - } - /* Free current node */ - prev_root_node = root_node; - root_node = root_node->left_child; - qdf_mem_free(prev_root_node); - } -} - -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_LIST_LOCK(dfs); - if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) - TAILQ_FOREACH_SAFE(precac_entry, - &dfs->dfs_precac_list, - pe_list, tmp_precac_entry) { - dfs_free_precac_tree_nodes(dfs, precac_entry); - TAILQ_REMOVE(&dfs->dfs_precac_list, - precac_entry, pe_list); - qdf_mem_free(precac_entry); - } - PRECAC_LIST_UNLOCK(dfs); - -} - #if defined(QCA_SUPPORT_AGILE_DFS) || defined(ATH_SUPPORT_ZERO_CAC_DFS) void dfs_agile_soc_obj_init(struct wlan_dfs *dfs, struct wlan_objmgr_psoc *psoc) @@ -2271,21 +953,7 @@ void dfs_agile_soc_obj_init(struct wlan_dfs *dfs, } #endif -void dfs_zero_cac_detach(struct wlan_dfs *dfs) -{ - dfs_deinit_precac_list(dfs); - PRECAC_LIST_LOCK_DESTROY(dfs); -} - -/** - * dfs_is_pcac_required_for_freq() - Find if given frequency is preCAC required. - * @node: Pointer to the preCAC tree Node in which the frequency is present. - * @freq: Frequency to be checked. - * - * Return: False if the frequency is not fully CAC done or in NOL, else true. - */ -static bool -dfs_is_pcac_required_for_freq(struct precac_tree_node *node, uint16_t freq) +bool dfs_is_pcac_required_for_freq(struct precac_tree_node *node, uint16_t freq) { while (node) { if (node->ch_freq == freq) { @@ -2302,188 +970,9 @@ dfs_is_pcac_required_for_freq(struct precac_tree_node *node, uint16_t freq) } #define DFS_160MHZ_SECSEG_CHAN_FREQ_OFFSET 40 -/** - * dfs_get_num_cur_subchans_in_node_freq() - Get number of excluded channels - * inside the current node. - * @dfs: Pointer to wlan_dfs structure. - * @node: Node to be checked. - * - * Return: uint8_t. - * Return the number of excluded (current operating channels in CAC) that are in - * the given tree node range. - */ -static uint8_t -dfs_get_num_cur_subchans_in_node_freq(struct wlan_dfs *dfs, - struct precac_tree_node *node) -{ - uint16_t exclude_pri_ch_freq, exclude_sec_ch_freq; - uint8_t chwidth_val = DFS_CHWIDTH_80_VAL; - uint8_t n_exclude_subchs = 0; - - exclude_pri_ch_freq = - dfs->dfs_curchan->dfs_ch_mhz_freq_seg1; - exclude_sec_ch_freq = - dfs->dfs_curchan->dfs_ch_mhz_freq_seg2; - if (WLAN_IS_CHAN_MODE_160(dfs->dfs_curchan)) { - if (exclude_sec_ch_freq < exclude_pri_ch_freq) - exclude_sec_ch_freq -= - DFS_160MHZ_SECSEG_CHAN_OFFSET; - else - exclude_sec_ch_freq += - DFS_160MHZ_SECSEG_CHAN_OFFSET; - } - - if (WLAN_IS_CHAN_MODE_20(dfs->dfs_curchan)) - chwidth_val = DFS_CHWIDTH_20_VAL; - else if (WLAN_IS_CHAN_MODE_40(dfs->dfs_curchan)) - chwidth_val = DFS_CHWIDTH_40_VAL; - - /* Check if the channel is a subset of the tree node and if it's - * currently in CAC period. This is to avoid excluding channels twice, - * one below and one in the already CACed channels exclusion (in the - * caller API). */ - if (IS_WITHIN_RANGE(exclude_pri_ch_freq, - node->ch_freq, - (node->bandwidth / 2)) && - dfs_is_pcac_required_for_freq(node, exclude_pri_ch_freq)) - n_exclude_subchs += N_SUBCHS_FOR_BANDWIDTH(chwidth_val); - if (IS_WITHIN_RANGE(exclude_sec_ch_freq, - node->ch_freq, - (node->bandwidth / 2)) && - dfs_is_pcac_required_for_freq(node, exclude_sec_ch_freq)) - n_exclude_subchs += N_SUBCHS_FOR_BANDWIDTH(chwidth_val); - return n_exclude_subchs; -} - -/* dfs_is_cac_needed_for_bst_node_for_freq() - For a requested bandwidth, find - * if the current preCAC BSTree - * node needs CAC. - * @dfs: Pointer to wlan_dfs struct. - * @node: Node to be checked. - * @req_bandwidth: bandwidth of channel requested. - * - * Return: TRUE/FALSE. - * Return true if there exists a channel of the requested bandwidth - * for the node which is not CAC done, else false. - */ -static bool -dfs_is_cac_needed_for_bst_node_for_freq(struct wlan_dfs *dfs, - struct precac_tree_node *node, - uint8_t req_bandwidth) -{ - uint8_t n_subchs_for_req_bw, n_allowed_subchs, n_excluded_subchs; - - if (!node) - return false; - - /* Find the number of subchannels for the requested bandwidth */ - n_excluded_subchs = dfs_get_num_cur_subchans_in_node_freq(dfs, node); - n_subchs_for_req_bw = N_SUBCHS_FOR_BANDWIDTH(req_bandwidth); - n_allowed_subchs = node->n_valid_subchs - - (node->n_nol_subchs + n_excluded_subchs); - - /* Return false if, - * 1. Number of allowed subchannels (all subchannels other than - * current operating sub-channels and NOL sub-channels) in the - * current node is less than the requested number of subchannels. - * 3. If the number CAC done subchannels + NOL subchannels + current - * operating subchannels in the current node is equal to number of - * valid subchannels in the node. - * else, return true. - */ - if ((n_allowed_subchs < n_subchs_for_req_bw) || - ((node->n_caced_subchs + node->n_nol_subchs + n_excluded_subchs) == - node->n_valid_subchs)) - return false; - - return true; -} - -/* dfs_find_ieee_ch_from_precac_tree_for_freq() - from the given preCAC tree, - * find a IEEE freq of the given bandwidth - * which is valid and needs CAC. - * @root: PreCAC BSTree root pointer. - * @req_bw: Bandwidth of channel requested. - * - * Return: IEEE channel frequency. - * Return a valid freq value which needs CAC for the given bandwidth, else - * return 0. - */ -static uint16_t -dfs_find_ieee_ch_from_precac_tree_for_freq(struct wlan_dfs *dfs, - struct precac_tree_node *root, - uint8_t req_bw) -{ - struct precac_tree_node *curr_node; - - if (!dfs_is_cac_needed_for_bst_node_for_freq(dfs, root, req_bw)) - return 0; - - curr_node = root; - while (curr_node) { - if (curr_node->bandwidth == req_bw) { - /* find if current node in valid state (req.) */ - if (dfs_is_cac_needed_for_bst_node_for_freq(dfs, - curr_node, - req_bw)) - return curr_node->ch_freq; - else - return 0; - } - - /* Find if we need to go to left or right subtree. - * Note: If both are available, go to left. - */ - if (!dfs_is_cac_needed_for_bst_node_for_freq( - dfs, - curr_node->left_child, - req_bw)) - curr_node = curr_node->right_child; - else - curr_node = curr_node->left_child; - } - /* If requested bandwidth is invalid, return 0 here */ - return 0; -} - #ifdef WLAN_DFS_PRECAC_AUTO_CHAN_SUPPORT #ifdef CONFIG_CHAN_FREQ_API -/** - * dfs_find_precac_state_of_node() - Find the preCAC state of the given channel. - * @channel: Channel whose preCAC state is to be found. - * @precac_entry: PreCAC entry where the channel exists. - * - * Return, enum value of type precac_chan_state. - */ -static enum precac_chan_state -dfs_find_precac_state_of_node(qdf_freq_t channel, - struct dfs_precac_entry *precac_entry) -{ - struct precac_tree_node *node = precac_entry->tree_root; - - while (node) { - if (node->ch_freq == channel) { - if (node->n_nol_subchs) - return PRECAC_NOL; - if (node->n_caced_subchs == - N_SUBCHS_FOR_BANDWIDTH(node->bandwidth)) - return PRECAC_DONE; - return PRECAC_REQUIRED; - } - node = dfs_descend_precac_tree_for_freq(node, channel); - } - return PRECAC_ERR; -} - -/** - * dfs_configure_deschan_for_precac() - API to prioritize user configured - * channel for preCAC. - * - * @dfs: Pointer to DFS of wlan_dfs structure. - * Return: frequency of type qdf_freq_t if configured, else 0. - */ -static qdf_freq_t -dfs_configure_deschan_for_precac(struct wlan_dfs *dfs) +qdf_freq_t dfs_configure_deschan_for_precac(struct wlan_dfs *dfs) { struct dfs_channel *deschan = dfs->dfs_autoswitch_chan; qdf_freq_t channels[2]; @@ -2538,73 +1027,8 @@ dfs_configure_deschan_for_precac(struct wlan_dfs *dfs) return 0; } #endif -#else -static inline qdf_freq_t -dfs_configure_deschan_for_precac(struct wlan_dfs *dfs) -{ - return 0; -} #endif -/* - * dfs_get_ieeechan_for_precac_for_freq() - Get chan frequency for preCAC. - * @dfs: Pointer to wlan_dfs. - * @exclude_pri_ch_freq: Primary frequency to be excluded. - * @exclude_sec_ch_freq: Secondary freqeuncy to be excluded. - * @bandwidth: Bandwidth. - */ -uint16_t dfs_get_ieeechan_for_precac_for_freq(struct wlan_dfs *dfs, - uint16_t exclude_pri_ch_freq, - uint16_t exclude_sec_ch_freq, - uint8_t bw) -{ - struct dfs_precac_entry *precac_entry; - struct precac_tree_node *root = NULL; - uint16_t ieee_chan_freq = 0; - - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, - "current operating channel(s) to be excluded = [%u] [%u]", - exclude_pri_ch_freq, - exclude_sec_ch_freq); - - /* If interCAC is enabled, prioritize the desired channel first before - * using the normal logic to find a channel for preCAC. */ - ieee_chan_freq = dfs_configure_deschan_for_precac(dfs); - - if (ieee_chan_freq) - goto exit; - - PRECAC_LIST_LOCK(dfs); - if (!TAILQ_EMPTY(&dfs->dfs_precac_list)) { - TAILQ_FOREACH(precac_entry, &dfs->dfs_precac_list, - pe_list) { - root = precac_entry->tree_root; - ieee_chan_freq = - dfs_find_ieee_ch_from_precac_tree_for_freq(dfs, - root, - bw); - if (ieee_chan_freq) - break; - } - } - PRECAC_LIST_UNLOCK(dfs); - -exit: - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "Channel picked for preCAC = %u", - ieee_chan_freq); - - return ieee_chan_freq; -} - -void dfs_cancel_precac_timer(struct wlan_dfs *dfs) -{ - struct dfs_soc_priv_obj *dfs_soc_obj; - - dfs_soc_obj = dfs->dfs_soc_obj; - qdf_timer_sync_cancel(&dfs_soc_obj->dfs_precac_timer); - dfs_soc_obj->dfs_precac_timer_running = 0; -} - #ifdef QCA_SUPPORT_AGILE_DFS /* FIND_IF_OVERLAP_WITH_WEATHER_RANGE() - Find if the given channel range * overlaps with the weather channel range. @@ -2721,6 +1145,7 @@ void dfs_start_agile_precac_timer(struct wlan_dfs *dfs, } #endif +#ifdef CONFIG_CHAN_FREQ_API /* * dfs_start_precac_timer_for_freq() - Start preCAC timer. * @dfs: pointer to wlan_dfs. @@ -2803,171 +1228,9 @@ void dfs_start_precac_timer_for_freq(struct wlan_dfs *dfs, "precactimeout = %d", (precac_timeout)*1000); qdf_timer_mod(&dfs_soc_obj->dfs_precac_timer, (precac_timeout) * 1000); } +#endif -/* dfs_print_node_data() - Print the precac tree node data. - * @dfs: Pointer to WLAN DFS structure. - * @node: Precac tree node pointer. - * - * Sample print for below tree: - * - * A A(C,N) - * / \ | - * B C |------- B(C,N) - * / \ | | - * D E | |------- D(C,N) - * | | - * | |------- E(C,N) - * | - * |------- E(C,N) - * - * Where C is number of CACed subchannels, and N is number of NOL subchannels. - * For each node, the prefix and previous line prefix to be printed will be - * based on the level (and by our logic, bandwidth) of the current node. - * - */ -#define MAX_PREFIX_CHAR 28 -/*Retaining IEEE to print node data */ -static void dfs_print_node_data(struct wlan_dfs *dfs, - struct precac_tree_node *node) -{ - char prefix[MAX_PREFIX_CHAR] = ""; - char prev_line_prefix[MAX_PREFIX_CHAR] = ""; - char inv[4] = "inv"; - - switch (node->depth) { - case DEPTH_160_ROOT: - break; - case DEPTH_80_ROOT: - qdf_str_lcopy(prev_line_prefix, "|", MAX_PREFIX_CHAR); - qdf_str_lcopy(prefix, "|------- ", MAX_PREFIX_CHAR); - break; - case DEPTH_40_ROOT: - qdf_str_lcopy(prev_line_prefix, "| |", MAX_PREFIX_CHAR); - qdf_str_lcopy(prefix, "| |------- ", MAX_PREFIX_CHAR); - break; - case DEPTH_20_ROOT: - qdf_str_lcopy(prev_line_prefix, - "| | |", - MAX_PREFIX_CHAR); - qdf_str_lcopy(prefix, - "| | |------- ", - MAX_PREFIX_CHAR); - break; - default: - return; - } - - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s", prev_line_prefix); - /* if current node is not a valid ic channel, print invalid */ - if (node->n_valid_subchs != N_SUBCHS_FOR_BANDWIDTH(node->bandwidth)) - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s%s", prefix, inv); - else - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "%s%u(%u,%u)", - prefix, - node->ch_ieee, - node->n_caced_subchs, - node->n_nol_subchs); -} - -/* dfs_print_precac_tree_nodes() - print the precac tree nodes using - * preorder traversal. (Root-Left-Right) - * @dfs: WLAN DFS structure. - * @precac_entry: A single entry in the precac list. - * - * Algorithm used - Morris preorder tree traversal (iterative). - * - * Description of Algorithm: - * Consider the below tree, Preorder sequence (A, B, D, E, C) - * - * A - * / \ - * B C - * / \ - * D E - * - * What is inorder predecessor? - * - * For a given node, the inorder predecessor of the node is - * the rightmost node of the left subtree. - * For e.g.: In our tree, E is the predecessor of A, - * D is the predecessor of B. - * - * Steps for Algorithm: - * Starting from the root node as the current node, - * 1) If there is no left child, print current node data and go to right child. - * 2) If the left child exists, - * 2.1) Find the inorder predecessor of the current node. - * 2.2) If the predecessor's right child is - * 2.2.1) NULL, then - * A) Print current node. - * B) Make the predecessor's right child as the current node. - * C) Go to left child. - * 2.2.2) Current node, then - * A) Make the predecessor's right child as NULL. - * B) Go to the right child. - * 3) Repeat 1 & 2 till current node is NULL. - * - * The above Binary Tree structure during the afore mentioned steps: - * Note: Nodes with '[]' are printed. - * - * A [A] [A] [A] [A] [A] [A] - * / \ /|\ /|\ /|\ /|\ / \ / \ - * B C --> B | C --> [B] | C --> [B] | C --> [B]| C --> [B] C --> [B] [C] - * / \ / \| // \| // \| / \| / \ / \ - * D E D E D E [D] E [D] E [D] [E] [D] [E] - * - */ -static void dfs_print_precac_tree_nodes(struct wlan_dfs *dfs, - struct dfs_precac_entry *precac_entry) -{ - struct precac_tree_node *root = precac_entry->tree_root; - struct precac_tree_node *curr_node, *inorder_predecessor; - - if (!root) - return; - curr_node = root; - while (curr_node) { - if (!curr_node->left_child) { - dfs_print_node_data(dfs, curr_node); - curr_node = curr_node->right_child; - } else { - /* Find the right most leaf node of the left subtree. */ - inorder_predecessor = curr_node->left_child; - while (inorder_predecessor->right_child && - inorder_predecessor->right_child != curr_node) - inorder_predecessor = - inorder_predecessor->right_child; - - /* If the right most child of left subtree already - * is linked to current node. We have traversed - * left subtree. Remove the link and go to right - * subtree - */ - if (inorder_predecessor->right_child == curr_node) { - inorder_predecessor->right_child = NULL; - curr_node = curr_node->right_child; - } else { - /* Print current node data, make current node - * as predecessor's right child, and move to left child. - */ - dfs_print_node_data(dfs, curr_node); - inorder_predecessor->right_child = curr_node; - curr_node = curr_node->left_child; - } - } - } -} - -/** - * dfs_is_precac_completed_count_non_zero() - API to find if the preCAC - * completed channels count is zero/non_zero. - * @dfs: Pointer to DFS object. - * - * Return true, if there exists atleast one node/subchannel in the preCAC list - * that is CAC done, else return false. - */ -static bool -dfs_is_precac_completed_count_non_zero(struct wlan_dfs *dfs) +bool dfs_is_precac_completed_count_non_zero(struct wlan_dfs *dfs) { struct dfs_precac_entry *precac_entry = NULL; @@ -2993,62 +1256,6 @@ dfs_is_precac_completed_count_non_zero(struct wlan_dfs *dfs) return false; } -#ifdef ATH_SUPPORT_ZERO_CAC_DFS -enum precac_status_for_chan -dfs_precac_status_for_channel(struct wlan_dfs *dfs, - struct dfs_channel *deschan) -{ - if (!dfs_is_precac_completed_count_non_zero(dfs)) - return DFS_NO_PRECAC_COMPLETED_CHANS; - - if (dfs_is_precac_done(dfs, deschan)) - return DFS_PRECAC_COMPLETED_CHAN; - - return DFS_PRECAC_REQUIRED_CHAN; -} -#endif - -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, - "Precac status of all nodes in the list:"); - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, - "NOTE: Syntax for each node: (,)"); - TAILQ_FOREACH(tmp_precac_entry, - &dfs->dfs_precac_list, - pe_list) { - dfs_print_precac_tree_nodes(dfs, tmp_precac_entry); - } - 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); -} - /* * dfs_set_precac_preferred_channel() - Set preCAC preferred channel. * @dfs: Pointer to wlan_dfs. @@ -3180,67 +1387,6 @@ exit: return false; } -enum precac_chan_state -dfs_get_precac_chan_state_for_freq(struct wlan_dfs *dfs, uint16_t pcac_freq) -{ - struct dfs_channel chan; - struct dfs_precac_entry *tmp_precac_entry; - struct precac_tree_node *root = NULL; - enum precac_chan_state ret = PRECAC_ERR; - - qdf_mem_zero(&chan, sizeof(struct dfs_channel)); - if (QDF_STATUS_SUCCESS != - dfs_mlme_find_dot11_chan_for_freq(dfs->dfs_pdev_obj, - pcac_freq, 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, - &chan.dfs_ch_mhz_freq_seg1, - &chan.dfs_ch_mhz_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", pcac_freq); - return PRECAC_ERR; - } - - PRECAC_LIST_LOCK(dfs); - if (dfs_is_precac_timer_running(dfs)) { - tmp_precac_entry = TAILQ_FIRST(&dfs->dfs_precac_list); - if (tmp_precac_entry && (tmp_precac_entry->vht80_ch_freq == - chan.dfs_ch_mhz_freq_seg1)) { - ret = PRECAC_NOW; - goto end; - } - } - - TAILQ_FOREACH(tmp_precac_entry, - &dfs->dfs_precac_list, pe_list) { - if (tmp_precac_entry->vht80_ch_freq == - chan.dfs_ch_mhz_freq_seg1) { - root = tmp_precac_entry->tree_root; - if (root->n_nol_subchs) - ret = PRECAC_NOL; - else if (root->n_caced_subchs == - N_SUBCHS_FOR_BANDWIDTH(root->bandwidth)) - ret = PRECAC_DONE; - else - ret = PRECAC_REQUIRED; - goto end; - } - } -end: - PRECAC_LIST_UNLOCK(dfs); - return ret; -} #endif #ifdef QCA_SUPPORT_AGILE_DFS @@ -3809,7 +1955,7 @@ void dfs_set_agilecac_chan_for_freq(struct wlan_dfs *dfs, } #endif - +#ifdef CONFIG_CHAN_FREQ_API /* * dfs_find_vht80_chan_for_precac_for_freq() - Find VHT80 channel for preCAC. * @dfs: Pointer to wlan_dfs. @@ -3926,89 +2072,7 @@ void dfs_find_vht80_chan_for_precac_for_freq(struct wlan_dfs *dfs, } /* End of if(ieee_freq) */ } /* End of if(dfs_is_legacy_precac_enabled(dfs)) */ } - -void dfs_set_precac_enable(struct wlan_dfs *dfs, uint32_t value) -{ - struct wlan_objmgr_psoc *psoc; - struct wlan_lmac_if_target_tx_ops *tgt_tx_ops; - uint32_t target_type; - struct target_psoc_info *tgt_hdl; - struct tgt_info *info; - struct wlan_lmac_if_tx_ops *tx_ops; - - psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); - if (!psoc) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "psoc is NULL"); - dfs->dfs_legacy_precac_ucfg = 0; - dfs->dfs_agile_precac_ucfg = 0; - return; - } - - tx_ops = wlan_psoc_get_lmac_if_txops(psoc); - if (!tx_ops) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tx_ops is NULL"); - return; - } - - tgt_tx_ops = &tx_ops->target_tx_ops; - target_type = lmac_get_target_type(dfs->dfs_pdev_obj); - - tgt_hdl = wlan_psoc_get_tgt_if_handle(psoc); - if (!tgt_hdl) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "target_psoc_info is null"); - return; - } - - info = (struct tgt_info *)(&tgt_hdl->info); - - if (!info) { - dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "tgt_info is null"); - return; - } - - /* - * If - * 1) The chip is CASCADE, - * 2) The user has enabled Pre-CAC and - * 3) The regdomain the ETSI, - * then enable preCAC. - * - * OR - * - * If - * 1) The chip has agile_capability enabled - * 2) The user has enabled Pre-CAC and - * 3) The regdomain the ETSI, - * then enable Agile preCAC. - */ - - if ((1 == value) && - (utils_get_dfsdomain(dfs->dfs_pdev_obj) == DFS_ETSI_DOMAIN)) { - if (tgt_tx_ops->tgt_is_tgt_type_qca9984(target_type)) - dfs->dfs_legacy_precac_ucfg = value; - else - dfs->dfs_agile_precac_ucfg = value; - } else { - dfs->dfs_agile_precac_ucfg = 0; - dfs->dfs_legacy_precac_ucfg = 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"); - if (tgt_tx_ops->tgt_is_tgt_type_qca9984(target_type)) { - dfs_cancel_precac_timer(dfs); - dfs->dfs_soc_obj->precac_state_started = 0; - } else { -#ifdef QCA_SUPPORT_AGILE_DFS - dfs_agile_sm_deliver_evt(dfs->dfs_soc_obj, - DFS_AGILE_SM_EV_AGILE_STOP, - 0, (void *)dfs); #endif - } - } -} #ifdef QCA_SUPPORT_AGILE_DFS void dfs_agile_precac_start(struct wlan_dfs *dfs) @@ -4115,976 +2179,6 @@ void dfs_set_fw_adfs_support(struct wlan_dfs *dfs, } #endif -void dfs_reinit_precac_lists(struct wlan_dfs *src_dfs, - struct wlan_dfs *dest_dfs, - uint16_t low_5g_freq, - uint16_t high_5g_freq) -{ - struct dfs_precac_entry *tmp_precac_entry, *tmp_precac_entry2; - - /* If the destination DFS is not adhering ETSI (or) - * if the source DFS does not have any lists, return (nothing to do). - */ - if (utils_get_dfsdomain(dest_dfs->dfs_pdev_obj) != DFS_ETSI_DOMAIN || - TAILQ_EMPTY(&src_dfs->dfs_precac_list)) - return; - - /* If dest_dfs and src_dfs are same it will cause dead_lock. */ - if (dest_dfs == src_dfs) - return; - - PRECAC_LIST_LOCK(dest_dfs); - if (TAILQ_EMPTY(&dest_dfs->dfs_precac_list)) - TAILQ_INIT(&dest_dfs->dfs_precac_list); - PRECAC_LIST_LOCK(src_dfs); - TAILQ_FOREACH(tmp_precac_entry, - &src_dfs->dfs_precac_list, - pe_list) { - if (low_5g_freq <= tmp_precac_entry->vht80_ch_freq && - high_5g_freq >= tmp_precac_entry->vht80_ch_freq) { - /* If the destination DFS already have the entries for - * some reason, remove them and update with the active - * entry in the source DFS list. - */ - TAILQ_FOREACH(tmp_precac_entry2, - &dest_dfs->dfs_precac_list, - pe_list) { - if (tmp_precac_entry2->vht80_ch_freq == - tmp_precac_entry->vht80_ch_freq) - TAILQ_REMOVE(&dest_dfs->dfs_precac_list, - tmp_precac_entry2, - pe_list); - } - TAILQ_REMOVE(&src_dfs->dfs_precac_list, - tmp_precac_entry, - pe_list); - tmp_precac_entry->dfs = dest_dfs; - TAILQ_INSERT_TAIL(&dest_dfs->dfs_precac_list, - tmp_precac_entry, - pe_list); - } - } - PRECAC_LIST_UNLOCK(src_dfs); - PRECAC_LIST_UNLOCK(dest_dfs); -} - -#ifdef QCA_SUPPORT_AGILE_DFS -/* dfs_start_agile_engine() - Prepare ADFS params and program the agile - * engine sending agile config cmd to FW. - * @dfs: Pointer to struct wlan_dfs. - */ -void dfs_start_agile_engine(struct wlan_dfs *dfs) -{ - struct dfs_agile_cac_params adfs_param; - struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; - struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; - - /* Fill the RCAC ADFS params and send it to FW. - * FW does not use RCAC timeout values for RCAC feature. - * FW runs an infinite timer. - */ - dfs_fill_adfs_chan_params(dfs, &adfs_param); - adfs_param.min_precac_timeout = MIN_RCAC_DURATION; - adfs_param.max_precac_timeout = MAX_RCAC_DURATION; - adfs_param.ocac_mode = QUICK_RCAC_MODE; - - qdf_info("%s : %d RCAC channel request sent for pdev: %pK " - "ch_freq: %d", __func__, __LINE__, dfs->dfs_pdev_obj, - dfs->dfs_agile_precac_freq_mhz); - - dfs_tx_ops = wlan_psoc_get_dfs_txops(dfs_soc_obj->psoc); - - if (dfs_tx_ops && dfs_tx_ops->dfs_agile_ch_cfg_cmd) - dfs_tx_ops->dfs_agile_ch_cfg_cmd(dfs->dfs_pdev_obj, - &adfs_param); - else - dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS, - "dfs_tx_ops=%pK", dfs_tx_ops); -} - -/** - * --------------------- ROLLING CAC STATE MACHINE ---------------------- - * - * Rolling CAC is a feature where in, a separate hardware (Agile detector) - * will be brought up in a channel that is not the current operating channel - * and will continue to monitor the channel non-stop, until the next - * channel change or radar in this RCAC channel. - * - * Now if the Rolling CAC channel was radar free for a minimum duration - * (1 min.) and the device is now switching to this channel, no CAC is required. - * - * I.e. let's say the current operating channel is 64 HT80 and we are starting - * the agile detector in 100 HT80. After a minute of being up in 100 HT80, we - * switch the radio to 100 HT80. This operating channel change will not - * require CAC now since the channel was radar free for the last 1 minute, - * as determined by the agile detector. - * - * Introduction of a rolling CAC state machine: - * - * To acheive the rolling CAC feature using the agile detector, a trivial - * state machine is implemented, as represented below: - * - * _________________ - * | | - * |------------>| INIT |<-----------| - * | |_________________| | - * | | | - * | | | - * | [EV_RCAC_STOP] | [EV_RCAC_START] | [EV_RCAC_STOP] - * | [EV_ADFS_RADAR] | | [EV_ADFS_RADAR] - * | | | - * | | | - * ________|________ | ________|________ - * | | |----------->| | - * | COMPLETE | | RUNNING | - * |_________________|<-------------------------|_________________| - * [EV_RCAC_DONE] - * - * - * - * Legend: - * _________________ - * | | - * 1. | RCAC STATES | - * |_________________| - * - * 2. [RCAC_EVENTS] - * - * - * Event triggers and handlers description: - * - * EV_RCAC_START: - * Posted from vdev response and is handled by all three states. - * 1. INIT handler: - * a. Check if RCAC is already running, - * - If yes, do not transition. - * - If no, go to step b. - * b. Check if a new RCAC channel can be found, - * - If no, do not transition. - * - If yes, transition to RUNNING. - * - * EV_RCAC_STOP: - * Posted from last vap down or config disable, handled by RUNNING - * and COMPLETE. - * 1. RUNNING handler: - * a. Stop the HOST RCAC timer. - * b. Send wmi_adfs_abort_cmd to FW and transition to INIT. - * 2. COMPLETE handler: - * a. Send wmi_adfs_abort_cmd to FW and transition to INIT. - * - * EV_ADFS_RADAR: - * Posted from radar detection and is handled in RUNNING and COMPLETE. - * 1. RUNNING handler (same as EV_RCAC_START): - * a. Check if RCAC was running for this pdev, - * - If yes, transition to INIT and post EV_RCAC_START event. - * - If no, ignore. - * 2. COMPLETE handler (same as EV_RCAC_START): - * a. Check if RCAC was running for this pdev, - * - If yes, transition to INIT and post EV_RCAC_START event. - * - If no, ignore. - * - * Note: EV_ADFS_RADAR works same as EV_RCAC_START event right now, but - * will change in future, where, based on user preference, either - * a new RCAC channel will be picked (requiring the transition to - * INIT like present), or RCAC will be restarted on the same channel. - * - * EV_RCAC_DONE: - * Posted from host RCAC timer completion and is handled in RUNNING. - * 1. RUNNING handler: - * a. mark RCAC done and transition to COMPLETE. - * - * Epilogue: - * Rolling CAC state machine is for the entire psoc and since the - * agile detector can run for one pdev at a time, sharing of resource is - * required. - * In case of ETSI preCAC, sharing was done in a round robin fashion where - * each pdev runs ADFS for it's channels alternatively. However, in RCAC, the - * CAC period is not defined is continuous till the next channel change. - * - * Hence ADFS detector is shared as follows: - * 1. First come first serve: the pdev that is brought up first, i.e, for - * the first vdev response, an RCAC_START is posted and this pdev will - * hold the agile detector and run RCAC till it is stopped. - * 2. Stopping the RCAC can be either by disabling user config "rcac_en 0" - * or by bringing down all vaps, or if no channel is available. - * 3. Once RCAC is stopped for a pdev, it can be started in the other pdev - * by restarting it's vap (i.e. a vdev response). - * - * A working sequence of RCAC is as follows: - * - Consider that the channel configured during bring up is 52HT80. - * 1. The First VAP's vdev_start_resp posts an event EV_RCAC_START to the - * RCAC state machine. - * 2. The RCAC state machine which is in INIT state (default) receives the - * event, picks a channel to do rolling CAC on, e.g. channel 100HT80. - * The SM is then transitioned to RUNNING state. - * 3. In the entry of RUNNING state, a host timer is started and agile - * cfg cmd to FW is sent. - * 4. When the HOST timer expires, it posts the EV_RCAC_DONE event to - * the state machine. - * 5. EV_RCAC_DONE event received in RUNNING state, transitions the SM - * to COMPLETE. - * 6. In the entry of COMPLETE, the RCAC channel is marked as CAC done - * in the precac tree. - * 7. If radar is detected on primary channel, the new channel is the - * RCAC channel (100HT80) which does not require CAC if the preCAC - * tree is marked as CAC done. - * Before sending vdev_start, an EV_RCAC_STOP is posted - * which moves the SM to INIT state clearing all the params and - * bringing down the agile detector. - * (CAC decisions are taken before). - * 8. After vdev_resp, another EV_RCAC_START is sent to restart the - * RCAC SM with a new RCAC channel if available. - * - * A future enhancement will be triggering RCAC_START at user level. - */ - -/** - * dfs_agile_set_curr_state() - API to set the current state of Agile SM. - * @dfs_soc_obj: Pointer to DFS soc private object. - * @state: value of current state. - * - * Return: void. - */ -static void dfs_agile_set_curr_state(struct dfs_soc_priv_obj *dfs_soc_obj, - enum dfs_agile_sm_state state) -{ - if (state < DFS_AGILE_S_MAX) { - dfs_soc_obj->dfs_agile_sm_cur_state = state; - } else { - dfs_err(NULL, WLAN_DEBUG_DFS_ALWAYS, - "DFS RCAC state (%d) is invalid", state); - QDF_BUG(0); - } -} - -/** - * dfs_agile_get_curr_state() - API to get current state of Agile SM. - * @dfs_soc_obj: Pointer to DFS soc private object. - * - * Return: current state enum of type, dfs_rcac_sm_state. - */ -static enum dfs_agile_sm_state -dfs_agile_get_curr_state(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - return dfs_soc_obj->dfs_agile_sm_cur_state; -} - -/** - * dfs_rcac_sm_transition_to() - Wrapper API to transition the Agile SM state. - * @dfs_soc_obj: Pointer to dfs soc private object that hold the SM handle. - * @state: State to which the SM is transitioning to. - * - * Return: void. - */ -static void dfs_agile_sm_transition_to(struct dfs_soc_priv_obj *dfs_soc_obj, - enum dfs_agile_sm_state state) -{ - wlan_sm_transition_to(dfs_soc_obj->dfs_agile_sm_hdl, state); -} - -/** - * dfs_agile_sm_deliver_event() - API to post events to Agile SM. - * @dfs_soc_obj: Pointer to dfs soc private object. - * @event: Event to be posted to the RCAC SM. - * @event_data_len: Length of event data. - * @event_data: Pointer to event data. - * - * Return: QDF_STATUS_SUCCESS on handling the event, else failure. - * - * Note: This version of event posting API is not under lock and hence - * should only be called for posting events within the SM and not be - * under a dispatcher API without a lock. - */ -static -QDF_STATUS dfs_agile_sm_deliver_event(struct dfs_soc_priv_obj *dfs_soc_obj, - enum dfs_agile_sm_evt event, - uint16_t event_data_len, - void *event_data) -{ - return wlan_sm_dispatch(dfs_soc_obj->dfs_agile_sm_hdl, - event, - event_data_len, - event_data); -} - -#ifdef QCA_SUPPORT_ADFS_RCAC -/* dfs_start_agile_rcac_timer() - Start host agile RCAC timer. - * - * @dfs: Pointer to struct wlan_dfs. - */ -void dfs_start_agile_rcac_timer(struct wlan_dfs *dfs) -{ - struct dfs_soc_priv_obj *dfs_soc_obj = dfs->dfs_soc_obj; - uint32_t rcac_timeout = MIN_RCAC_DURATION; - - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Host RCAC timeout = %d ms", rcac_timeout); - - qdf_timer_mod(&dfs_soc_obj->dfs_rcac_timer, - rcac_timeout); -} - - -/* dfs_stop_agile_rcac_timer() - Cancel the RCAC timer. - * - * @dfs: Pointer to struct wlan_dfs. - */ -void dfs_stop_agile_rcac_timer(struct wlan_dfs *dfs) -{ - struct dfs_soc_priv_obj *dfs_soc_obj; - - dfs_soc_obj = dfs->dfs_soc_obj; - qdf_timer_sync_cancel(&dfs_soc_obj->dfs_rcac_timer); -} - - -/** - * dfs_abort_agile_rcac() - Send abort Agile RCAC to F/W. - * @dfs: Pointer to struct wlan_dfs. - */ -static void dfs_abort_agile_rcac(struct wlan_dfs *dfs) -{ - - struct wlan_objmgr_psoc *psoc; - struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; - - dfs_stop_agile_rcac_timer(dfs); - psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); - dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc); - if (dfs_tx_ops && dfs_tx_ops->dfs_ocac_abort_cmd) - dfs_tx_ops->dfs_ocac_abort_cmd(dfs->dfs_pdev_obj); - - qdf_mem_zero(&dfs->dfs_rcac_param, sizeof(struct dfs_rcac_params)); - dfs->dfs_agile_precac_freq_mhz = 0; - dfs->dfs_precac_chwidth = CH_WIDTH_INVALID; - dfs->dfs_soc_obj->cur_agile_dfs_index = DFS_PSOC_NO_IDX; -} -#else -static inline void dfs_abort_agile_rcac(struct wlan_dfs *dfs) -{ -} -#endif - -/* dfs_abort_agile_precac() - Reset parameters of wlan_dfs and send abort - * to F/W. - * @dfs: Pointer to struct wlan_dfs. - */ -static void dfs_abort_agile_precac(struct wlan_dfs *dfs) -{ - struct wlan_objmgr_psoc *psoc; - struct wlan_lmac_if_dfs_tx_ops *dfs_tx_ops; - - psoc = wlan_pdev_get_psoc(dfs->dfs_pdev_obj); - dfs_tx_ops = wlan_psoc_get_dfs_txops(psoc); - - dfs_agile_precac_cleanup(dfs); - /*Send the abort to F/W as well */ - if (dfs_tx_ops && dfs_tx_ops->dfs_ocac_abort_cmd) - dfs_tx_ops->dfs_ocac_abort_cmd(dfs->dfs_pdev_obj); -} - -/** - * dfs_agile_state_init_entry() - Entry API for INIT state - * @ctx: DFS SoC private object - * - * API to perform operations on moving to INIT state - * - * Return: void - */ -static void dfs_agile_state_init_entry(void *ctx) -{ - struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; - - dfs_agile_set_curr_state(dfs_soc, DFS_AGILE_S_INIT); -} - -/** - * dfs_agile_state_init_exit() - Exit API for INIT state - * @ctx: DFS SoC private object - * - * API to perform operations on moving out of INIT state - * - * Return: void - */ -static void dfs_agile_state_init_exit(void *ctx) -{ - /* NO OPS */ -} - -/** - * dfs_init_agile_start_evt_handler() - Init state start event handler. - * @dfs: Instance of wlan_dfs structure. - * @dfs_soc: DFS SoC private object - * - * Return : True if PreCAC/RCAC chan is found. - */ -static bool dfs_init_agile_start_evt_handler(struct wlan_dfs *dfs, - struct dfs_soc_priv_obj *dfs_soc) -{ - bool is_chan_found = false; - - /*For RCAC */ - if (dfs_is_agile_rcac_enabled(dfs)) { - /* Check if feature is enabled for this DFS and if RCAC channel - * is valid, if those are true, send appropriate WMIs to FW - * and only then transition to the state as follows. - */ - dfs_prepare_agile_rcac_channel(dfs, &is_chan_found); - } - /*For PreCAC */ - else if (dfs_is_agile_precac_enabled(dfs)) { - dfs_soc->dfs_priv[dfs->dfs_psoc_idx].agile_precac_active - = true; - if (!dfs_soc->precac_state_started && - !dfs_soc->dfs_precac_timer_running) { - dfs_soc->precac_state_started = true; - dfs_prepare_agile_precac_chan(dfs, &is_chan_found); - } - } - - return is_chan_found; -} - -/** - * dfs_agile_state_init_event() - INIT State event handler - * @ctx: DFS SoC private object - * @event: Event posted to the SM. - * @event_data_len: Length of event data. - * @event_data: Pointer to event data. - * - * API to handle events in INIT state - * - * Return: TRUE: on handling event - * FALSE: on ignoring the event - */ -static bool dfs_agile_state_init_event(void *ctx, - uint16_t event, - uint16_t event_data_len, - void *event_data) -{ - struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; - bool status; - struct wlan_dfs *dfs; - bool is_chan_found; - - if (!event_data) - return false; - - dfs = (struct wlan_dfs *)event_data; - - switch (event) { - case DFS_AGILE_SM_EV_AGILE_START: - - if (dfs_soc->cur_agile_dfs_index != DFS_PSOC_NO_IDX) - return true; - - is_chan_found = dfs_init_agile_start_evt_handler(dfs, - dfs_soc); - if (is_chan_found) { - dfs_soc->cur_agile_dfs_index = dfs->dfs_psoc_idx; - dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_RUNNING); - } else { - /* - * This happens when there is no preCAC chan - * in any of the radios - */ - dfs_agile_precac_cleanup(dfs); - /* Cleanup and wait */ - } - - status = true; - break; - default: - status = false; - break; - } - - return status; -} - -/** - * dfs_agile_state_running_entry() - Entry API for running state - * @ctx: DFS SoC private object - * - * API to perform operations on moving to running state - * - * Return: void - */ -static void dfs_agile_state_running_entry(void *ctx) -{ - struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; - struct wlan_dfs *dfs = - dfs_soc->dfs_priv[dfs_soc->cur_agile_dfs_index].dfs; - - dfs_agile_set_curr_state(dfs_soc, DFS_AGILE_S_RUNNING); - - /* RCAC */ - if (dfs_is_agile_rcac_enabled(dfs)) { - dfs_start_agile_rcac_timer(dfs); - dfs_start_agile_engine(dfs); - } -} - -/** - * dfs_agile_state_running_exit() - Exit API for RUNNING state - * @ctx: DFS SoC private object - * - * API to perform operations on moving out of RUNNING state - * - * Return: void - */ -static void dfs_agile_state_running_exit(void *ctx) -{ - /* NO OPS */ -} - -/** - * dfs_agile_state_running_event() - RUNNING State event handler - * @ctx: DFS SoC private object - * @event: Event posted to the SM. - * @event_data_len: Length of event data. - * @event_data: Pointer to event data. - * - * API to handle events in RUNNING state - * - * Return: TRUE: on handling event - * FALSE: on ignoring the event - */ -static bool dfs_agile_state_running_event(void *ctx, - uint16_t event, - uint16_t event_data_len, - void *event_data) -{ - struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; - bool status; - struct wlan_dfs *dfs; - bool is_cac_done_on_des_chan; - - if (!event_data) - return false; - - dfs = (struct wlan_dfs *)event_data; - - if (dfs->dfs_psoc_idx != dfs_soc->cur_agile_dfs_index) - return false; - - switch (event) { - case DFS_AGILE_SM_EV_ADFS_RADAR: - /* After radar is found on the Agile channel we need to find - * a new channel and then start Agile CAC on that. - * On receiving the "DFS_AGILE_SM_EV_ADFS_RADAR_FOUND" if - * we change the state from [RUNNING] -> [RUNNING] then - * [RUNNING] should handle case in which a channel is not found - * and bring the state machine back to INIT. - * Instead we move the state to INIT and post the event - * "DFS_AGILE_SM_EV_AGILE_START" so INIT handles the case of - * channel not found and stay in that state. - * Abort the existing RCAC and restart from INIT state. - */ - if (dfs_is_agile_rcac_enabled(dfs)) - dfs_abort_agile_rcac(dfs); - else if (dfs_is_agile_precac_enabled(dfs)) - dfs_abort_agile_precac(dfs); - - dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); - dfs_agile_sm_deliver_event(dfs_soc, - DFS_AGILE_SM_EV_AGILE_START, - event_data_len, - event_data); - - status = true; - break; - case DFS_AGILE_SM_EV_AGILE_STOP: - if (dfs_is_agile_rcac_enabled(dfs)) - dfs_abort_agile_rcac(dfs); - else if (dfs_is_agile_precac_enabled(dfs)) - dfs_abort_agile_precac(dfs); - - dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); - status = true; - break; - case DFS_AGILE_SM_EV_AGILE_DONE: - if (dfs_is_agile_precac_enabled(dfs)) { - if (dfs_soc->ocac_status == OCAC_SUCCESS) { - dfs_soc->ocac_status = OCAC_RESET; - dfs_mark_adfs_chan_as_cac_done(dfs); - } - dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); - dfs_agile_precac_cleanup(dfs); - is_cac_done_on_des_chan = - dfs_precac_check_home_chan_change(dfs); - if (!is_cac_done_on_des_chan) { - dfs_agile_sm_deliver_event(dfs_soc, - DFS_AGILE_SM_EV_AGILE_START, - event_data_len, - event_data); - } - } else if (dfs_is_agile_rcac_enabled(dfs)) { - dfs_agile_sm_transition_to(dfs_soc, - DFS_AGILE_S_COMPLETE); - } - status = true; - default: - status = false; - break; - } - - return status; -} - -/** - * dfs_agile_state_complete_entry() - Entry API for complete state - * @ctx: DFS SoC private object - * - * API to perform operations on moving to complete state - * - * Return: void - */ -static void dfs_agile_state_complete_entry(void *ctx) -{ - struct dfs_soc_priv_obj *dfs_soc_obj = (struct dfs_soc_priv_obj *)ctx; - struct wlan_dfs *dfs; - - dfs_agile_set_curr_state(dfs_soc_obj, DFS_AGILE_S_COMPLETE); - - if (!(dfs_soc_obj->cur_agile_dfs_index < WLAN_UMAC_MAX_PDEVS)) - return; - - dfs = dfs_soc_obj->dfs_priv[dfs_soc_obj->cur_agile_dfs_index].dfs; - - /* Mark the RCAC channel as CAC done. */ - dfs_mark_adfs_chan_as_cac_done(dfs); -} - -/** - * dfs_agile_state_complete_exit() - Exit API for complete state - * @ctx: DFS SoC private object - * - * API to perform operations on moving out of complete state - * - * Return: void - */ -static void dfs_agile_state_complete_exit(void *ctx) -{ - /* NO OPs. */ -} - -/** - * dfs_agile_state_complete_event() - COMPLETE State event handler - * @ctx: DFS SoC private object - * @event: Event posted to the SM. - * @event_data_len: Length of event data. - * @event_data: Pointer to event data. - * - * API to handle events in COMPLETE state - * - * Return: TRUE: on handling event - * FALSE: on ignoring the event - */ -static bool dfs_agile_state_complete_event(void *ctx, - uint16_t event, - uint16_t event_data_len, - void *event_data) -{ - struct dfs_soc_priv_obj *dfs_soc = (struct dfs_soc_priv_obj *)ctx; - bool status; - struct wlan_dfs *dfs; - - if (!event_data) - return false; - - dfs = (struct wlan_dfs *)event_data; - - if (dfs->dfs_psoc_idx != dfs_soc->cur_agile_dfs_index) - return false; - - switch (event) { - case DFS_AGILE_SM_EV_ADFS_RADAR: - /* Reset the RCAC done state for this RCAC chan of this dfs. - * Unmark the channels for RCAC done before calling abort API as - * the abort API invalidates the cur_agile_dfs_index. - */ - dfs_unmark_rcac_done(dfs); - /* Abort the existing RCAC and restart from INIT state. */ - dfs_abort_agile_rcac(dfs); - dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); - dfs_agile_sm_deliver_event(dfs_soc, - DFS_AGILE_SM_EV_AGILE_START, - event_data_len, - event_data); - status = true; - break; - case DFS_AGILE_SM_EV_AGILE_STOP: - /* Reset the RCAC done state for this RCAC chan of this dfs. - * Unmark the channels for RCAC done before calling abort API as - * the abort API invalidates the cur_agile_dfs_index. - */ - dfs_unmark_rcac_done(dfs); - dfs_abort_agile_rcac(dfs); - dfs_agile_sm_transition_to(dfs_soc, DFS_AGILE_S_INIT); - status = true; - break; - default: - status = false; - break; - } - - return status; -} - -static struct wlan_sm_state_info dfs_agile_sm_info[] = { - { - (uint8_t)DFS_AGILE_S_INIT, - (uint8_t)WLAN_SM_ENGINE_STATE_NONE, - (uint8_t)WLAN_SM_ENGINE_STATE_NONE, - false, - "INIT", - dfs_agile_state_init_entry, - dfs_agile_state_init_exit, - dfs_agile_state_init_event - }, - { - (uint8_t)DFS_AGILE_S_RUNNING, - (uint8_t)WLAN_SM_ENGINE_STATE_NONE, - (uint8_t)WLAN_SM_ENGINE_STATE_NONE, - false, - "RUNNING", - dfs_agile_state_running_entry, - dfs_agile_state_running_exit, - dfs_agile_state_running_event - }, - { - (uint8_t)DFS_AGILE_S_COMPLETE, - (uint8_t)WLAN_SM_ENGINE_STATE_NONE, - (uint8_t)WLAN_SM_ENGINE_STATE_NONE, - false, - "COMPLETE", - dfs_agile_state_complete_entry, - dfs_agile_state_complete_exit, - dfs_agile_state_complete_event - }, -}; - -static const char *dfs_agile_sm_event_names[] = { - "EV_AGILE_START", - "EV_AGILE_STOP", - "EV_AGILE_DONE", - "EV_ADFS_RADAR_FOUND", -}; - -/** - * dfs_agile_sm_print_state() - API to log the current state. - * @dfs_soc_obj: Pointer to dfs soc private object. - * - * Return: void. - */ -static void dfs_agile_sm_print_state(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - enum dfs_agile_sm_state state; - - state = dfs_agile_get_curr_state(dfs_soc_obj); - if (!(state < DFS_AGILE_S_MAX)) - return; - - dfs_debug(NULL, WLAN_DEBUG_DFS_AGILE, "->[%s] %s", - dfs_soc_obj->dfs_agile_sm_hdl->name, - dfs_agile_sm_info[state].name); -} - -/** - * dfs_agile_sm_print_state_event() - API to log the current state and event - * received. - * @dfs_soc_obj: Pointer to dfs soc private object. - * @event: Event posted to RCAC SM. - * - * Return: void. - */ -static void dfs_agile_sm_print_state_event(struct dfs_soc_priv_obj *dfs_soc_obj, - enum dfs_agile_sm_evt event) -{ - enum dfs_agile_sm_state state; - - state = dfs_agile_get_curr_state(dfs_soc_obj); - if (!(state < DFS_AGILE_S_MAX)) - return; - - dfs_debug(NULL, WLAN_DEBUG_DFS_AGILE, "[%s]%s, %s", - dfs_soc_obj->dfs_agile_sm_hdl->name, - dfs_agile_sm_info[state].name, - dfs_agile_sm_event_names[event]); -} - -QDF_STATUS dfs_agile_sm_deliver_evt(struct dfs_soc_priv_obj *dfs_soc_obj, - enum dfs_agile_sm_evt event, - uint16_t event_data_len, - void *event_data) -{ - enum dfs_agile_sm_state old_state, new_state; - QDF_STATUS status; - - DFS_AGILE_SM_SPIN_LOCK(dfs_soc_obj); - old_state = dfs_agile_get_curr_state(dfs_soc_obj); - - /* Print current state and event received */ - dfs_agile_sm_print_state_event(dfs_soc_obj, event); - - status = dfs_agile_sm_deliver_event(dfs_soc_obj, event, - event_data_len, event_data); - - new_state = dfs_agile_get_curr_state(dfs_soc_obj); - - /* Print new state after event if transition happens */ - if (old_state != new_state) - dfs_agile_sm_print_state(dfs_soc_obj); - DFS_AGILE_SM_SPIN_UNLOCK(dfs_soc_obj); - - return status; -} - -QDF_STATUS dfs_agile_sm_create(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - struct wlan_sm *sm; - - sm = wlan_sm_create("DFS_AGILE", dfs_soc_obj, - DFS_AGILE_S_INIT, - dfs_agile_sm_info, - QDF_ARRAY_SIZE(dfs_agile_sm_info), - dfs_agile_sm_event_names, - QDF_ARRAY_SIZE(dfs_agile_sm_event_names)); - if (!sm) { - qdf_err("DFS AGILE SM allocation failed"); - return QDF_STATUS_E_FAILURE; - } - dfs_soc_obj->dfs_agile_sm_hdl = sm; - - qdf_spinlock_create(&dfs_soc_obj->dfs_agile_sm_lock); - - /* Initialize the RCAC DFS index to default (no index). */ - dfs_soc_obj->cur_agile_dfs_index = DFS_PSOC_NO_IDX; - return QDF_STATUS_SUCCESS; -} - -QDF_STATUS dfs_agile_sm_destroy(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - wlan_sm_delete(dfs_soc_obj->dfs_agile_sm_hdl); - qdf_spinlock_destroy(&dfs_soc_obj->dfs_agile_sm_lock); - - return QDF_STATUS_SUCCESS; -} - -#ifdef QCA_SUPPORT_ADFS_RCAC -QDF_STATUS dfs_set_rcac_enable(struct wlan_dfs *dfs, bool rcac_en) -{ - if (rcac_en == dfs->dfs_agile_rcac_ucfg) { - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, - "Rolling CAC: %d is already configured", rcac_en); - return QDF_STATUS_SUCCESS; - } - dfs->dfs_agile_rcac_ucfg = rcac_en; - - /* RCAC config is changed. Reset the preCAC tree. */ - dfs_reset_precac_lists(dfs); - - if (!rcac_en) { - dfs_agile_sm_deliver_evt(dfs->dfs_soc_obj, - DFS_AGILE_SM_EV_AGILE_STOP, - 0, - (void *)dfs); - } - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "rolling cac is %d", rcac_en); - - return QDF_STATUS_SUCCESS; -} - -QDF_STATUS dfs_get_rcac_enable(struct wlan_dfs *dfs, bool *rcacen) -{ - *rcacen = dfs->dfs_agile_rcac_ucfg; - - return QDF_STATUS_SUCCESS; -} - -QDF_STATUS dfs_set_rcac_freq(struct wlan_dfs *dfs, qdf_freq_t rcac_freq) -{ - if (wlan_reg_is_5ghz_ch_freq(rcac_freq)) - dfs->dfs_agile_rcac_freq_ucfg = rcac_freq; - else - dfs->dfs_agile_rcac_freq_ucfg = 0; - - dfs_info(dfs, WLAN_DEBUG_DFS_ALWAYS, "rolling cac freq %d", - dfs->dfs_agile_rcac_freq_ucfg); - - return QDF_STATUS_SUCCESS; -} - -QDF_STATUS dfs_get_rcac_freq(struct wlan_dfs *dfs, qdf_freq_t *rcac_freq) -{ - *rcac_freq = dfs->dfs_agile_rcac_freq_ucfg; - - return QDF_STATUS_SUCCESS; -} - -/* - * Rolling CAC Timer timeout function. Following actions are done - * on timer expiry: - * Timer running flag is cleared. - * If the rolling CAC state is completed, the RCAC freq and its sub-channels - * are marked as 'CAC Done' in the preCAC tree. - */ -static os_timer_func(dfs_rcac_timeout) -{ - struct wlan_dfs *dfs; - struct dfs_soc_priv_obj *dfs_soc_obj; - - OS_GET_TIMER_ARG(dfs_soc_obj, struct dfs_soc_priv_obj *); - - dfs = dfs_soc_obj->dfs_priv[dfs_soc_obj->cur_agile_dfs_index].dfs; - - dfs_agile_sm_deliver_evt(dfs_soc_obj, - DFS_AGILE_SM_EV_AGILE_DONE, - 0, - (void *)dfs); -} - -void dfs_rcac_timer_init(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - qdf_timer_init(NULL, &dfs_soc_obj->dfs_rcac_timer, - dfs_rcac_timeout, - (void *)dfs_soc_obj, - QDF_TIMER_TYPE_WAKE_APPS); -} - -void dfs_rcac_timer_deinit(struct dfs_soc_priv_obj *dfs_soc_obj) -{ - qdf_timer_free(&dfs_soc_obj->dfs_rcac_timer); -} - -/* dfs_prepare_agile_rcac_channel() - Find a valid Rolling CAC channel if - * available. - * - * @dfs: Pointer to struct wlan_dfs. - * @is_rcac_chan_available: Flag to indicate if a valid RCAC channel is - * available. - */ -void dfs_prepare_agile_rcac_channel(struct wlan_dfs *dfs, - bool *is_rcac_chan_available) -{ - qdf_freq_t rcac_ch_freq = 0; - - /* Find out a valid rcac_ch_freq */ - dfs_set_agilecac_chan_for_freq(dfs, &rcac_ch_freq, 0, 0); - - /* If RCAC channel is available, the caller will start the timer and - * send RCAC config to FW. If channel not available, the caller takes - * care of sending RCAC abort and moving SM to INIT, resetting the RCAC - * variables. - */ - *is_rcac_chan_available = rcac_ch_freq ? true : false; - dfs_debug(dfs, WLAN_DEBUG_DFS_AGILE, "Chosen rcac channel: %d", - rcac_ch_freq); -} -#endif -#endif - #if defined(QCA_SUPPORT_AGILE_DFS) || defined(ATH_SUPPORT_ZERO_CAC_DFS) || \ defined(QCA_SUPPORT_ADFS_RCAC) QDF_STATUS