xref: /wlan-dirver/qca-wifi-host-cmn/umac/dfs/core/src/misc/dfs_nol.c (revision 8b3dca18206e1a0461492f082fa6e270b092c035)
1 /*
2  * Copyright (c) 2016-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2002-2010, Atheros Communications Inc.
4  * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved.
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /**
20  * DOC: This file contains NOL related functionality, NOL being the non
21  * occupancy list. After radar has been detected in a particular channel,
22  * the channel cannot be used for a period of 30 minutes which is called
23  * the non occupancy. The NOL is basically a list of all the channels that
24  * radar has been detected on. Each channel has a 30 minute timer associated
25  * with it. This file contains the functionality to add a channel to the NOL,
26  * the NOL timer  function and the functionality to remove a channel from the
27  * NOL when its time is up.
28  */
29 
30 #include "../dfs.h"
31 #include "../dfs_channel.h"
32 #include "../dfs_ioctl_private.h"
33 #include "../dfs_zero_cac.h"
34 #include "../dfs_internal.h"
35 #include <qdf_time.h>
36 #include <wlan_dfs_mlme_api.h>
37 #include <wlan_objmgr_vdev_obj.h>
38 #include <wlan_dfs_utils_api.h>
39 #include <wlan_reg_services_api.h>
40 #if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(HOST_DFS_SPOOF_TEST)
41 #include "../dfs_process_radar_found_ind.h"
42 #include "../dfs_partial_offload_radar.h"
43 #endif
44 #include <qdf_types.h>
45 
46 void dfs_set_update_nol_flag(struct wlan_dfs *dfs, bool val)
47 {
48 	dfs->update_nol = val;
49 }
50 
51 bool dfs_get_update_nol_flag(struct wlan_dfs *dfs)
52 {
53 	return dfs->update_nol;
54 }
55 
56 /**
57  * dfs_nol_elem_free_work_cb -  Free NOL element
58  *
59  * Free the NOL element memory
60  */
61 static void dfs_nol_elem_free_work_cb(void *context)
62 {
63 	struct wlan_dfs *dfs = (struct wlan_dfs *)context;
64 	struct dfs_nolelem *nol_head;
65 
66 	while (true) {
67 		WLAN_DFSNOL_LOCK(dfs);
68 
69 		nol_head = TAILQ_FIRST(&dfs->dfs_nol_free_list);
70 		if (nol_head) {
71 			TAILQ_REMOVE(&dfs->dfs_nol_free_list, nol_head,
72 				     nolelem_list);
73 			WLAN_DFSNOL_UNLOCK(dfs);
74 			qdf_hrtimer_kill(&nol_head->nol_timer);
75 			qdf_mem_free(nol_head);
76 		} else {
77 			WLAN_DFSNOL_UNLOCK(dfs);
78 			break;
79 		}
80 	}
81 }
82 
83 void dfs_nol_attach(struct wlan_dfs *dfs)
84 {
85 	dfs->wlan_dfs_nol_timeout = DFS_NOL_TIMEOUT_S;
86 	qdf_create_work(NULL, &dfs->dfs_nol_elem_free_work,
87 			dfs_nol_elem_free_work_cb, dfs);
88 	TAILQ_INIT(&dfs->dfs_nol_free_list);
89 	dfs->dfs_use_nol = 1;
90 	WLAN_DFSNOL_LOCK_CREATE(dfs);
91 }
92 
93 void dfs_nol_detach(struct wlan_dfs *dfs)
94 {
95 	dfs_nol_timer_cleanup(dfs);
96 	qdf_flush_work(&dfs->dfs_nol_elem_free_work);
97 	qdf_destroy_work(NULL, &dfs->dfs_nol_elem_free_work);
98 	WLAN_DFSNOL_LOCK_DESTROY(dfs);
99 }
100 
101 /**
102  * dfs_nol_delete() - Delete the given frequency/chwidth from the NOL.
103  * @dfs: Pointer to wlan_dfs structure.
104  * @delfreq: Freq to delete.
105  * @delchwidth: Channel width to delete.
106  */
107 static void dfs_nol_delete(struct wlan_dfs *dfs,
108 		uint16_t delfreq,
109 		uint16_t delchwidth)
110 {
111 	struct dfs_nolelem *nol, **prev_next;
112 
113 	if (!dfs) {
114 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
115 		return;
116 	}
117 
118 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
119 		"remove channel=%d/%d MHz from NOL",
120 		 delfreq, delchwidth);
121 	prev_next = &(dfs->dfs_nol);
122 	nol = dfs->dfs_nol;
123 	while (nol) {
124 		if (nol->nol_freq == delfreq &&
125 			nol->nol_chwidth == delchwidth) {
126 			*prev_next = nol->nol_next;
127 			dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
128 				"removing channel %d/%dMHz from NOL tstamp=%d",
129 				 nol->nol_freq,
130 				nol->nol_chwidth,
131 				(qdf_system_ticks_to_msecs
132 				 (qdf_system_ticks()) / 1000));
133 			TAILQ_INSERT_TAIL(&dfs->dfs_nol_free_list,
134 						nol, nolelem_list);
135 			nol = *prev_next;
136 
137 			/* Update the NOL counter. */
138 			dfs->dfs_nol_count--;
139 
140 			/* Be paranoid! */
141 			if (dfs->dfs_nol_count < 0) {
142 				dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "dfs_nol_count < 0; eek!");
143 				dfs->dfs_nol_count = 0;
144 			}
145 
146 		} else {
147 			prev_next = &(nol->nol_next);
148 			nol = nol->nol_next;
149 		}
150 	}
151 }
152 
153 /**
154  * dfs_remove_from_nol() - Remove the freq from NOL list.
155  * @arg: argument of the timer
156  *
157  * When NOL times out, this function removes the channel from NOL list.
158  */
159 #ifdef CONFIG_CHAN_FREQ_API
160 
161 static enum qdf_hrtimer_restart_status
162 dfs_remove_from_nol(qdf_hrtimer_data_t *arg)
163 {
164 	struct wlan_dfs *dfs;
165 	uint16_t delfreq;
166 	uint16_t delchwidth;
167 	uint8_t chan;
168 	struct dfs_nolelem *nol_arg;
169 
170 	nol_arg = container_of(arg, struct dfs_nolelem, nol_timer);
171 	dfs = nol_arg->nol_dfs;
172 	delfreq = nol_arg->nol_freq;
173 	delchwidth = nol_arg->nol_chwidth;
174 
175 	/* Delete the given NOL entry. */
176 	DFS_NOL_DELETE_CHAN_LOCKED(dfs, delfreq, delchwidth);
177 
178 	utils_dfs_reg_update_nol_chan_for_freq(dfs->dfs_pdev_obj,
179 					       &delfreq, 1, DFS_NOL_RESET);
180 	/* Update the wireless stack with the new NOL. */
181 	dfs_nol_update(dfs);
182 
183 	dfs_mlme_nol_timeout_notification(dfs->dfs_pdev_obj);
184 	chan = utils_dfs_freq_to_chan(delfreq);
185 	utils_dfs_deliver_event(dfs->dfs_pdev_obj, delfreq,
186 				WLAN_EV_NOL_FINISHED);
187 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
188 		  "remove channel %d from nol", chan);
189 	utils_dfs_unmark_precac_nol_for_freq(dfs->dfs_pdev_obj, delfreq);
190 
191 	utils_dfs_save_nol(dfs->dfs_pdev_obj);
192 
193 	/*
194 	 * Check if a channel is configured by the user to which we have to
195 	 * switch after it's NOL expiry. If that is the case, change
196 	 * channel immediately.
197 	 *
198 	 * If a channel switch is required (indicated by the return value of
199 	 * dfs_switch_to_postnol_chan_if_nol_expired), return from this function
200 	 * without posting Start event to Agile SM. That will be taken care
201 	 * of, after VAP start.
202 	 */
203 	if (dfs_switch_to_postnol_chan_if_nol_expired(dfs))
204 		return QDF_HRTIMER_NORESTART;
205 	/*
206 	 * If BW Expand is enabled, check if the user configured channel is
207 	 * available. If it is available, STOP the AGILE SM and Restart the
208 	 * AGILE SM. This will clear any old preCAC/RCAC chan information.
209 	 */
210 	if (dfs_bwexpand_find_usr_cnf_chan(dfs)) {
211 		utils_dfs_agile_sm_deliver_evt(dfs->dfs_pdev_obj,
212 					       DFS_AGILE_SM_EV_AGILE_STOP);
213 		utils_dfs_agile_sm_deliver_evt(dfs->dfs_pdev_obj,
214 					       DFS_AGILE_SM_EV_AGILE_START);
215 	} else {
216 		/*
217 		 * In case of interCAC feature, check if the user configured
218 		 * desired channel is RCAC done or not.
219 		 * (AP operating on an intermediate channel as desired channel
220 		 * is still not CAC done). If the RCAC of the desired channel
221 		 * was interrupted by radar, initiate RCAC on NOL expiry
222 		 * of the channel.
223 		 *
224 		 * If rcac is not started by dfs_restart_rcac_on_nol_expiry()
225 		 * API initiate rcac start here.
226 		 */
227 		if (!dfs_restart_rcac_on_nol_expiry(dfs))
228 			utils_dfs_agile_sm_deliver_evt(dfs->dfs_pdev_obj,
229 						       DFS_AGILE_SM_EV_AGILE_START);
230 	}
231 	return QDF_HRTIMER_NORESTART;
232 }
233 #endif
234 
235 void dfs_print_nol(struct wlan_dfs *dfs)
236 {
237 	struct dfs_nolelem *nol;
238 	int i = 0;
239 	uint32_t diff_ms, remaining_sec;
240 
241 	if (!dfs) {
242 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
243 		return;
244 	}
245 
246 	nol = dfs->dfs_nol;
247 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL, "NOL");
248 	while (nol) {
249 		diff_ms = qdf_do_div(qdf_get_monotonic_boottime() -
250 				     nol->nol_start_us, 1000);
251 		if (nol->nol_timeout_ms > diff_ms)
252 			diff_ms = (nol->nol_timeout_ms - diff_ms);
253 		else
254 			diff_ms = 0;
255 		remaining_sec = diff_ms / 1000; /* Convert to seconds */
256 		dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS,
257 			"nol:%d channel=%d MHz width=%d MHz time left=%u seconds nol start_us=%llu",
258 			i++, nol->nol_freq,
259 			nol->nol_chwidth,
260 			remaining_sec,
261 			nol->nol_start_us);
262 		nol = nol->nol_next;
263 	}
264 }
265 
266 void dfs_print_nolhistory(struct wlan_dfs *dfs)
267 {
268 	struct dfs_channel *chan_list;
269 	int i, j;
270 	int nchans;
271 
272 	if (!dfs) {
273 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
274 		return;
275 	}
276 
277 	nchans = dfs_get_num_chans();
278 
279 	chan_list = qdf_mem_malloc(nchans * sizeof(*chan_list));
280 	if (!chan_list)
281 		return;
282 
283 	utils_dfs_get_nol_history_chan_list(dfs->dfs_pdev_obj,
284 					    (void *)chan_list, &nchans);
285 	if (!nchans) {
286 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "zero chans");
287 		qdf_mem_free(chan_list);
288 		return;
289 	}
290 
291 	for (i = 0, j = 0; i < nchans; i++, j++)
292 		dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS,
293 			 "nolhistory = %d channel = %d MHz",
294 			 j, chan_list[i].dfs_ch_freq);
295 
296 	qdf_mem_free(chan_list);
297 }
298 
299 void dfs_get_nol(struct wlan_dfs *dfs,
300 		struct dfsreq_nolelem *dfs_nol,
301 		int *nchan)
302 {
303 	struct dfs_nolelem *nol;
304 
305 	*nchan = 0;
306 
307 	if (!dfs) {
308 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
309 		return;
310 	}
311 
312 	nol = dfs->dfs_nol;
313 	while (nol) {
314 		dfs_nol[*nchan].nol_freq = nol->nol_freq;
315 		dfs_nol[*nchan].nol_chwidth = nol->nol_chwidth;
316 		dfs_nol[*nchan].nol_start_us = nol->nol_start_us;
317 		dfs_nol[*nchan].nol_timeout_ms = nol->nol_timeout_ms;
318 		++(*nchan);
319 		nol = nol->nol_next;
320 	}
321 }
322 
323 #ifdef CONFIG_CHAN_FREQ_API
324 void dfs_set_nol(struct wlan_dfs *dfs,
325 		 struct dfsreq_nolelem *dfs_nol,
326 		 int nchan)
327 {
328 #define TIME_IN_MS 1000
329 	uint32_t nol_time_lft_ms;
330 	struct dfs_channel chan;
331 	int i;
332 
333 	if (!dfs) {
334 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
335 		return;
336 	}
337 
338 	for (i = 0; i < nchan; i++) {
339 		nol_time_lft_ms = qdf_do_div(qdf_get_monotonic_boottime() -
340 					     dfs_nol[i].nol_start_us, 1000);
341 
342 		if (nol_time_lft_ms < dfs_nol[i].nol_timeout_ms) {
343 			chan.dfs_ch_freq = dfs_nol[i].nol_freq;
344 			chan.dfs_ch_flags = 0;
345 			chan.dfs_ch_flagext = 0;
346 			nol_time_lft_ms =
347 				(dfs_nol[i].nol_timeout_ms - nol_time_lft_ms);
348 
349 			DFS_NOL_ADD_CHAN_LOCKED(dfs, chan.dfs_ch_freq,
350 						(nol_time_lft_ms / TIME_IN_MS));
351 			utils_dfs_reg_update_nol_chan_for_freq(
352 						dfs->dfs_pdev_obj,
353 						&chan.dfs_ch_freq,
354 						1, DFS_NOL_SET);
355 		}
356 	}
357 #undef TIME_IN_MS
358 	dfs_nol_update(dfs);
359 }
360 #else
361 #endif
362 
363 void dfs_nol_addchan(struct wlan_dfs *dfs,
364 		uint16_t freq,
365 		uint32_t dfs_nol_timeout)
366 {
367 #define TIME_IN_MS 1000
368 #define TIME_IN_US (TIME_IN_MS * 1000)
369 	struct dfs_nolelem *nol, *elem, *prev;
370 	/* For now, assume all events are 20MHz wide. */
371 	int ch_width = 20;
372 
373 	if (!dfs) {
374 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
375 		return;
376 	}
377 	nol = dfs->dfs_nol;
378 	prev = dfs->dfs_nol;
379 	elem = NULL;
380 	while (nol) {
381 		if ((nol->nol_freq == freq) &&
382 				(nol->nol_chwidth == ch_width)) {
383 			nol->nol_start_us = qdf_get_monotonic_boottime();
384 			nol->nol_timeout_ms = dfs_nol_timeout * TIME_IN_MS;
385 
386 			dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
387 				"Update OS Ticks for NOL %d MHz / %d MHz",
388 				 nol->nol_freq, nol->nol_chwidth);
389 			qdf_hrtimer_cancel(&nol->nol_timer);
390 			qdf_hrtimer_start(&nol->nol_timer,
391 					  qdf_time_ms_to_ktime(
392 						nol->nol_timeout_ms),
393 					  QDF_HRTIMER_MODE_REL);
394 			return;
395 		}
396 		prev = nol;
397 		nol = nol->nol_next;
398 	}
399 
400 	/* Add a new element to the NOL. */
401 	elem = (struct dfs_nolelem *)qdf_mem_malloc(sizeof(struct dfs_nolelem));
402 	if (!elem)
403 		goto bad;
404 
405 	qdf_mem_zero(elem, sizeof(*elem));
406 	elem->nol_dfs = dfs;
407 	elem->nol_freq = freq;
408 	elem->nol_chwidth = ch_width;
409 	elem->nol_start_us = qdf_get_monotonic_boottime();
410 	elem->nol_timeout_ms = dfs_nol_timeout*TIME_IN_MS;
411 	elem->nol_next = NULL;
412 	if (prev) {
413 		prev->nol_next = elem;
414 	} else {
415 		/* This is the first element in the NOL. */
416 		dfs->dfs_nol = elem;
417 	}
418 
419 	qdf_hrtimer_init(&elem->nol_timer, dfs_remove_from_nol,
420 			 QDF_CLOCK_MONOTONIC, QDF_HRTIMER_MODE_REL,
421 			 QDF_CONTEXT_TASKLET);
422 	qdf_hrtimer_start(&elem->nol_timer,
423 			  qdf_time_ms_to_ktime(dfs_nol_timeout * TIME_IN_MS),
424 			  QDF_HRTIMER_MODE_REL);
425 
426 	/* Update the NOL counter. */
427 	dfs->dfs_nol_count++;
428 
429 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
430 		"new NOL channel %d MHz / %d MHz",
431 		 elem->nol_freq, elem->nol_chwidth);
432 	return;
433 
434 bad:
435 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL | WLAN_DEBUG_DFS,
436 		"failed to allocate memory for nol entry");
437 
438 #undef TIME_IN_MS
439 #undef TIME_IN_US
440 }
441 
442 void dfs_get_nol_chfreq_and_chwidth(struct dfsreq_nolelem *dfs_nol,
443 		uint32_t *nol_chfreq,
444 		uint32_t *nol_chwidth,
445 		int index)
446 {
447 	if (!dfs_nol)
448 		return;
449 
450 	*nol_chfreq = dfs_nol[index].nol_freq;
451 	*nol_chwidth = dfs_nol[index].nol_chwidth;
452 }
453 
454 void dfs_nol_update(struct wlan_dfs *dfs)
455 {
456 	struct dfsreq_nolelem *dfs_nol;
457 	int nlen;
458 
459 	if (!dfs->dfs_nol_count) {
460 		dfs_debug(dfs, WLAN_DEBUG_DFS_NOL, "dfs_nol_count is zero");
461 		dfs_mlme_clist_update(dfs->dfs_pdev_obj, NULL, 0);
462 		return;
463 	}
464 
465 	/*
466 	 * Allocate enough entries to store the NOL. At least on Linux
467 	 * (don't ask why), if you allocate a 0 entry array, the
468 	 * returned pointer is 0x10.  Make sure you're aware of this
469 	 * when you start debugging.
470 	 */
471 	dfs_nol = (struct dfsreq_nolelem *)qdf_mem_malloc(
472 		sizeof(struct dfsreq_nolelem) * dfs->dfs_nol_count);
473 
474 	/*
475 	 * XXX TODO: if this fails, just schedule a task to retry
476 	 * updating the NOL at a later stage.  That way the NOL
477 	 * update _DOES_ happen - hopefully the failure was just
478 	 * temporary.
479 	 */
480 	if (!dfs_nol)
481 		return;
482 
483 	DFS_GET_NOL_LOCKED(dfs, dfs_nol, &nlen);
484 
485 	/* Be suitably paranoid for now. */
486 	if (nlen != dfs->dfs_nol_count)
487 		dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "nlen (%d) != dfs->dfs_nol_count (%d)!",
488 			 nlen, dfs->dfs_nol_count);
489 
490 	/*
491 	 * Call the driver layer to have it recalculate the NOL flags
492 	 * for each driver/umac channel. If the list is empty, pass
493 	 * NULL instead of dfs_nol. The operating system may have some
494 	 * special representation for "malloc a 0 byte memory region"
495 	 * - for example, Linux 2.6.38-13 (ubuntu) returns 0x10 rather
496 	 * than a valid allocation (and is likely not NULL so the
497 	 * pointer doesn't match NULL checks in any later code.
498 	 */
499 	dfs_mlme_clist_update(dfs->dfs_pdev_obj,
500 			(nlen > 0) ? dfs_nol : NULL,
501 			nlen);
502 
503 	qdf_mem_free(dfs_nol);
504 }
505 
506 void dfs_nol_free_list(struct wlan_dfs *dfs)
507 {
508 	struct dfs_nolelem *nol = dfs->dfs_nol, *prev;
509 
510 	while (nol) {
511 		prev = nol;
512 		nol = nol->nol_next;
513 		qdf_mem_free(prev);
514 		/* Update the NOL counter. */
515 		dfs->dfs_nol_count--;
516 
517 		if (dfs->dfs_nol_count < 0) {
518 			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs_nol_count < 0");
519 			ASSERT(0);
520 		}
521 	}
522 
523 	dfs->dfs_nol = NULL;
524 }
525 
526 #ifdef CONFIG_CHAN_FREQ_API
527 void dfs_nol_timer_cleanup(struct wlan_dfs *dfs)
528 {
529 	struct dfs_nolelem *nol;
530 	uint16_t nol_freq;
531 
532 	while (true) {
533 		WLAN_DFSNOL_LOCK(dfs);
534 
535 		nol = dfs->dfs_nol;
536 		if (nol) {
537 			dfs->dfs_nol = nol->nol_next;
538 			dfs->dfs_nol_count--;
539 			nol_freq = nol->nol_freq;
540 			WLAN_DFSNOL_UNLOCK(dfs);
541 			utils_dfs_reg_update_nol_chan_for_freq(
542 					dfs->dfs_pdev_obj,
543 					&nol_freq,
544 					1,
545 					DFS_NOL_RESET);
546 			qdf_hrtimer_kill(&nol->nol_timer);
547 			qdf_mem_free(nol);
548 		} else {
549 			WLAN_DFSNOL_UNLOCK(dfs);
550 			break;
551 		}
552 	}
553 }
554 #endif
555 
556 void dfs_nol_workqueue_cleanup(struct wlan_dfs *dfs)
557 {
558 	qdf_flush_work(&dfs->dfs_nol_elem_free_work);
559 }
560 
561 int dfs_get_use_nol(struct wlan_dfs *dfs)
562 {
563 	return dfs->dfs_use_nol;
564 }
565 
566 int dfs_get_nol_timeout(struct wlan_dfs *dfs)
567 {
568 	return dfs->wlan_dfs_nol_timeout;
569 }
570 
571 void dfs_getnol(struct wlan_dfs *dfs, void *dfs_nolinfo)
572 {
573 	struct dfsreq_nolinfo *nolinfo = (struct dfsreq_nolinfo *)dfs_nolinfo;
574 
575 	DFS_GET_NOL_LOCKED(dfs, nolinfo->dfs_nol, &(nolinfo->dfs_ch_nchans));
576 }
577 
578 #if !defined(MOBILE_DFS_SUPPORT)
579 #ifdef CONFIG_CHAN_FREQ_API
580 void dfs_clear_nolhistory(struct wlan_dfs *dfs)
581 {
582 	struct dfs_channel *chan_list;
583 	int nchans;
584 	bool sta_opmode;
585 	int i;
586 	qdf_freq_t *nol_freq_list = NULL;
587 	uint32_t num_nol_history_chans;
588 
589 	if (!dfs->dfs_is_stadfs_enabled)
590 		return;
591 
592 	sta_opmode = dfs_mlme_is_opmode_sta(dfs->dfs_pdev_obj);
593 	if (!sta_opmode)
594 		return;
595 
596 	nchans = dfs_get_num_chans();
597 
598 	if (!nchans) {
599 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "zero chans");
600 		return;
601 	}
602 
603 	chan_list = qdf_mem_malloc(nchans * sizeof(*chan_list));
604 	if (!chan_list)
605 		return;
606 
607 	utils_dfs_get_nol_history_chan_list(dfs->dfs_pdev_obj,
608 					    (void *)chan_list,
609 					    &num_nol_history_chans);
610 
611 	if (!num_nol_history_chans) {
612 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "zero chans");
613 		qdf_mem_free(chan_list);
614 		return;
615 	}
616 
617 	if (num_nol_history_chans > nchans)
618 		num_nol_history_chans = nchans;
619 
620 	nol_freq_list =
621 		qdf_mem_malloc(num_nol_history_chans * sizeof(qdf_freq_t));
622 
623 	if (!nol_freq_list) {
624 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS, "Unable to alloc memory for freq list");
625 		qdf_mem_free(chan_list);
626 		return;
627 	}
628 
629 	for (i = 0; i < num_nol_history_chans; i++)
630 		nol_freq_list[i] = chan_list[i].dfs_ch_freq;
631 
632 	utils_dfs_reg_update_nol_history_chan_for_freq(dfs->dfs_pdev_obj,
633 						       nol_freq_list,
634 						       num_nol_history_chans,
635 						       DFS_NOL_HISTORY_RESET);
636 	qdf_mem_free(chan_list);
637 	qdf_mem_free(nol_freq_list);
638 }
639 #endif
640 #endif
641 
642 #if defined(WLAN_DFS_PARTIAL_OFFLOAD) && defined(HOST_DFS_SPOOF_TEST) && \
643 	defined(CONFIG_CHAN_FREQ_API)
644 void dfs_remove_spoof_channel_from_nol(struct wlan_dfs *dfs)
645 {
646 	struct dfs_nolelem *nol;
647 	uint16_t freq_list[MAX_20MHZ_SUBCHANS];
648 	int i, nchans = 0;
649 
650 	nchans = dfs_get_bonding_channels_for_freq(dfs,
651 						   &dfs->dfs_radar_found_chan,
652 						   SEG_ID_PRIMARY,
653 						   DETECTOR_ID_0,
654 						   freq_list);
655 
656 	WLAN_DFSNOL_LOCK(dfs);
657 	for (i = 0; i < nchans && i < MAX_20MHZ_SUBCHANS; i++) {
658 		nol = dfs->dfs_nol;
659 		while (nol) {
660 			if (nol->nol_freq == freq_list[i]) {
661 				qdf_hrtimer_start(&nol->nol_timer,
662 						  qdf_time_ms_to_ktime(0),
663 						  QDF_HRTIMER_MODE_REL);
664 				break;
665 			}
666 			nol = nol->nol_next;
667 		}
668 	}
669 	WLAN_DFSNOL_UNLOCK(dfs);
670 
671 	utils_dfs_reg_update_nol_chan_for_freq(dfs->dfs_pdev_obj,
672 					     freq_list, nchans, DFS_NOL_RESET);
673 }
674 #endif
675