xref: /wlan-dirver/qca-wifi-host-cmn/umac/dcs/core/src/wlan_dcs.c (revision d0c05845839e5f2ba5a8dcebe0cd3e4cd4e8dfcf)
1 /*
2  * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /**
19  * DOC: wlan_dcs.c
20  *
21  * This file provide definitions for following:
22  * - (de)register to WMI events for psoc enable
23  * - send dcs wmi command
24  * - dcs algorithm handling
25  */
26 
27 #include <target_if_dcs.h>
28 #include "wlan_dcs.h"
29 #include <wlan_objmgr_psoc_obj_i.h>
30 #include "wlan_utility.h"
31 #ifdef WLAN_POLICY_MGR_ENABLE
32 #include "wlan_policy_mgr_api.h"
33 #endif
34 
35 struct dcs_pdev_priv_obj *
36 wlan_dcs_get_pdev_private_obj(struct wlan_objmgr_psoc *psoc, uint32_t pdev_id)
37 {
38 	struct dcs_psoc_priv_obj *dcs_psoc_obj;
39 	struct dcs_pdev_priv_obj *dcs_pdev_priv = NULL;
40 
41 	if (!psoc) {
42 		dcs_err("psoc is null");
43 		goto end;
44 	}
45 
46 	dcs_psoc_obj = wlan_objmgr_psoc_get_comp_private_obj(
47 							psoc,
48 							WLAN_UMAC_COMP_DCS);
49 	if (!dcs_psoc_obj) {
50 		dcs_err("dcs psoc object is null");
51 		goto end;
52 	}
53 
54 	if (pdev_id >= WLAN_DCS_MAX_PDEVS) {
55 		dcs_err("invalid pdev_id: %u", pdev_id);
56 		goto end;
57 	}
58 
59 	dcs_pdev_priv = &dcs_psoc_obj->dcs_pdev_priv[pdev_id];
60 end:
61 
62 	return dcs_pdev_priv;
63 }
64 
65 QDF_STATUS wlan_dcs_attach(struct wlan_objmgr_psoc *psoc)
66 {
67 	struct wlan_target_if_dcs_tx_ops *dcs_tx_ops;
68 
69 	if (!psoc) {
70 		dcs_err("psoc is null");
71 		return QDF_STATUS_E_NULL_VALUE;
72 	}
73 
74 	dcs_tx_ops = target_if_dcs_get_tx_ops(psoc);
75 	if (!dcs_tx_ops) {
76 		dcs_err("tx_ops is null!");
77 		return QDF_STATUS_E_NULL_VALUE;
78 	}
79 
80 	if (!dcs_tx_ops->dcs_attach) {
81 		dcs_err("dcs_attach function is null!");
82 		return QDF_STATUS_E_NULL_VALUE;
83 	}
84 
85 	return dcs_tx_ops->dcs_attach(psoc);
86 }
87 
88 QDF_STATUS wlan_dcs_detach(struct wlan_objmgr_psoc *psoc)
89 {
90 	struct wlan_target_if_dcs_tx_ops *dcs_tx_ops;
91 
92 	if (!psoc) {
93 		dcs_err("psoc is null");
94 		return QDF_STATUS_E_NULL_VALUE;
95 	}
96 
97 	dcs_tx_ops = target_if_dcs_get_tx_ops(psoc);
98 	if (!dcs_tx_ops) {
99 		dcs_err("tx_ops is null!");
100 		return QDF_STATUS_E_NULL_VALUE;
101 	}
102 
103 	if (!dcs_tx_ops->dcs_detach) {
104 		dcs_err("dcs_detach function is null!");
105 		return QDF_STATUS_E_NULL_VALUE;
106 	}
107 
108 	return dcs_tx_ops->dcs_detach(psoc);
109 }
110 
111 QDF_STATUS wlan_dcs_cmd_send(struct wlan_objmgr_psoc *psoc,
112 			     uint32_t pdev_id,
113 			     bool is_host_pdev_id)
114 {
115 	struct wlan_target_if_dcs_tx_ops *dcs_tx_ops;
116 	struct dcs_pdev_priv_obj *dcs_pdev_priv;
117 	uint32_t dcs_enable;
118 
119 	if (!psoc) {
120 		dcs_err("psoc is null");
121 		return QDF_STATUS_E_NULL_VALUE;
122 	}
123 
124 	dcs_pdev_priv = wlan_dcs_get_pdev_private_obj(psoc, pdev_id);
125 	if (!dcs_pdev_priv) {
126 		dcs_err("dcs pdev private object is null");
127 		return QDF_STATUS_E_NULL_VALUE;
128 	}
129 
130 	dcs_enable = dcs_pdev_priv->dcs_host_params.dcs_enable &
131 			dcs_pdev_priv->dcs_host_params.dcs_enable_cfg;
132 	dcs_tx_ops = target_if_dcs_get_tx_ops(psoc);
133 
134 	if (dcs_tx_ops && dcs_tx_ops->dcs_cmd_send) {
135 		dcs_debug("dcs_enable: %u, pdev_id: %u", dcs_enable, pdev_id);
136 		return dcs_tx_ops->dcs_cmd_send(psoc,
137 						pdev_id,
138 						is_host_pdev_id,
139 						dcs_enable);
140 	}
141 
142 	return QDF_STATUS_SUCCESS;
143 }
144 
145 /**
146  * wlan_dcs_im_copy_stats() - dcs target interference mitigation statistics copy
147  * @prev_stats: previous statistics pointer
148  * @curr_stats: current statistics pointer
149  *
150  * Return: None
151  */
152 static inline void
153 wlan_dcs_im_copy_stats(struct wlan_host_dcs_im_tgt_stats *prev_stats,
154 		       struct wlan_host_dcs_im_tgt_stats *curr_stats)
155 {
156 	if (!prev_stats || !curr_stats) {
157 		dcs_err("previous or current stats is null");
158 		return;
159 	}
160 
161 	/*
162 	 * Right now no other actions are required beyond memcopy,
163 	 * if required the rest of the code would follow.
164 	 */
165 	qdf_mem_copy(prev_stats, curr_stats,
166 		     sizeof(struct wlan_host_dcs_im_tgt_stats));
167 }
168 
169 /**
170  * wlan_dcs_im_print_stats() - print current/previous dcs target im statistics
171  * @prev_stats: previous statistics pointer
172  * @curr_stats: current statistics pointer
173  *
174  * Return: None
175  */
176 static void
177 wlan_dcs_im_print_stats(struct wlan_host_dcs_im_tgt_stats *prev_stats,
178 			struct wlan_host_dcs_im_tgt_stats *curr_stats)
179 {
180 	if (!prev_stats || !curr_stats) {
181 		dcs_err("previous or current stats is null");
182 		return;
183 	}
184 
185 	/* Debug, dump all received stats first */
186 	dcs_debug("tgt_curr/tsf: %u", curr_stats->reg_tsf32);
187 	dcs_debug("tgt_curr/last_ack_rssi: %u", curr_stats->last_ack_rssi);
188 	dcs_debug("tgt_curr/tx_waste_time: %u", curr_stats->tx_waste_time);
189 	dcs_debug("tgt_curr/dcs_rx_time: %u", curr_stats->rx_time);
190 	dcs_debug("tgt_curr/listen_time: %u",
191 		  curr_stats->mib_stats.listen_time);
192 	dcs_debug("tgt_curr/tx_frame_cnt: %u",
193 		  curr_stats->mib_stats.reg_tx_frame_cnt);
194 	dcs_debug("tgt_curr/rx_frame_cnt: %u",
195 		  curr_stats->mib_stats.reg_rx_frame_cnt);
196 	dcs_debug("tgt_curr/rxclr_cnt: %u",
197 		  curr_stats->mib_stats.reg_rxclr_cnt);
198 	dcs_debug("tgt_curr/reg_cycle_cnt: %u",
199 		  curr_stats->mib_stats.reg_cycle_cnt);
200 	dcs_debug("tgt_curr/rxclr_ext_cnt: %u",
201 		  curr_stats->mib_stats.reg_rxclr_ext_cnt);
202 	dcs_debug("tgt_curr/ofdm_phyerr_cnt: %u",
203 		  curr_stats->mib_stats.reg_ofdm_phyerr_cnt);
204 	dcs_debug("tgt_curr/cck_phyerr_cnt: %u",
205 		  curr_stats->mib_stats.reg_cck_phyerr_cnt);
206 
207 	dcs_debug("tgt_prev/tsf: %u", prev_stats->reg_tsf32);
208 	dcs_debug("tgt_prev/last_ack_rssi: %u", prev_stats->last_ack_rssi);
209 	dcs_debug("tgt_prev/tx_waste_time: %u", prev_stats->tx_waste_time);
210 	dcs_debug("tgt_prev/rx_time: %u", prev_stats->rx_time);
211 	dcs_debug("tgt_prev/listen_time: %u",
212 		  prev_stats->mib_stats.listen_time);
213 	dcs_debug("tgt_prev/tx_frame_cnt: %u",
214 		  prev_stats->mib_stats.reg_tx_frame_cnt);
215 	dcs_debug("tgt_prev/rx_frame_cnt: %u",
216 		  prev_stats->mib_stats.reg_rx_frame_cnt);
217 	dcs_debug("tgt_prev/rxclr_cnt: %u",
218 		  prev_stats->mib_stats.reg_rxclr_cnt);
219 	dcs_debug("tgt_prev/reg_cycle_cnt: %u",
220 		  prev_stats->mib_stats.reg_cycle_cnt);
221 	dcs_debug("tgt_prev/rxclr_ext_cnt: %u",
222 		  prev_stats->mib_stats.reg_rxclr_ext_cnt);
223 	dcs_debug("tgt_prev/ofdm_phyerr_cnt: %u",
224 		  prev_stats->mib_stats.reg_ofdm_phyerr_cnt);
225 	dcs_debug("tgt_prev/cck_phyerr_cnt: %u",
226 		  prev_stats->mib_stats.reg_cck_phyerr_cnt);
227 }
228 
229 /**
230  * wlan_dcs_update_chan_util() - update chan utilization of dcs stats
231  * @p_dcs_im_stats: pointer to pdev_dcs_im_stats
232  * @rx_cu: rx channel utilization
233  * @tx_cu: tx channel utilization
234  * @obss_rx_cu: obss rx channel utilization
235  * @total_cu: total channel utilization
236  * @chan_nf: Channel noise floor (units are in dBm)
237  *
238  * Return: Void
239  */
240 static void wlan_dcs_update_chan_util(struct pdev_dcs_im_stats *p_dcs_im_stats,
241 				      uint32_t rx_cu, uint32_t tx_cu,
242 				      uint32_t obss_rx_cu,
243 				      uint32_t total_cu, uint32_t chan_nf)
244 {
245 	if (p_dcs_im_stats) {
246 		p_dcs_im_stats->dcs_ch_util_im_stats.rx_cu = rx_cu;
247 		p_dcs_im_stats->dcs_ch_util_im_stats.tx_cu = tx_cu;
248 		p_dcs_im_stats->dcs_ch_util_im_stats.obss_rx_cu = obss_rx_cu;
249 		p_dcs_im_stats->dcs_ch_util_im_stats.total_cu = total_cu;
250 		p_dcs_im_stats->dcs_ch_util_im_stats.chan_nf = chan_nf;
251 	}
252 }
253 
254 /**
255  * wlan_dcs_wlan_interference_process() - dcs detection algorithm handling
256  * @curr_stats: current target im stats pointer
257  * @dcs_pdev_priv: dcs pdev priv pointer
258  *
259  * Return: true or false means start dcs callback handler or not
260  */
261 static bool
262 wlan_dcs_wlan_interference_process(
263 				struct wlan_host_dcs_im_tgt_stats *curr_stats,
264 				struct dcs_pdev_priv_obj *dcs_pdev_priv)
265 {
266 	struct wlan_host_dcs_im_tgt_stats *prev_stats;
267 	struct pdev_dcs_params dcs_host_params;
268 	struct pdev_dcs_im_stats *p_dcs_im_stats;
269 	bool start_dcs_cbk_handler = false;
270 
271 	uint32_t reg_tsf_delta = 0;
272 	uint32_t scaled_reg_tsf_delta;
273 	uint32_t rxclr_delta = 0;
274 	uint32_t rxclr_ext_delta = 0;
275 	uint32_t cycle_count_delta = 0;
276 	uint32_t scaled_cycle_count_delta;
277 	uint32_t tx_frame_delta = 0;
278 	uint32_t rx_frame_delta = 0;
279 	uint32_t my_bss_rx_delta = 0;
280 	uint32_t reg_total_cu = 0;
281 	uint32_t reg_tx_cu = 0;
282 	uint32_t reg_rx_cu = 0;
283 	uint32_t obss_rx_cu = 0;
284 	uint32_t reg_unused_cu = 0;
285 	uint32_t rx_time_cu = 0;
286 	uint32_t reg_ofdm_phyerr_delta = 0;
287 	uint32_t reg_cck_phyerr_delta = 0;
288 	uint32_t reg_ofdm_phyerr_cu = 0;
289 	uint32_t ofdm_phy_err_rate = 0;
290 	uint32_t cck_phy_err_rate = 0;
291 	uint32_t max_phy_err_rate = 0;
292 	uint32_t max_phy_err_count = 0;
293 	uint32_t total_wasted_cu = 0;
294 	uint32_t wasted_tx_cu = 0;
295 	uint32_t tx_err = 0;
296 	uint32_t too_many_phy_errors = 0;
297 
298 	if (!curr_stats) {
299 		dcs_err("curr_stats is NULL");
300 		goto end;
301 	}
302 
303 	if (!dcs_pdev_priv) {
304 		dcs_err("dcs pdev private object is NULL");
305 		goto end;
306 	}
307 
308 	dcs_host_params = dcs_pdev_priv->dcs_host_params;
309 	p_dcs_im_stats = &dcs_pdev_priv->dcs_im_stats;
310 	prev_stats =  &dcs_pdev_priv->dcs_im_stats.prev_dcs_im_stats;
311 
312 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
313 		wlan_dcs_im_print_stats(prev_stats, curr_stats);
314 
315 	/*
316 	 * Counters would have wrapped. Ideally we should be able to figure this
317 	 * out, but we never know how many times counters wrapped, just ignore.
318 	 */
319 	if ((curr_stats->mib_stats.listen_time <= 0) ||
320 	    (curr_stats->reg_tsf32 <= prev_stats->reg_tsf32)) {
321 		if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
322 			dcs_debug("ignoring due to negative TSF value");
323 		goto copy_stats;
324 	}
325 
326 	reg_tsf_delta = curr_stats->reg_tsf32 - prev_stats->reg_tsf32;
327 
328 	/*
329 	 * Do nothing if current stats are not seeming good, probably
330 	 * a reset happened on chip, force cleared
331 	 */
332 	if (prev_stats->mib_stats.reg_rxclr_cnt >
333 		curr_stats->mib_stats.reg_rxclr_cnt) {
334 		if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
335 			dcs_debug("ignoring due to negative rxclr count");
336 		goto copy_stats;
337 	}
338 
339 	rxclr_delta = curr_stats->mib_stats.reg_rxclr_cnt -
340 			prev_stats->mib_stats.reg_rxclr_cnt;
341 	rxclr_ext_delta = curr_stats->mib_stats.reg_rxclr_ext_cnt -
342 				prev_stats->mib_stats.reg_rxclr_ext_cnt;
343 	tx_frame_delta = curr_stats->mib_stats.reg_tx_frame_cnt -
344 				prev_stats->mib_stats.reg_tx_frame_cnt;
345 
346 	rx_frame_delta = curr_stats->mib_stats.reg_rx_frame_cnt -
347 				prev_stats->mib_stats.reg_rx_frame_cnt;
348 
349 	cycle_count_delta = curr_stats->mib_stats.reg_cycle_cnt -
350 				prev_stats->mib_stats.reg_cycle_cnt;
351 
352 	my_bss_rx_delta = curr_stats->my_bss_rx_cycle_count -
353 				prev_stats->my_bss_rx_cycle_count;
354 
355 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
356 		dcs_debug("rxclr_delta: %u, rxclr_ext_delta: %u, tx_frame_delta: %u, rx_frame_delta: %u, cycle_count_delta: %u, my_bss_rx_delta: %u",
357 			  rxclr_delta, rxclr_ext_delta, tx_frame_delta,
358 			  rx_frame_delta, cycle_count_delta, my_bss_rx_delta);
359 
360 	/* Update user stats */
361 	wlan_dcs_pdev_obj_lock(dcs_pdev_priv);
362 	if (dcs_pdev_priv->dcs_host_params.user_request_count) {
363 		struct wlan_host_dcs_im_user_stats *p_user_stats =
364 					     &p_dcs_im_stats->user_dcs_im_stats;
365 
366 		p_user_stats->cycle_count += cycle_count_delta;
367 		p_user_stats->rxclr_count += rxclr_delta;
368 		p_user_stats->rx_frame_count += rx_frame_delta;
369 		p_user_stats->my_bss_rx_cycle_count += my_bss_rx_delta;
370 		if (0 == p_user_stats->max_rssi &&
371 		    0 == p_user_stats->min_rssi) {
372 			p_user_stats->max_rssi = curr_stats->last_ack_rssi;
373 			p_user_stats->min_rssi = curr_stats->last_ack_rssi;
374 		} else {
375 			if (curr_stats->last_ack_rssi > p_user_stats->max_rssi)
376 				p_user_stats->max_rssi =
377 						      curr_stats->last_ack_rssi;
378 			if (curr_stats->last_ack_rssi < p_user_stats->min_rssi)
379 				p_user_stats->min_rssi =
380 						      curr_stats->last_ack_rssi;
381 		}
382 		dcs_pdev_priv->dcs_host_params.user_request_count--;
383 		if (0 == dcs_pdev_priv->dcs_host_params.user_request_count)
384 			dcs_pdev_priv->dcs_host_params.notify_user = 1;
385 	}
386 	wlan_dcs_pdev_obj_unlock(dcs_pdev_priv);
387 
388 	/*
389 	 * Total channel utiliztaion is the amount of time RXCLR is
390 	 * counted. RXCLR is counted, when 'RX is NOT clear', please
391 	 * refer to mac documentation. It means either TX or RX is ON
392 	 *
393 	 * Why shift by 8 ? after multiplication it could overflow. At one
394 	 * second rate, normally neither cycle_count_delta nor the tsf_delta
395 	 * would be zero after shift by 8 bits. In corner case, host resets
396 	 * dcs stats, and at the same time tsf counters is wrapped.
397 	 * Then all the variable in prev_stats are 0, and the variable in
398 	 * curr_stats may be a small value, so add check for cycle_count_delta
399 	 * and the tsf_delta after shift by 8 bits.
400 	 */
401 	scaled_cycle_count_delta = cycle_count_delta >> 8;
402 	scaled_reg_tsf_delta = reg_tsf_delta >> 8;
403 	if (!scaled_cycle_count_delta || !scaled_reg_tsf_delta) {
404 		if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
405 			dcs_debug("cycle count or TSF NULL --Investigate--");
406 		goto copy_stats;
407 	}
408 	reg_total_cu = ((rxclr_delta >> 8) * 100) / scaled_cycle_count_delta;
409 	reg_tx_cu = ((tx_frame_delta >> 8) * 100) / scaled_cycle_count_delta;
410 	reg_rx_cu = ((rx_frame_delta >> 8) * 100) / scaled_cycle_count_delta;
411 	rx_time_cu = ((curr_stats->rx_time >> 8) * 100) / scaled_reg_tsf_delta;
412 	obss_rx_cu = (((rx_frame_delta - my_bss_rx_delta) >> 8) * 100) /
413 		     scaled_cycle_count_delta;
414 	wlan_dcs_update_chan_util(p_dcs_im_stats, reg_rx_cu, reg_tx_cu,
415 				  obss_rx_cu, reg_total_cu,
416 				  curr_stats->chan_nf);
417 
418 	/*
419 	 * Amount of the time AP received cannot go higher than the receive
420 	 * cycle count delta. If at all it is, there should have been a
421 	 * computation error, ceil it to receive_cycle_count_diff
422 	 */
423 	if (rx_time_cu > reg_rx_cu)
424 		rx_time_cu = reg_rx_cu;
425 
426 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
427 		dcs_debug("reg_total_cu: %u, reg_tx_cu: %u, reg_rx_cu: %u, rx_time_cu: %u, obss_rx_cu: %u dcs_algorithm: %d",
428 			  reg_total_cu, reg_tx_cu, reg_rx_cu,
429 			  rx_time_cu, obss_rx_cu,
430 			  dcs_host_params.dcs_algorithm_process);
431 
432 	/*
433 	 * For below scenario, will ignore dcs event data and won't do
434 	 * interference detection algorithm calculation:
435 	 * 1: Current SAP channel isn't on 5G band
436 	 * 2: In the process of ACS
437 	 * 3: In the process of dcs disabling dcs_restart_delay time duration
438 	 */
439 	if (!dcs_host_params.dcs_algorithm_process)
440 		goto copy_stats;
441 
442 	/*
443 	 * Unusable channel utilization is amount of time that we
444 	 * spent in backoff or waiting for other transmit/receive to
445 	 * complete. If there is interference it is more likely that
446 	 * we overshoot the limit. In case of multiple stations, we
447 	 * still see increased channel utilization.  This assumption may
448 	 * not be true for the VOW scenario where either multicast or
449 	 * unicast-UDP is used ( mixed traffic would still cause high
450 	 * channel utilization).
451 	 */
452 	wasted_tx_cu = ((curr_stats->tx_waste_time >> 8) * 100) /
453 							scaled_reg_tsf_delta;
454 
455 	/*
456 	 * Transmit channel utilization cannot go higher than the amount of time
457 	 * wasted, if so cap the wastage to transmit channel utillzation. This
458 	 * could happen to compution error.
459 	 */
460 	if (reg_tx_cu < wasted_tx_cu)
461 		wasted_tx_cu = reg_tx_cu;
462 
463 	tx_err = (reg_tx_cu && wasted_tx_cu) ?
464 			(wasted_tx_cu * 100) / reg_tx_cu : 0;
465 
466 	/*
467 	 * The below actually gives amount of time we are not using, or the
468 	 * interferer is active.
469 	 * rx_time_cu is what computed receive time *NOT* rx_cycle_count
470 	 * rx_cycle_count is our receive+interferer's transmit
471 	 * un-used is really total_cycle_counts -
472 	 *      (our_rx_time(rx_time_cu) + our_receive_time)
473 	 */
474 	reg_unused_cu = (reg_total_cu >= (reg_tx_cu + rx_time_cu)) ?
475 				(reg_total_cu - (reg_tx_cu + rx_time_cu)) : 0;
476 
477 	/* If any retransmissions are there, count them as wastage */
478 	total_wasted_cu = reg_unused_cu + wasted_tx_cu;
479 
480 	/* Check ofdm and cck errors */
481 	if (unlikely(curr_stats->mib_stats.reg_ofdm_phyerr_cnt <
482 			prev_stats->mib_stats.reg_ofdm_phyerr_cnt))
483 		reg_ofdm_phyerr_delta =
484 			curr_stats->mib_stats.reg_ofdm_phyerr_cnt;
485 	else
486 		reg_ofdm_phyerr_delta =
487 			curr_stats->mib_stats.reg_ofdm_phyerr_cnt -
488 				prev_stats->mib_stats.reg_ofdm_phyerr_cnt;
489 
490 	if (unlikely(curr_stats->mib_stats.reg_cck_phyerr_cnt <
491 			prev_stats->mib_stats.reg_cck_phyerr_cnt))
492 		reg_cck_phyerr_delta = curr_stats->mib_stats.reg_cck_phyerr_cnt;
493 	else
494 		reg_cck_phyerr_delta =
495 			curr_stats->mib_stats.reg_cck_phyerr_cnt -
496 				prev_stats->mib_stats.reg_cck_phyerr_cnt;
497 
498 	/*
499 	 * Add the influence of ofdm phy errors to the wasted channel
500 	 * utillization, this computed through time wasted in errors
501 	 */
502 	reg_ofdm_phyerr_cu = reg_ofdm_phyerr_delta *
503 				dcs_host_params.phy_err_penalty;
504 	total_wasted_cu +=
505 		(reg_ofdm_phyerr_cu > 0) ?
506 		(((reg_ofdm_phyerr_cu >> 8) * 100) / scaled_reg_tsf_delta) : 0;
507 
508 	ofdm_phy_err_rate = (curr_stats->mib_stats.reg_ofdm_phyerr_cnt * 1000) /
509 				curr_stats->mib_stats.listen_time;
510 	cck_phy_err_rate = (curr_stats->mib_stats.reg_cck_phyerr_cnt * 1000) /
511 				curr_stats->mib_stats.listen_time;
512 
513 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE)) {
514 		dcs_debug("reg_unused_cu: %u, reg_ofdm_phyerr_delta: %u, reg_cck_phyerr_delta: %u, reg_ofdm_phyerr_cu: %u",
515 			  reg_unused_cu, reg_ofdm_phyerr_delta,
516 			  reg_cck_phyerr_delta, reg_ofdm_phyerr_cu);
517 		dcs_debug("total_wasted_cu: %u, ofdm_phy_err_rate: %u, cck_phy_err_rate: %u",
518 			  total_wasted_cu, ofdm_phy_err_rate, cck_phy_err_rate);
519 		dcs_debug("new_unused_cu: %u, reg_ofdm_phy_error_cu: %u",
520 			  reg_unused_cu,
521 			 (curr_stats->mib_stats.reg_ofdm_phyerr_cnt * 100) /
522 					curr_stats->mib_stats.listen_time);
523 	}
524 
525 	/* Check if the error rates are higher than the thresholds */
526 	max_phy_err_rate = QDF_MAX(ofdm_phy_err_rate, cck_phy_err_rate);
527 
528 	max_phy_err_count = QDF_MAX(curr_stats->mib_stats.reg_ofdm_phyerr_cnt,
529 				    curr_stats->mib_stats.reg_cck_phyerr_cnt);
530 
531 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
532 		dcs_debug("max_phy_err_rate: %u, max_phy_err_count: %u",
533 			  max_phy_err_rate, max_phy_err_count);
534 
535 	if (((max_phy_err_rate >= dcs_host_params.phy_err_threshold) &&
536 	     (max_phy_err_count > dcs_host_params.phy_err_threshold)) ||
537 		(curr_stats->phyerr_cnt > dcs_host_params.radar_err_threshold))
538 		too_many_phy_errors = 1;
539 
540 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_CRITICAL)) {
541 		dcs_debug("total_cu: %u, tx_cu: %u, rx_cu: %u, rx_time_cu: %u, unused cu: %u",
542 			  reg_total_cu, reg_tx_cu,
543 			  reg_rx_cu, rx_time_cu, reg_unused_cu);
544 		dcs_debug("phyerr: %u, total_wasted_cu: %u, phyerror_cu: %u, wasted_cu: %u, reg_tx_cu: %u, reg_rx_cu: %u",
545 			  too_many_phy_errors, total_wasted_cu,
546 			  reg_ofdm_phyerr_cu, wasted_tx_cu,
547 			  reg_tx_cu, reg_rx_cu);
548 		dcs_debug("tx_err: %u", tx_err);
549 	}
550 
551 	if (reg_unused_cu >= dcs_host_params.coch_intfr_threshold)
552 		/* Quickly reach to decision */
553 		p_dcs_im_stats->im_intfr_cnt += 2;
554 	else if (too_many_phy_errors &&
555 		 (((total_wasted_cu >
556 			(dcs_host_params.coch_intfr_threshold + 10)) &&
557 		((reg_tx_cu + reg_rx_cu) > dcs_host_params.user_max_cu)) ||
558 		((reg_tx_cu > DCS_TX_MAX_CU) &&
559 			(tx_err >= dcs_host_params.tx_err_threshold))))
560 		p_dcs_im_stats->im_intfr_cnt++;
561 
562 	if (p_dcs_im_stats->im_intfr_cnt >=
563 		dcs_host_params.intfr_detection_threshold) {
564 		if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_CRITICAL)) {
565 			dcs_debug("interference threshold exceeded");
566 			dcs_debug("unused_cu: %u, too_any_phy_errors: %u, total_wasted_cu: %u, reg_tx_cu: %u, reg_rx_cu: %u",
567 				  reg_unused_cu, too_many_phy_errors,
568 				  total_wasted_cu, reg_tx_cu, reg_rx_cu);
569 		}
570 
571 		p_dcs_im_stats->im_intfr_cnt = 0;
572 		p_dcs_im_stats->im_samp_cnt = 0;
573 		/*
574 		 * Once the interference is detected, change the channel, as on
575 		 * today this is common routine for wirelesslan and
576 		 * non-wirelesslan interference. Name as such kept the same
577 		 * because of the DA code, which is using the same function.
578 		 */
579 		start_dcs_cbk_handler = true;
580 	} else if (0 == p_dcs_im_stats->im_intfr_cnt ||
581 			p_dcs_im_stats->im_samp_cnt >=
582 				dcs_host_params.intfr_detection_window) {
583 		p_dcs_im_stats->im_intfr_cnt = 0;
584 		p_dcs_im_stats->im_samp_cnt = 0;
585 	}
586 
587 	/* Count the current run too */
588 	p_dcs_im_stats->im_samp_cnt++;
589 
590 	if (unlikely(dcs_host_params.dcs_debug >= DCS_DEBUG_VERBOSE))
591 		dcs_debug("intfr_count: %u, sample_count: %u",
592 			  p_dcs_im_stats->im_intfr_cnt,
593 			  p_dcs_im_stats->im_samp_cnt);
594 copy_stats:
595 	 /* Copy the stats for next cycle */
596 	wlan_dcs_im_copy_stats(prev_stats, curr_stats);
597 end:
598 	return start_dcs_cbk_handler;
599 }
600 
601 void wlan_dcs_disable_timer_fn(void *dcs_timer_args)
602 {
603 	struct pdev_dcs_timer_args *dcs_timer_args_ctx;
604 	struct wlan_objmgr_psoc *psoc;
605 	uint32_t pdev_id;
606 	struct dcs_psoc_priv_obj *dcs_psoc_priv;
607 	struct dcs_pdev_priv_obj *dcs_pdev_priv;
608 
609 	if (!dcs_timer_args) {
610 		dcs_err("dcs timer args is null");
611 		return;
612 	}
613 
614 	dcs_timer_args_ctx = (struct pdev_dcs_timer_args *)dcs_timer_args;
615 	psoc = dcs_timer_args_ctx->psoc;
616 	pdev_id = dcs_timer_args_ctx->pdev_id;
617 
618 	dcs_psoc_priv =
619 		wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_DCS);
620 	if (!dcs_psoc_priv) {
621 		dcs_err("dcs psoc private object is null");
622 		return;
623 	}
624 
625 	dcs_pdev_priv = &dcs_psoc_priv->dcs_pdev_priv[pdev_id];
626 	dcs_pdev_priv->dcs_freq_ctrl_params.disable_delay_process = false;
627 
628 	dcs_info("dcs disable timeout, enable dcs detection again");
629 	wlan_dcs_set_algorithm_process(psoc, pdev_id, true);
630 }
631 
632 /**
633  * wlan_dcs_frequency_control() - dcs frequency control handling
634  * @psoc: psoc pointer
635  * @dcs_pdev_priv: dcs pdev priv pointer
636  * @event: dcs stats event pointer
637  *
638  * Return: none
639  */
640 static void wlan_dcs_frequency_control(struct wlan_objmgr_psoc *psoc,
641 				       struct dcs_pdev_priv_obj *dcs_pdev_priv,
642 				       struct wlan_host_dcs_event *event)
643 {
644 	struct dcs_psoc_priv_obj *dcs_psoc_priv;
645 	struct pdev_dcs_freq_ctrl_params *dcs_freq_ctrl_params;
646 	uint8_t timestamp_pos;
647 	unsigned long current_time;
648 	uint8_t delta_pos;
649 	unsigned long delta_time;
650 	bool disable_dcs_sometime = false;
651 
652 	if (!psoc || !dcs_pdev_priv || !event) {
653 		dcs_err("psoc or dcs_pdev_priv or event is null");
654 		return;
655 	}
656 
657 	dcs_freq_ctrl_params = &dcs_pdev_priv->dcs_freq_ctrl_params;
658 	if (dcs_freq_ctrl_params->disable_delay_process) {
659 		dcs_err("In the process of dcs disable, shouldn't go to here");
660 		return;
661 	}
662 
663 	current_time = qdf_get_system_timestamp();
664 	if (dcs_freq_ctrl_params->dcs_happened_count >=
665 		dcs_freq_ctrl_params->disable_threshold_per_5mins) {
666 		delta_pos =
667 			dcs_freq_ctrl_params->dcs_happened_count -
668 			dcs_freq_ctrl_params->disable_threshold_per_5mins;
669 		delta_pos = delta_pos % MAX_DCS_TIME_RECORD;
670 
671 		delta_time = current_time -
672 				dcs_freq_ctrl_params->timestamp[delta_pos];
673 		if (delta_time < DCS_FREQ_CONTROL_TIME)
674 			disable_dcs_sometime = true;
675 	}
676 
677 	if (!disable_dcs_sometime) {
678 		timestamp_pos = dcs_freq_ctrl_params->dcs_happened_count %
679 							MAX_DCS_TIME_RECORD;
680 		dcs_freq_ctrl_params->timestamp[timestamp_pos] = current_time;
681 		dcs_freq_ctrl_params->dcs_happened_count++;
682 	}
683 
684 	/*
685 	 * Before start dcs callback handler or disable dcs for some time,
686 	 * need to ignore dcs event data and won't do interference detection
687 	 * algorithm calculation for disabling dcs detection firstly.
688 	 */
689 	wlan_dcs_set_algorithm_process(psoc, event->dcs_param.pdev_id, false);
690 
691 	if (disable_dcs_sometime) {
692 		dcs_freq_ctrl_params->disable_delay_process = true;
693 		dcs_pdev_priv->dcs_timer_args.psoc = psoc;
694 		dcs_pdev_priv->dcs_timer_args.pdev_id =
695 						event->dcs_param.pdev_id;
696 		qdf_timer_start(&dcs_pdev_priv->dcs_disable_timer,
697 				dcs_pdev_priv->dcs_freq_ctrl_params.
698 				restart_delay * 60 * 1000);
699 		dcs_info("start dcs disable timer");
700 	} else {
701 		dcs_psoc_priv = wlan_objmgr_psoc_get_comp_private_obj(
702 							psoc,
703 							WLAN_UMAC_COMP_DCS);
704 		if (!dcs_psoc_priv) {
705 			dcs_err("dcs private psoc object is null");
706 			return;
707 		}
708 
709 		dcs_info("start dcs callback handler");
710 		dcs_psoc_priv->dcs_cbk.cbk(psoc, event->dcs_param.pdev_id,
711 					   event->dcs_param.interference_type,
712 					   dcs_psoc_priv->dcs_cbk.arg);
713 	}
714 }
715 
716 /**
717  * wlan_dcs_switch_chan() - switch channel for vdev
718  * @vdev: vdev ptr
719  * @tgt_freq: target frequency
720  * @tgt_width: target channel width
721  *
722  * Return: QDF_STATUS
723  */
724 static QDF_STATUS
725 wlan_dcs_switch_chan(struct wlan_objmgr_vdev *vdev, qdf_freq_t tgt_freq,
726 		     enum phy_ch_width tgt_width)
727 {
728 	struct wlan_objmgr_psoc *psoc;
729 	struct dcs_psoc_priv_obj *dcs_psoc_priv;
730 	dcs_switch_chan_cb switch_chan_cb;
731 
732 	psoc = wlan_vdev_get_psoc(vdev);
733 	if (!psoc)
734 		return QDF_STATUS_E_INVAL;
735 
736 	dcs_psoc_priv = wlan_objmgr_psoc_get_comp_private_obj(psoc,
737 					WLAN_UMAC_COMP_DCS);
738 	if (!dcs_psoc_priv)
739 		return QDF_STATUS_E_INVAL;
740 
741 	switch_chan_cb = dcs_psoc_priv->switch_chan_cb;
742 	if (!switch_chan_cb)
743 		return QDF_STATUS_E_NOSUPPORT;
744 
745 	return switch_chan_cb(vdev, tgt_freq, tgt_width);
746 }
747 
748 #ifdef WLAN_POLICY_MGR_ENABLE
749 /**
750  * wlan_dcs_get_pcl_for_sap() - get preferred channel list for SAP
751  * @vdev: vdev ptr
752  * @freq_list: Pointer to PCL
753  * @freq_list_sz: Max size of PCL
754  *
755  * Return: number of channels in PCL
756  */
757 static uint32_t wlan_dcs_get_pcl_for_sap(struct wlan_objmgr_vdev *vdev,
758 					 qdf_freq_t *freq_list,
759 					 uint32_t freq_list_sz)
760 {
761 	struct wlan_objmgr_psoc *psoc;
762 	struct wlan_objmgr_pdev *pdev;
763 	struct policy_mgr_pcl_list *pcl;
764 	qdf_freq_t freq;
765 	enum channel_state state;
766 	QDF_STATUS status;
767 	int i, j;
768 
769 	psoc = wlan_vdev_get_psoc(vdev);
770 	if (!psoc)
771 		return 0;
772 
773 	pdev = wlan_vdev_get_pdev(vdev);
774 	if (!pdev)
775 		return 0;
776 
777 	pcl = qdf_mem_malloc(sizeof(*pcl));
778 	if (!pcl)
779 		return 0;
780 
781 	status = policy_mgr_get_pcl_for_vdev_id(psoc,
782 						PM_SAP_MODE,
783 						pcl->pcl_list, &pcl->pcl_len,
784 						pcl->weight_list,
785 						QDF_ARRAY_SIZE(pcl->weight_list),
786 						wlan_vdev_get_id(vdev));
787 	if (QDF_IS_STATUS_ERROR(status) || !pcl->pcl_len) {
788 		qdf_mem_free(pcl);
789 		return 0;
790 	}
791 
792 	for (i = 0, j = 0; i < pcl->pcl_len && i < freq_list_sz; i++) {
793 		freq = (qdf_freq_t)pcl->pcl_list[i];
794 		state = wlan_reg_get_channel_state_for_pwrmode(
795 							pdev,
796 							freq,
797 							REG_CURRENT_PWR_MODE);
798 		if (state != CHANNEL_STATE_ENABLE)
799 			continue;
800 
801 		freq_list[j++] = freq;
802 	}
803 
804 	qdf_mem_free(pcl);
805 	return j;
806 }
807 #else
808 static uint32_t wlan_dcs_get_pcl_for_sap(struct wlan_objmgr_vdev *vdev,
809 					 qdf_freq_t *freq_list,
810 					 uint32_t freq_list_sz)
811 {
812 	struct wlan_objmgr_pdev *pdev;
813 	struct regulatory_channel *cur_chan_list;
814 	qdf_freq_t freq;
815 	enum channel_state state;
816 	int i, j;
817 
818 	pdev = wlan_vdev_get_pdev(vdev);
819 	if (!pdev)
820 		return 0;
821 
822 	cur_chan_list = qdf_mem_malloc(NUM_CHANNELS *
823 			sizeof(struct regulatory_channel));
824 	if (!cur_chan_list)
825 		return 0;
826 
827 	if (wlan_reg_get_current_chan_list(pdev, cur_chan_list) !=
828 					   QDF_STATUS_SUCCESS) {
829 		qdf_mem_free(cur_chan_list);
830 		return 0;
831 	}
832 
833 	for (i = 0, j = 0; i < NUM_CHANNELS && i < freq_list_sz; i++) {
834 		freq = cur_chan_list[i].center_freq;
835 		state = wlan_reg_get_channel_state_for_pwrmode(
836 						       pdev,
837 						       freq,
838 						       REG_CURRENT_PWR_MODE);
839 		if (state != CHANNEL_STATE_ENABLE)
840 			continue;
841 
842 		freq_list[j++] = freq;
843 	}
844 
845 	qdf_mem_free(cur_chan_list);
846 	return j;
847 }
848 #endif
849 
850 /**
851  * wlan_dcs_awgn_get_intf_for_seg() - get interference for specified segment
852  * @awgn_info: awgn info pointer
853  * @segment: segment index in channel band
854  *
855  * This function extracts the information from awgn event and check interference
856  * within the specified segment.
857  *
858  * Return: true if interference is found within the segment, false otherwise.
859  */
860 static bool
861 wlan_dcs_awgn_get_intf_for_seg(struct wlan_host_dcs_awgn_info *awgn_info,
862 			       uint32_t segment)
863 {
864 	uint32_t seg_mask;
865 
866 	switch (segment) {
867 	case WLAN_DCS_SEG_PRI20:
868 		seg_mask = WLAN_DCS_SEG_PRI20_MASK;
869 		break;
870 	case WLAN_DCS_SEG_SEC20:
871 		seg_mask = WLAN_DCS_SEG_SEC20_MASK;
872 		break;
873 	case WLAN_DCS_SEG_SEC40:
874 		seg_mask = WLAN_DCS_SEG_SEC40_MASK;
875 		break;
876 	case WLAN_DCS_SEG_SEC80:
877 		seg_mask = WLAN_DCS_SEG_SEC80_MASK;
878 		break;
879 	case WLAN_DCS_SEG_SEC160:
880 		seg_mask = WLAN_DCS_SEG_SEC160_MASK;
881 		break;
882 	default:
883 		seg_mask = 0xFFFFFFFF;
884 		break;
885 	}
886 
887 	return (awgn_info->chan_bw_intf_bitmap & seg_mask);
888 }
889 
890 /**
891  * wlan_dcs_get_max_seg_idx() - get max segment index for channel width
892  * @width: channel width
893  *
894  * Return: max segment index(enum wlan_dcs_chan_seg) for the channel width.
895  */
896 static enum wlan_dcs_chan_seg wlan_dcs_get_max_seg_idx(enum phy_ch_width width)
897 {
898 	switch (width) {
899 	case CH_WIDTH_160MHZ:
900 	case CH_WIDTH_80P80MHZ:
901 		return WLAN_DCS_SEG_SEC80;
902 	case CH_WIDTH_80MHZ:
903 		return WLAN_DCS_SEG_SEC40;
904 	case CH_WIDTH_40MHZ:
905 		return WLAN_DCS_SEG_SEC20;
906 	case CH_WIDTH_20MHZ:
907 		return WLAN_DCS_SEG_PRI20;
908 	default:
909 		dcs_err("Invalid ch width %d", width);
910 		return WLAN_DCS_SEG_INVALID;
911 	}
912 }
913 
914 /**
915  * wlan_dcs_get_chan_width_for_seg() - get channel width for specified segment
916  * @seg_idx: segment index
917  *
918  * Return: channel width for segment index
919  */
920 static enum phy_ch_width
921 wlan_dcs_get_chan_width_for_seg(enum wlan_dcs_chan_seg seg_idx)
922 {
923 	switch (seg_idx) {
924 	case WLAN_DCS_SEG_SEC80:
925 		return CH_WIDTH_160MHZ;
926 	case WLAN_DCS_SEG_SEC40:
927 		return CH_WIDTH_80MHZ;
928 	case WLAN_DCS_SEG_SEC20:
929 		return CH_WIDTH_40MHZ;
930 	case WLAN_DCS_SEG_PRI20:
931 		return CH_WIDTH_20MHZ;
932 	default:
933 		dcs_err("Invalid seg idx %d", seg_idx);
934 		return CH_WIDTH_INVALID;
935 	}
936 }
937 
938 /**
939  * wlan_dcs_get_max_no_intf_bw() - get max no interference band width
940  * @awgn_info: pointer to awgn info
941  * @width: pointer to channel width
942  *
943  * This function trys to get max no interference band width according to
944  * awgn event.
945  *
946  * Return: true if valid no interference band width is found, false otherwise.
947  */
948 static bool
949 wlan_dcs_get_max_no_intf_bw(struct wlan_host_dcs_awgn_info *awgn_info,
950 			    enum phy_ch_width *width)
951 {
952 	enum wlan_dcs_chan_seg seg_idx, max_seg_idx;
953 
954 	max_seg_idx = wlan_dcs_get_max_seg_idx(awgn_info->channel_width);
955 	if (max_seg_idx == WLAN_DCS_SEG_INVALID)
956 		return false;
957 
958 	seg_idx = WLAN_DCS_SEG_PRI20;
959 	while (seg_idx <= max_seg_idx) {
960 		if (wlan_dcs_awgn_get_intf_for_seg(awgn_info, seg_idx)) {
961 			dcs_debug("Intf found for seg idx %d", seg_idx);
962 			break;
963 		}
964 		seg_idx++;
965 	}
966 
967 	/* scroll back to the last no-intf idx */
968 	seg_idx--;
969 
970 	if (seg_idx == WLAN_DCS_SEG_INVALID) {
971 		/* If pri20 contains interference, do full channel change */
972 		dcs_debug("Primary 20MHz Channel interference detected");
973 		return false;
974 	}
975 
976 	*width = wlan_dcs_get_chan_width_for_seg(seg_idx);
977 	if (*width == CH_WIDTH_160MHZ &&
978 	    awgn_info->channel_width == CH_WIDTH_80P80MHZ)
979 		*width = CH_WIDTH_80P80MHZ;
980 
981 	dcs_debug("Found the max no intf width %d", *width);
982 	return (*width != CH_WIDTH_INVALID);
983 }
984 
985 /**
986  * wlan_dcs_get_available_chan_for_bw() - get available channel for specified
987  *  band width
988  * @pdev: pdev ptr
989  * @awgn_info: pointer to awgn info
990  * @bw: channel width
991  * @freq_list: List of preferred channels
992  * @freq_num: Number of channels in the PCL
993  * @random: request for random channel
994  *
995  * Return: the selected channel frequency, 0 if no available chan is found.
996  */
997 static qdf_freq_t
998 wlan_dcs_get_available_chan_for_bw(struct wlan_objmgr_pdev *pdev,
999 				   struct wlan_host_dcs_awgn_info *awgn_info,
1000 				   enum phy_ch_width bw, qdf_freq_t *freq_list,
1001 				   uint32_t freq_num, bool random)
1002 {
1003 	int i, j = 0;
1004 	uint32_t random_chan_idx;
1005 	qdf_freq_t freq, selected_freq = 0;
1006 	const struct bonded_channel_freq *bonded_chan_ptr = NULL;
1007 	enum channel_state state;
1008 	uint16_t chan_cfreq;
1009 	bool is_safe = true;
1010 
1011 	if (!freq_list || !freq_num)
1012 		return selected_freq;
1013 
1014 	for (i = 0; i < freq_num; i++) {
1015 		if (j && !random) {
1016 			selected_freq = freq_list[0];
1017 			dcs_debug("get the first available freq %u for bw %u",
1018 				  selected_freq, bw);
1019 			break;
1020 		}
1021 
1022 		freq = freq_list[i];
1023 		if (!WLAN_REG_IS_SAME_BAND_FREQS(freq, awgn_info->center_freq))
1024 			continue;
1025 
1026 		/**
1027 		 * DFS channel may need CAC during restart, which costs time
1028 		 * and may cause failure.
1029 		 */
1030 		if (wlan_reg_is_dfs_for_freq(pdev, freq)) {
1031 			dcs_debug("skip dfs freq %u", freq);
1032 			continue;
1033 		}
1034 
1035 		if (bonded_chan_ptr &&
1036 		    freq >= bonded_chan_ptr->start_freq &&
1037 		    freq <= bonded_chan_ptr->end_freq) {
1038 			if (is_safe) {
1039 				dcs_debug("add freq directly [%d] = %u",
1040 					  j, freq);
1041 				freq_list[j++] = freq;
1042 			}
1043 			continue;
1044 		}
1045 
1046 		state = wlan_reg_get_5g_bonded_channel_and_state_for_pwrmode(
1047 				pdev, freq, bw, &bonded_chan_ptr,
1048 				REG_CURRENT_PWR_MODE);
1049 		if (state != CHANNEL_STATE_ENABLE)
1050 			continue;
1051 
1052 		/* no bonding channel for 20MHz */
1053 		if (bw == CH_WIDTH_20MHZ) {
1054 			if (WLAN_DCS_IS_FREQ_IN_WIDTH(awgn_info->center_freq,
1055 						      awgn_info->center_freq0,
1056 						      awgn_info->center_freq1,
1057 						      awgn_info->channel_width,
1058 						      freq))
1059 				continue;
1060 
1061 			dcs_debug("add freq[%d] = %u", j, freq);
1062 			freq_list[j++] = freq;
1063 			continue;
1064 		}
1065 
1066 		is_safe = true;
1067 		chan_cfreq =  bonded_chan_ptr->start_freq;
1068 		while (chan_cfreq <= bonded_chan_ptr->end_freq) {
1069 			if (WLAN_DCS_IS_FREQ_IN_WIDTH(awgn_info->center_freq,
1070 						      awgn_info->center_freq0,
1071 						      awgn_info->center_freq1,
1072 						      awgn_info->channel_width,
1073 						      chan_cfreq)) {
1074 				is_safe = false;
1075 				break;
1076 			}
1077 			chan_cfreq = chan_cfreq + 20;
1078 		}
1079 		if (is_safe) {
1080 			dcs_debug("add freq[%d] = %u", j, freq);
1081 			freq_list[j++] = freq;
1082 		}
1083 	}
1084 
1085 	if (j && random) {
1086 		qdf_get_random_bytes(&random_chan_idx, sizeof(random_chan_idx));
1087 		random_chan_idx = random_chan_idx % j;
1088 		selected_freq = freq_list[random_chan_idx];
1089 		dcs_debug("get freq[%d] = %u for bw %u",
1090 			  random_chan_idx, selected_freq, bw);
1091 	}
1092 
1093 	return selected_freq;
1094 }
1095 
1096 /**
1097  * wlan_dcs_sap_get_available_chan() - get available channel for sap
1098  * @vdev: vdev ptr
1099  * @awgn_info: pointer to awgn info
1100  * @tgt_freq: frequency of the selected channel
1101  * @tgt_width: band width of the selected channel
1102  * @random: request for random channel
1103  *
1104  * This function trys to get no-interference chan with max possible bandwidth
1105  * from pcl for sap according to awgn info.
1106  *
1107  * Return: true if available channel is found, false otherwise.
1108  */
1109 static bool
1110 wlan_dcs_sap_select_chan(struct wlan_objmgr_vdev *vdev,
1111 			 struct wlan_host_dcs_awgn_info *awgn_info,
1112 			 qdf_freq_t *tgt_freq, enum phy_ch_width *tgt_width,
1113 			 bool random)
1114 {
1115 	int32_t tmp_width;
1116 	qdf_freq_t tmp_freq = 0;
1117 	struct wlan_objmgr_pdev *pdev;
1118 	qdf_freq_t *freq_list;
1119 	uint32_t freq_num;
1120 
1121 	freq_list = qdf_mem_malloc(sizeof(*freq_list) * NUM_CHANNELS);
1122 	if (!freq_list)
1123 		return false;
1124 
1125 	freq_num = wlan_dcs_get_pcl_for_sap(vdev, freq_list, NUM_CHANNELS);
1126 	if (!freq_num) {
1127 		qdf_mem_free(freq_list);
1128 		return false;
1129 	}
1130 
1131 	tmp_width = awgn_info->channel_width;
1132 	pdev = wlan_vdev_get_pdev(vdev);
1133 	if (!pdev) {
1134 		qdf_mem_free(freq_list);
1135 		return false;
1136 	}
1137 
1138 	while (tmp_width >= CH_WIDTH_20MHZ) {
1139 		tmp_freq = wlan_dcs_get_available_chan_for_bw(pdev, awgn_info,
1140 							      tmp_width,
1141 							      freq_list,
1142 							      freq_num,
1143 							      random);
1144 		if (tmp_freq)
1145 			break;
1146 		tmp_width--;
1147 	}
1148 
1149 	if (tmp_freq) {
1150 		*tgt_width = tmp_width;
1151 		*tgt_freq = tmp_freq;
1152 		dcs_debug("new_width: %d new_freq %u", tmp_width, tmp_freq);
1153 
1154 		qdf_mem_free(freq_list);
1155 		return true;
1156 	}
1157 
1158 	qdf_mem_free(freq_list);
1159 	return false;
1160 }
1161 
1162 /**
1163  * wlan_dcs_is_awgnim_valid() - validate awgn info
1164  * @awgn_info: pointer to awgn info
1165  *
1166  * Return: true if valid, false otherwise.
1167  */
1168 static inline bool
1169 wlan_dcs_is_awgnim_valid(struct wlan_host_dcs_awgn_info *awgn_info)
1170 {
1171 	return (awgn_info &&
1172 		awgn_info->center_freq && awgn_info->chan_bw_intf_bitmap &&
1173 		awgn_info->channel_width != CH_WIDTH_INVALID &&
1174 		WLAN_REG_IS_6GHZ_CHAN_FREQ(awgn_info->center_freq));
1175 }
1176 
1177 /**
1178  * wlan_dcs_vdev_get_op_chan_info() - get operating channel info for vdev
1179  * @vdev: pointer to vdev object
1180  * @cfreq: Center frequency of primary channel
1181  * @cfreq0: Center frequency of segment 1
1182  * @cfreq1: Center frequency of segment 2
1183  * @ch_width: Channel width, enum phy_ch_width
1184  *
1185  * Return: QDF_STATUS
1186  */
1187 static QDF_STATUS
1188 wlan_dcs_vdev_get_op_chan_info(struct wlan_objmgr_vdev *vdev,
1189 			       qdf_freq_t *cfreq, qdf_freq_t *cfreq0,
1190 			       qdf_freq_t *cfreq1, enum phy_ch_width *ch_width)
1191 {
1192 	struct wlan_channel *chan;
1193 
1194 	if (!vdev)
1195 		return QDF_STATUS_E_INVAL;
1196 
1197 	*cfreq = 0;
1198 	*cfreq0 = 0;
1199 	*cfreq1 = 0;
1200 	*ch_width = 0;
1201 
1202 	if (wlan_vdev_mlme_is_active(vdev) != QDF_STATUS_SUCCESS)
1203 		return QDF_STATUS_E_INVAL;
1204 
1205 	chan = wlan_vdev_get_active_channel(vdev);
1206 	if (!chan)
1207 		return QDF_STATUS_E_INVAL;
1208 
1209 	*cfreq = chan->ch_freq;
1210 	*cfreq0 = chan->ch_cfreq1;
1211 	*cfreq1 = chan->ch_cfreq2;
1212 	*ch_width = chan->ch_width;
1213 
1214 	return QDF_STATUS_SUCCESS;
1215 }
1216 
1217 /**
1218  * wlan_dcs_process_awgn_sta() - process AWGN event for STA
1219  * @pdev: pointer to pdev object
1220  * @object: vdev object
1221  * @arg: Arguments to the handler
1222  *
1223  * Return: void
1224  */
1225 static void wlan_dcs_process_awgn_sta(struct wlan_objmgr_pdev *pdev,
1226 				      void *object, void *arg)
1227 {
1228 	struct wlan_objmgr_vdev *vdev = object;
1229 	struct wlan_host_dcs_awgn_info *awgn_info = arg;
1230 	enum phy_ch_width ch_width;
1231 	enum phy_ch_width tgt_width = CH_WIDTH_INVALID;
1232 	qdf_freq_t op_freq, cfreq0, cfreq1;
1233 	qdf_freq_t tgt_freq = 0;
1234 	QDF_STATUS status;
1235 	uint8_t vdev_id;
1236 	bool found;
1237 
1238 	if (!vdev || !pdev)
1239 		return;
1240 
1241 	if (wlan_vdev_mlme_get_opmode(vdev) != QDF_STA_MODE)
1242 		return;
1243 
1244 	vdev_id = wlan_vdev_get_id(vdev);
1245 	status = wlan_dcs_vdev_get_op_chan_info(vdev, &op_freq, &cfreq0,
1246 						&cfreq1, &ch_width);
1247 	if (QDF_IS_STATUS_ERROR(status))
1248 		return;
1249 
1250 	if (awgn_info->center_freq != op_freq) {
1251 		dcs_debug("STA-%d: freq not match", vdev_id);
1252 		return;
1253 	}
1254 
1255 	found = wlan_dcs_get_max_no_intf_bw(awgn_info, &tgt_width);
1256 	if (found) {
1257 		if (ch_width <= tgt_width) {
1258 			dcs_debug("STA-%d: freq and bw are unchanged", vdev_id);
1259 			return;
1260 		}
1261 
1262 		tgt_freq = op_freq;
1263 	}
1264 
1265 	/* If no width is found, means to disconnect */
1266 	dcs_debug("STA-%d: target freq %u width %u",
1267 		  vdev_id, tgt_freq, tgt_width);
1268 	wlan_dcs_switch_chan(vdev, tgt_freq, tgt_width);
1269 }
1270 
1271 /**
1272  * wlan_dcs_process_awgn_sap() - process AWGN event for SAP
1273  * @pdev: pointer to pdev object
1274  * @object: vdev object
1275  * @arg: Arguments to the handler
1276  *
1277  * Return: void
1278  */
1279 static void wlan_dcs_process_awgn_sap(struct wlan_objmgr_pdev *pdev,
1280 				      void *object, void *arg)
1281 {
1282 	struct wlan_objmgr_vdev *vdev = object;
1283 	struct wlan_host_dcs_awgn_info *awgn_info = arg;
1284 	enum phy_ch_width ch_width;
1285 	enum phy_ch_width tgt_width = CH_WIDTH_INVALID;
1286 	qdf_freq_t op_freq, cfreq0, cfreq1;
1287 	qdf_freq_t tgt_freq = 0;
1288 	QDF_STATUS status;
1289 	uint8_t vdev_id;
1290 	bool found;
1291 
1292 	if (!vdev || !pdev)
1293 		return;
1294 
1295 	if (wlan_vdev_mlme_get_opmode(vdev) != QDF_SAP_MODE)
1296 		return;
1297 
1298 	vdev_id = wlan_vdev_get_id(vdev);
1299 	status = wlan_dcs_vdev_get_op_chan_info(vdev, &op_freq, &cfreq0, &cfreq1, &ch_width);
1300 	if (QDF_IS_STATUS_ERROR(status))
1301 		return;
1302 
1303 	if (awgn_info->center_freq != op_freq) {
1304 		dcs_debug("SAP-%d: freq not match rpt:%u - op:%u",
1305 			  vdev_id, awgn_info->center_freq, op_freq);
1306 		return;
1307 	}
1308 
1309 	found = wlan_dcs_get_max_no_intf_bw(awgn_info, &tgt_width);
1310 	if (found) {
1311 		if (ch_width <= tgt_width) {
1312 			dcs_debug("SAP-%d: both freq and bw are unchanged",
1313 				  vdev_id);
1314 			return;
1315 		}
1316 
1317 		tgt_freq = op_freq;
1318 	} else {
1319 		wlan_dcs_sap_select_chan(vdev, awgn_info, &tgt_freq,
1320 					 &tgt_width, true);
1321 	}
1322 
1323 	/* If no chan is selected, means to stop sap */
1324 	dcs_debug("SAP-%d: target freq %u width %u",
1325 		  vdev_id, tgt_freq, tgt_width);
1326 	wlan_dcs_switch_chan(vdev, tgt_freq, tgt_width);
1327 }
1328 
1329 /**
1330  * wlan_dcs_awgnim_process() - process awgn IM
1331  * @psoc: psoc ptr
1332  * @pdev_id: pdev id
1333  * @awgn_info: pointer to awgn info
1334  *
1335  * This function triggers channel change for all STAs and SAPs, according
1336  * to AWGN info.
1337  *
1338  * Return: None.
1339  */
1340 static void
1341 wlan_dcs_awgn_process(struct wlan_objmgr_psoc *psoc, uint8_t pdev_id,
1342 		      struct wlan_host_dcs_awgn_info *awgn_info)
1343 {
1344 	struct wlan_objmgr_pdev *pdev;
1345 
1346 	if (!wlan_dcs_is_awgnim_valid(awgn_info)) {
1347 		dcs_err("Invalid awgnim event");
1348 		return;
1349 	}
1350 
1351 	pdev = wlan_objmgr_get_pdev_by_id(psoc, pdev_id, WLAN_DCS_ID);
1352 	if (!pdev) {
1353 		dcs_err("Invalid pdev id %d", pdev_id);
1354 		return;
1355 	}
1356 
1357 	dcs_debug("pdev id %u width %u freq %u freq0 %u fre1 %u bitmap 0x%x",
1358 		  pdev_id, awgn_info->channel_width, awgn_info->center_freq,
1359 		  awgn_info->center_freq0, awgn_info->center_freq1,
1360 		  awgn_info->chan_bw_intf_bitmap);
1361 
1362 	wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
1363 					  wlan_dcs_process_awgn_sta,
1364 					  awgn_info, 0, WLAN_DCS_ID);
1365 
1366 	wlan_objmgr_pdev_iterate_obj_list(pdev, WLAN_VDEV_OP,
1367 					  wlan_dcs_process_awgn_sap,
1368 					  awgn_info, 0, WLAN_DCS_ID);
1369 
1370 	wlan_objmgr_pdev_release_ref(pdev, WLAN_DCS_ID);
1371 }
1372 
1373 QDF_STATUS
1374 wlan_dcs_process(struct wlan_objmgr_psoc *psoc,
1375 		 struct wlan_host_dcs_event *event)
1376 {
1377 	struct dcs_pdev_priv_obj *dcs_pdev_priv;
1378 	bool start_dcs_cbk_handler = false;
1379 
1380 	if (!psoc || !event) {
1381 		dcs_err("psoc or event is NULL");
1382 		return QDF_STATUS_E_INVAL;
1383 	}
1384 
1385 	dcs_pdev_priv = wlan_dcs_get_pdev_private_obj(psoc,
1386 						      event->dcs_param.pdev_id);
1387 	if (!dcs_pdev_priv) {
1388 		dcs_err("dcs pdev private object is null");
1389 		return QDF_STATUS_E_INVAL;
1390 	}
1391 
1392 	if (unlikely(dcs_pdev_priv->dcs_host_params.dcs_debug
1393 			>= DCS_DEBUG_VERBOSE))
1394 		dcs_debug("dcs_enable: %u, interference_type: %u, pdev_id: %u",
1395 			  dcs_pdev_priv->dcs_host_params.dcs_enable,
1396 			  event->dcs_param.interference_type,
1397 			  event->dcs_param.pdev_id);
1398 
1399 	switch (event->dcs_param.interference_type) {
1400 	case WLAN_HOST_DCS_CWIM:
1401 		break;
1402 	case WLAN_HOST_DCS_WLANIM:
1403 		if (!dcs_pdev_priv->dcs_host_params.dcs_enable)
1404 			break;
1405 
1406 		if (dcs_pdev_priv->dcs_host_params.dcs_enable &
1407 		    WLAN_HOST_DCS_WLANIM)
1408 			start_dcs_cbk_handler =
1409 				wlan_dcs_wlan_interference_process(
1410 							&event->wlan_stat,
1411 							dcs_pdev_priv);
1412 		if (dcs_pdev_priv->user_cb &&
1413 		    dcs_pdev_priv->dcs_host_params.notify_user) {
1414 			dcs_pdev_priv->dcs_host_params.notify_user = 0;
1415 			dcs_pdev_priv->user_cb(dcs_pdev_priv->requestor_vdev_id,
1416 				 &dcs_pdev_priv->dcs_im_stats.user_dcs_im_stats,
1417 				 0);
1418 		}
1419 		if (start_dcs_cbk_handler)
1420 			wlan_dcs_frequency_control(psoc,
1421 						   dcs_pdev_priv,
1422 						   event);
1423 		break;
1424 	case WLAN_HOST_DCS_AWGNIM:
1425 		/* Skip frequency control for AWGNIM */
1426 		wlan_dcs_awgn_process(psoc, event->dcs_param.pdev_id,
1427 				      &event->awgn_info);
1428 		break;
1429 	default:
1430 		dcs_err("unidentified interference type reported");
1431 		break;
1432 	}
1433 
1434 	return QDF_STATUS_SUCCESS;
1435 }
1436 
1437 void wlan_dcs_clear(struct wlan_objmgr_psoc *psoc, uint32_t pdev_id)
1438 {
1439 	struct dcs_pdev_priv_obj *dcs_pdev_priv;
1440 
1441 	if (!psoc) {
1442 		dcs_err("psoc is null");
1443 		return;
1444 	}
1445 
1446 	dcs_pdev_priv = wlan_dcs_get_pdev_private_obj(psoc, pdev_id);
1447 	if (!dcs_pdev_priv) {
1448 		dcs_err("dcs pdev private object is null");
1449 		return;
1450 	}
1451 
1452 	qdf_timer_stop(&dcs_pdev_priv->dcs_disable_timer);
1453 	qdf_mem_set(&dcs_pdev_priv->dcs_im_stats,
1454 		    sizeof(dcs_pdev_priv->dcs_im_stats), 0);
1455 	qdf_mem_set(dcs_pdev_priv->dcs_freq_ctrl_params.timestamp,
1456 		    MAX_DCS_TIME_RECORD * sizeof(unsigned long), 0);
1457 	dcs_pdev_priv->dcs_freq_ctrl_params.dcs_happened_count = 0;
1458 	dcs_pdev_priv->dcs_freq_ctrl_params.disable_delay_process = false;
1459 	wlan_dcs_set_algorithm_process(psoc, pdev_id, false);
1460 }
1461 
1462 void wlan_dcs_set_algorithm_process(struct wlan_objmgr_psoc *psoc,
1463 				    uint32_t pdev_id,
1464 				    bool dcs_algorithm_process)
1465 {
1466 	struct dcs_pdev_priv_obj *dcs_pdev_priv;
1467 
1468 	dcs_pdev_priv = wlan_dcs_get_pdev_private_obj(psoc, pdev_id);
1469 	if (!dcs_pdev_priv) {
1470 		dcs_err("dcs pdev private object is null");
1471 		return;
1472 	}
1473 
1474 	if (dcs_pdev_priv->dcs_host_params.force_disable_algorithm) {
1475 		dcs_debug("dcs alorithm is disabled forcely");
1476 		dcs_pdev_priv->dcs_host_params.dcs_algorithm_process = false;
1477 		return;
1478 	}
1479 
1480 	dcs_pdev_priv->dcs_host_params.dcs_algorithm_process =
1481 							dcs_algorithm_process;
1482 }
1483