xref: /wlan-dirver/qca-wifi-host-cmn/umac/dfs/core/src/misc/dfs_nol.c (revision c8e2987f9325baadee03d0265544a08c4a0217b0)
1 /*
2  * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2002-2010, Atheros Communications Inc.
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: This file contains NOL related functionality, NOL being the non
20  * occupancy list. After radar has been detected in a particular channel,
21  * the channel cannot be used for a period of 30 minutes which is called
22  * the non occupancy. The NOL is basically a list of all the channels that
23  * radar has been detected on. Each channel has a 30 minute timer associated
24  * with it. This file contains the functionality to add a channel to the NOL,
25  * the NOL timer  function and the functionality to remove a channel from the
26  * NOL when its time is up.
27  */
28 
29 #include "../dfs.h"
30 #include "../dfs_channel.h"
31 #include "../dfs_ioctl_private.h"
32 #include "../dfs_internal.h"
33 #include <qdf_time.h>
34 #include <wlan_dfs_mlme_api.h>
35 #include <wlan_dfs_utils_api.h>
36 #include <wlan_reg_services_api.h>
37 
38 /**
39  * dfs_nol_timeout() - NOL timeout function.
40  *
41  * Clears the WLAN_CHAN_DFS_RADAR_FOUND flag for the NOL timeout channel.
42  */
43 static os_timer_func(dfs_nol_timeout)
44 {
45 	struct dfs_channel *c = NULL, lc;
46 	unsigned long oldest, now;
47 	struct wlan_dfs *dfs = NULL;
48 	int i;
49 	int nchans = 0;
50 
51 	c = &lc;
52 
53 	OS_GET_TIMER_ARG(dfs, struct wlan_dfs *);
54 	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans);
55 
56 	now = oldest = qdf_system_ticks();
57 	for (i = 0; i < nchans; i++) {
58 		dfs_mlme_get_dfs_ch_channels(dfs->dfs_pdev_obj,
59 				&(c->dfs_ch_freq),
60 				&(c->dfs_ch_flags),
61 				&(c->dfs_ch_flagext),
62 				&(c->dfs_ch_ieee),
63 				&(c->dfs_ch_vhtop_ch_freq_seg1),
64 				&(c->dfs_ch_vhtop_ch_freq_seg2),
65 				i);
66 		if (WLAN_IS_CHAN_RADAR(c)) {
67 			if (qdf_system_time_after_eq(now,
68 						dfs->dfs_nol_event[i] +
69 						dfs_get_nol_timeout(dfs))) {
70 				c->dfs_ch_flagext &=
71 					~WLAN_CHAN_DFS_RADAR_FOUND;
72 				if (c->dfs_ch_flags &
73 						WLAN_CHAN_DFS_RADAR) {
74 					/*
75 					 * NB: do this here so we get only one
76 					 * msg instead of one for every channel
77 					 * table entry.
78 					 */
79 					dfs_debug(dfs, WLAN_DEBUG_DFS,
80 						"radar on channel %u (%u MHz) cleared after timeout",
81 
82 						c->dfs_ch_ieee,
83 						c->dfs_ch_freq);
84 				}
85 			} else if (dfs->dfs_nol_event[i] < oldest)
86 				oldest = dfs->dfs_nol_event[i];
87 		}
88 	}
89 	if (oldest != now) {
90 		/* Arrange to process next channel up for a status change. */
91 		qdf_timer_mod(&dfs->dfs_nol_timer,
92 				dfs_get_nol_timeout(dfs) -
93 				qdf_system_ticks_to_msecs(qdf_system_ticks()));
94 	}
95 }
96 
97 /**
98  * dfs_nol_elem_free_work_cb -  Free NOL element
99  *
100  * Free the NOL element memory
101  */
102 static void dfs_nol_elem_free_work_cb(void *context)
103 {
104 	struct wlan_dfs *dfs = (struct wlan_dfs *)context;
105 	struct dfs_nolelem *tmp_nol_entry, *nol_entry;
106 
107 	WLAN_DFSNOL_LOCK(dfs);
108 	if (!TAILQ_EMPTY(&dfs->dfs_nol_free_list))
109 		TAILQ_FOREACH_SAFE(nol_entry,
110 				&dfs->dfs_nol_free_list,
111 				nolelem_list,
112 				tmp_nol_entry) {
113 			TAILQ_REMOVE(&dfs->dfs_nol_free_list,
114 					nol_entry, nolelem_list);
115 			qdf_mem_free(nol_entry);
116 		}
117 	WLAN_DFSNOL_UNLOCK(dfs);
118 }
119 
120 void dfs_nol_timer_init(struct wlan_dfs *dfs)
121 {
122 	qdf_timer_init(NULL,
123 			&(dfs->dfs_nol_timer),
124 			dfs_nol_timeout,
125 			(void *)(dfs),
126 			QDF_TIMER_TYPE_WAKE_APPS);
127 }
128 
129 void dfs_nol_attach(struct wlan_dfs *dfs)
130 {
131 	dfs->wlan_dfs_nol_timeout = DFS_NOL_TIMEOUT_S;
132 	dfs_nol_timer_init(dfs);
133 	qdf_create_work(NULL, &dfs->dfs_nol_elem_free_work,
134 			dfs_nol_elem_free_work_cb, dfs);
135 	TAILQ_INIT(&dfs->dfs_nol_free_list);
136 	dfs->dfs_use_nol = 1;
137 	WLAN_DFSNOL_LOCK_CREATE(dfs);
138 }
139 
140 void dfs_nol_detach(struct wlan_dfs *dfs)
141 {
142 	dfs_nol_timer_cleanup(dfs);
143 	qdf_flush_work(&dfs->dfs_nol_elem_free_work);
144 	qdf_destroy_work(NULL, &dfs->dfs_nol_elem_free_work);
145 	WLAN_DFSNOL_LOCK_DESTROY(dfs);
146 }
147 
148 /**
149  * dfs_nol_delete() - Delete the given frequency/chwidth from the NOL.
150  * @dfs: Pointer to wlan_dfs structure.
151  * @delfreq: Freq to delete.
152  * @delchwidth: Channel width to delete.
153  */
154 static void dfs_nol_delete(struct wlan_dfs *dfs,
155 		uint16_t delfreq,
156 		uint16_t delchwidth)
157 {
158 	struct dfs_nolelem *nol, **prev_next;
159 
160 	if (!dfs) {
161 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
162 		return;
163 	}
164 
165 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
166 		"remove channel=%d/%d MHz from NOL",
167 		 delfreq, delchwidth);
168 	prev_next = &(dfs->dfs_nol);
169 	nol = dfs->dfs_nol;
170 	while (nol != NULL) {
171 		if (nol->nol_freq == delfreq &&
172 			nol->nol_chwidth == delchwidth) {
173 			*prev_next = nol->nol_next;
174 			dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
175 				"removing channel %d/%dMHz from NOL tstamp=%d",
176 				 nol->nol_freq,
177 				nol->nol_chwidth,
178 				(qdf_system_ticks_to_msecs
179 				 (qdf_system_ticks()) / 1000));
180 			TAILQ_INSERT_TAIL(&dfs->dfs_nol_free_list,
181 						nol, nolelem_list);
182 			nol = *prev_next;
183 
184 			/* Update the NOL counter. */
185 			dfs->dfs_nol_count--;
186 
187 			/* Be paranoid! */
188 			if (dfs->dfs_nol_count < 0) {
189 				dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "dfs_nol_count < 0; eek!");
190 				dfs->dfs_nol_count = 0;
191 			}
192 
193 		} else {
194 			prev_next = &(nol->nol_next);
195 			nol = nol->nol_next;
196 		}
197 	}
198 }
199 
200 /**
201  * dfs_remove_from_nol() - Remove the freq from NOL list.
202  *
203  * When NOL times out, this function removes the channel from NOL list.
204  */
205 static os_timer_func(dfs_remove_from_nol)
206 {
207 	struct dfs_nolelem *nol_arg;
208 	struct wlan_dfs *dfs;
209 	uint16_t delfreq;
210 	uint16_t delchwidth;
211 	uint8_t chan;
212 
213 	OS_GET_TIMER_ARG(nol_arg, struct dfs_nolelem *);
214 
215 	dfs = nol_arg->nol_dfs;
216 	delfreq = nol_arg->nol_freq;
217 	delchwidth = nol_arg->nol_chwidth;
218 
219 	/* Delete the given NOL entry. */
220 	DFS_NOL_DELETE_CHAN_LOCKED(dfs, delfreq, delchwidth);
221 
222 	/* Update the wireless stack with the new NOL. */
223 	dfs_nol_update(dfs);
224 
225 	dfs_mlme_nol_timeout_notification(dfs->dfs_pdev_obj);
226 	chan = utils_dfs_freq_to_chan(delfreq);
227 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
228 		    "remove channel %d from nol", chan);
229 	utils_dfs_reg_update_nol_ch(dfs->dfs_pdev_obj,
230 				    &chan, 1, DFS_NOL_RESET);
231 	utils_dfs_save_nol(dfs->dfs_pdev_obj);
232 
233 	/*
234 	 * Free the NOL element in a thread. This is to avoid freeing the
235 	 * timer object from within timer callback function . The nol element
236 	 * contains the timer Object.
237 	 */
238 	qdf_sched_work(NULL, &dfs->dfs_nol_elem_free_work);
239 }
240 
241 void dfs_print_nol(struct wlan_dfs *dfs)
242 {
243 	struct dfs_nolelem *nol;
244 	int i = 0;
245 	uint32_t diff_ms, remaining_sec;
246 
247 	if (!dfs) {
248 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
249 		return;
250 	}
251 
252 	nol = dfs->dfs_nol;
253 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL, "NOL");
254 	while (nol != NULL) {
255 		diff_ms = qdf_system_ticks_to_msecs(qdf_system_ticks() -
256 				nol->nol_start_ticks);
257 		diff_ms = (nol->nol_timeout_ms - diff_ms);
258 		remaining_sec = diff_ms / 1000; /* Convert to seconds */
259 		dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS,
260 			"nol:%d channel=%d MHz width=%d MHz time left=%u seconds nol starttick=%llu",
261 			i++, nol->nol_freq,
262 			nol->nol_chwidth,
263 			remaining_sec,
264 			(uint64_t)nol->nol_start_ticks);
265 		nol = nol->nol_next;
266 	}
267 }
268 
269 void dfs_print_nolhistory(struct wlan_dfs *dfs)
270 {
271 	struct dfs_channel *c, lc;
272 	int i, j = 0;
273 	int nchans = 0;
274 
275 	if (!dfs) {
276 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
277 		return;
278 	}
279 
280 	c = &lc;
281 
282 	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans);
283 	for (i = 0; i < nchans; i++) {
284 		dfs_mlme_get_dfs_ch_channels(dfs->dfs_pdev_obj,
285 				&(c->dfs_ch_freq),
286 				&(c->dfs_ch_flags),
287 				&(c->dfs_ch_flagext),
288 				&(c->dfs_ch_ieee),
289 				&(c->dfs_ch_vhtop_ch_freq_seg1),
290 				&(c->dfs_ch_vhtop_ch_freq_seg2),
291 				i);
292 		if (WLAN_IS_CHAN_HISTORY_RADAR(c)) {
293 			dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS,
294 				"nolhistory:%d channel=%d MHz Flags=%llx",
295 				j, c->dfs_ch_freq, c->dfs_ch_flags);
296 			j++;
297 		}
298 	}
299 }
300 
301 void dfs_get_nol(struct wlan_dfs *dfs,
302 		struct dfsreq_nolelem *dfs_nol,
303 		int *nchan)
304 {
305 	struct dfs_nolelem *nol;
306 
307 	*nchan = 0;
308 
309 	if (!dfs) {
310 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
311 		return;
312 	}
313 
314 	nol = dfs->dfs_nol;
315 	while (nol != NULL) {
316 		dfs_nol[*nchan].nol_freq = nol->nol_freq;
317 		dfs_nol[*nchan].nol_chwidth = nol->nol_chwidth;
318 		dfs_nol[*nchan].nol_start_ticks = nol->nol_start_ticks;
319 		dfs_nol[*nchan].nol_timeout_ms = nol->nol_timeout_ms;
320 		++(*nchan);
321 		nol = nol->nol_next;
322 	}
323 }
324 
325 void dfs_set_nol(struct wlan_dfs *dfs,
326 		struct dfsreq_nolelem *dfs_nol,
327 		int nchan)
328 {
329 #define TIME_IN_MS 1000
330 	uint32_t nol_time_left_ms;
331 	struct dfs_channel chan;
332 	int i;
333 	uint8_t chan_num;
334 
335 	if (!dfs) {
336 		dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs is NULL");
337 		return;
338 	}
339 
340 	for (i = 0; i < nchan; i++) {
341 		nol_time_left_ms =
342 			qdf_system_ticks_to_msecs(qdf_system_ticks() -
343 				dfs_nol[i].nol_start_ticks);
344 
345 		if (nol_time_left_ms < dfs_nol[i].nol_timeout_ms) {
346 			chan.dfs_ch_freq = dfs_nol[i].nol_freq;
347 			chan.dfs_ch_flags = 0;
348 			chan.dfs_ch_flagext = 0;
349 			nol_time_left_ms =
350 				(dfs_nol[i].nol_timeout_ms - nol_time_left_ms);
351 
352 			DFS_NOL_ADD_CHAN_LOCKED(dfs, chan.dfs_ch_freq,
353 					(nol_time_left_ms / TIME_IN_MS));
354 			chan_num = utils_dfs_freq_to_chan(chan.dfs_ch_freq);
355 			utils_dfs_reg_update_nol_ch(dfs->dfs_pdev_obj,
356 						&chan_num, 1, DFS_NOL_SET);
357 		}
358 	}
359 #undef TIME_IN_MS
360 	dfs_nol_update(dfs);
361 }
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 != NULL) {
381 		if ((nol->nol_freq == freq) &&
382 				(nol->nol_chwidth == ch_width)) {
383 			nol->nol_start_ticks = qdf_system_ticks();
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 
390 			qdf_timer_stop(&nol->nol_timer);
391 			qdf_timer_mod(&nol->nol_timer,
392 					dfs_nol_timeout * TIME_IN_MS);
393 			return;
394 		}
395 		prev = nol;
396 		nol = nol->nol_next;
397 	}
398 
399 	/* Add a new element to the NOL. */
400 	elem = (struct dfs_nolelem *)qdf_mem_malloc(sizeof(struct dfs_nolelem));
401 	if (!elem)
402 		goto bad;
403 
404 	qdf_mem_zero(elem, sizeof(*elem));
405 	elem->nol_dfs = dfs;
406 	elem->nol_freq = freq;
407 	elem->nol_chwidth = ch_width;
408 	elem->nol_start_ticks = qdf_system_ticks();
409 	elem->nol_timeout_ms = dfs_nol_timeout*TIME_IN_MS;
410 	elem->nol_next = NULL;
411 	if (prev) {
412 		prev->nol_next = elem;
413 	} else {
414 		/* This is the first element in the NOL. */
415 		dfs->dfs_nol = elem;
416 	}
417 
418 	qdf_timer_init(NULL,
419 			&elem->nol_timer, dfs_remove_from_nol,
420 			elem, QDF_TIMER_TYPE_WAKE_APPS);
421 	qdf_timer_mod(&elem->nol_timer, dfs_nol_timeout * TIME_IN_MS);
422 
423 	/* Update the NOL counter. */
424 	dfs->dfs_nol_count++;
425 
426 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
427 		"new NOL channel %d MHz / %d MHz",
428 		 elem->nol_freq, elem->nol_chwidth);
429 	return;
430 
431 bad:
432 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL | WLAN_DEBUG_DFS,
433 		"failed to allocate memory for nol entry");
434 
435 #undef TIME_IN_MS
436 #undef TIME_IN_US
437 }
438 
439 void dfs_get_nol_chfreq_and_chwidth(struct dfsreq_nolelem *dfs_nol,
440 		uint32_t *nol_chfreq,
441 		uint32_t *nol_chwidth,
442 		int index)
443 {
444 	if (!dfs_nol)
445 		return;
446 
447 	*nol_chfreq = dfs_nol[index].nol_freq;
448 	*nol_chwidth = dfs_nol[index].nol_chwidth;
449 }
450 
451 void dfs_nol_update(struct wlan_dfs *dfs)
452 {
453 	struct dfsreq_nolelem *dfs_nol;
454 	int nlen;
455 
456 	/*
457 	 * Allocate enough entries to store the NOL. At least on Linux
458 	 * (don't ask why), if you allocate a 0 entry array, the
459 	 * returned pointer is 0x10.  Make sure you're aware of this
460 	 * when you start debugging.
461 	 */
462 	dfs_nol = (struct dfsreq_nolelem *)qdf_mem_malloc(
463 		sizeof(struct dfsreq_nolelem) * dfs->dfs_nol_count);
464 
465 	if (!dfs_nol) {
466 		/*
467 		 * XXX TODO: if this fails, just schedule a task to retry
468 		 * updating the NOL at a later stage.  That way the NOL
469 		 * update _DOES_ happen - hopefully the failure was just
470 		 * temporary.
471 		 */
472 		dfs_alert(dfs, WLAN_DEBUG_DFS_ALWAYS, "failed to allocate NOL update memory!");
473 		return;
474 	}
475 
476 	DFS_GET_NOL_LOCKED(dfs, dfs_nol, &nlen);
477 
478 	/* Be suitably paranoid for now. */
479 	if (nlen != dfs->dfs_nol_count)
480 		dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "nlen (%d) != dfs->dfs_nol_count (%d)!",
481 			 nlen, dfs->dfs_nol_count);
482 
483 	/*
484 	 * Call the driver layer to have it recalculate the NOL flags
485 	 * for each driver/umac channel. If the list is empty, pass
486 	 * NULL instead of dfs_nol. The operating system may have some
487 	 * special representation for "malloc a 0 byte memory region"
488 	 * - for example, Linux 2.6.38-13 (ubuntu) returns 0x10 rather
489 	 * than a valid allocation (and is likely not NULL so the
490 	 * pointer doesn't match NULL checks in any later code.
491 	 */
492 	dfs_mlme_clist_update(dfs->dfs_pdev_obj,
493 			(nlen > 0) ? dfs_nol : NULL,
494 			nlen);
495 
496 	qdf_mem_free(dfs_nol);
497 }
498 
499 void dfs_nol_free_list(struct wlan_dfs *dfs)
500 {
501 	struct dfs_nolelem *nol = dfs->dfs_nol, *prev;
502 
503 	while (nol) {
504 		prev = nol;
505 		nol = nol->nol_next;
506 		qdf_mem_free(prev);
507 		/* Update the NOL counter. */
508 		dfs->dfs_nol_count--;
509 
510 		if (dfs->dfs_nol_count < 0) {
511 			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs_nol_count < 0");
512 			ASSERT(0);
513 		}
514 	}
515 
516 	dfs->dfs_nol = NULL;
517 }
518 
519 void dfs_nol_timer_cleanup(struct wlan_dfs *dfs)
520 {
521 	struct dfs_nolelem *nol;
522 
523 	WLAN_DFSNOL_LOCK(dfs);
524 	nol = dfs->dfs_nol;
525 	while (nol) {
526 		dfs->dfs_nol = nol->nol_next;
527 		dfs->dfs_nol_count--;
528 
529 		if (!qdf_timer_stop(&nol->nol_timer)) {
530 			/*
531 			 * Unlock is required so that when we sync with the
532 			 * nol_timeout timer we do not run into deadlock.
533 			 */
534 			WLAN_DFSNOL_UNLOCK(dfs);
535 			qdf_timer_sync_cancel(&(nol->nol_timer));
536 			WLAN_DFSNOL_LOCK(dfs);
537 		}
538 
539 		qdf_mem_free(nol);
540 		nol = dfs->dfs_nol;
541 	}
542 	WLAN_DFSNOL_UNLOCK(dfs);
543 }
544 
545 void dfs_nol_workqueue_cleanup(struct wlan_dfs *dfs)
546 {
547 	qdf_flush_work(&dfs->dfs_nol_elem_free_work);
548 }
549 
550 int dfs_get_use_nol(struct wlan_dfs *dfs)
551 {
552 	return dfs->dfs_use_nol;
553 }
554 
555 int dfs_get_nol_timeout(struct wlan_dfs *dfs)
556 {
557 	return dfs->wlan_dfs_nol_timeout;
558 }
559 
560 void dfs_getnol(struct wlan_dfs *dfs, void *dfs_nolinfo)
561 {
562 	struct dfsreq_nolinfo *nolinfo = (struct dfsreq_nolinfo *)dfs_nolinfo;
563 
564 	DFS_GET_NOL_LOCKED(dfs, nolinfo->dfs_nol, &(nolinfo->dfs_ch_nchans));
565 }
566 
567 void dfs_clear_nolhistory(struct wlan_dfs *dfs)
568 {
569 	/* We should have a dfs_clear_nolhistory API from Regdomain. */
570 	struct dfs_channel *c, lc;
571 	int i;
572 	int nchans = 0;
573 
574 	c = &lc;
575 	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans);
576 	for (i = 0; i < nchans; i++) {
577 		dfs_mlme_get_dfs_ch_channels(dfs->dfs_pdev_obj,
578 				&(c->dfs_ch_freq),
579 				&(c->dfs_ch_flags),
580 				&(c->dfs_ch_flagext),
581 				&(c->dfs_ch_ieee),
582 				&(c->dfs_ch_vhtop_ch_freq_seg1),
583 				&(c->dfs_ch_vhtop_ch_freq_seg2),
584 				i);
585 		WLAN_CHAN_CLR_HISTORY_RADAR(c);
586 	}
587 }
588