xref: /wlan-dirver/qca-wifi-host-cmn/umac/dfs/core/src/misc/dfs_nol.c (revision 3149adf58a329e17232a4c0e58d460d025edd55a)
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 	elem->nol_dfs = dfs;
405 	elem->nol_freq = freq;
406 	elem->nol_chwidth = ch_width;
407 	elem->nol_start_ticks = qdf_system_ticks();
408 	elem->nol_timeout_ms = dfs_nol_timeout*TIME_IN_MS;
409 	elem->nol_next = NULL;
410 	if (prev) {
411 		prev->nol_next = elem;
412 	} else {
413 		/* This is the first element in the NOL. */
414 		dfs->dfs_nol = elem;
415 	}
416 
417 	qdf_timer_init(NULL,
418 			&elem->nol_timer, dfs_remove_from_nol,
419 			elem, QDF_TIMER_TYPE_WAKE_APPS);
420 	qdf_timer_mod(&elem->nol_timer, dfs_nol_timeout * TIME_IN_MS);
421 
422 	/* Update the NOL counter. */
423 	dfs->dfs_nol_count++;
424 
425 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL,
426 		"new NOL channel %d MHz / %d MHz",
427 		 elem->nol_freq, elem->nol_chwidth);
428 	return;
429 
430 bad:
431 	dfs_debug(dfs, WLAN_DEBUG_DFS_NOL | WLAN_DEBUG_DFS,
432 		"failed to allocate memory for nol entry");
433 
434 #undef TIME_IN_MS
435 #undef TIME_IN_US
436 }
437 
438 void dfs_get_nol_chfreq_and_chwidth(struct dfsreq_nolelem *dfs_nol,
439 		uint32_t *nol_chfreq,
440 		uint32_t *nol_chwidth,
441 		int index)
442 {
443 	if (!dfs_nol)
444 		return;
445 
446 	*nol_chfreq = dfs_nol[index].nol_freq;
447 	*nol_chwidth = dfs_nol[index].nol_chwidth;
448 }
449 
450 void dfs_nol_update(struct wlan_dfs *dfs)
451 {
452 	struct dfsreq_nolelem *dfs_nol;
453 	int nlen;
454 
455 	/*
456 	 * Allocate enough entries to store the NOL. At least on Linux
457 	 * (don't ask why), if you allocate a 0 entry array, the
458 	 * returned pointer is 0x10.  Make sure you're aware of this
459 	 * when you start debugging.
460 	 */
461 	dfs_nol = (struct dfsreq_nolelem *)qdf_mem_malloc(
462 		sizeof(struct dfsreq_nolelem) * dfs->dfs_nol_count);
463 
464 	if (!dfs_nol) {
465 		/*
466 		 * XXX TODO: if this fails, just schedule a task to retry
467 		 * updating the NOL at a later stage.  That way the NOL
468 		 * update _DOES_ happen - hopefully the failure was just
469 		 * temporary.
470 		 */
471 		dfs_alert(dfs, WLAN_DEBUG_DFS_ALWAYS, "failed to allocate NOL update memory!");
472 		return;
473 	}
474 
475 	DFS_GET_NOL_LOCKED(dfs, dfs_nol, &nlen);
476 
477 	/* Be suitably paranoid for now. */
478 	if (nlen != dfs->dfs_nol_count)
479 		dfs_info(NULL, WLAN_DEBUG_DFS_ALWAYS, "nlen (%d) != dfs->dfs_nol_count (%d)!",
480 			 nlen, dfs->dfs_nol_count);
481 
482 	/*
483 	 * Call the driver layer to have it recalculate the NOL flags
484 	 * for each driver/umac channel. If the list is empty, pass
485 	 * NULL instead of dfs_nol. The operating system may have some
486 	 * special representation for "malloc a 0 byte memory region"
487 	 * - for example, Linux 2.6.38-13 (ubuntu) returns 0x10 rather
488 	 * than a valid allocation (and is likely not NULL so the
489 	 * pointer doesn't match NULL checks in any later code.
490 	 */
491 	dfs_mlme_clist_update(dfs->dfs_pdev_obj,
492 			(nlen > 0) ? dfs_nol : NULL,
493 			nlen);
494 
495 	qdf_mem_free(dfs_nol);
496 }
497 
498 void dfs_nol_free_list(struct wlan_dfs *dfs)
499 {
500 	struct dfs_nolelem *nol = dfs->dfs_nol, *prev;
501 
502 	while (nol) {
503 		prev = nol;
504 		nol = nol->nol_next;
505 		qdf_mem_free(prev);
506 		/* Update the NOL counter. */
507 		dfs->dfs_nol_count--;
508 
509 		if (dfs->dfs_nol_count < 0) {
510 			dfs_err(dfs, WLAN_DEBUG_DFS_ALWAYS,  "dfs_nol_count < 0");
511 			ASSERT(0);
512 		}
513 	}
514 
515 	dfs->dfs_nol = NULL;
516 }
517 
518 void dfs_nol_timer_cleanup(struct wlan_dfs *dfs)
519 {
520 	struct dfs_nolelem *nol;
521 
522 	WLAN_DFSNOL_LOCK(dfs);
523 	nol = dfs->dfs_nol;
524 	while (nol) {
525 		dfs->dfs_nol = nol->nol_next;
526 		dfs->dfs_nol_count--;
527 
528 		if (!qdf_timer_stop(&nol->nol_timer)) {
529 			/*
530 			 * Unlock is required so that when we sync with the
531 			 * nol_timeout timer we do not run into deadlock.
532 			 */
533 			WLAN_DFSNOL_UNLOCK(dfs);
534 			qdf_timer_sync_cancel(&(nol->nol_timer));
535 			WLAN_DFSNOL_LOCK(dfs);
536 		}
537 
538 		qdf_mem_free(nol);
539 		nol = dfs->dfs_nol;
540 	}
541 	WLAN_DFSNOL_UNLOCK(dfs);
542 }
543 
544 void dfs_nol_workqueue_cleanup(struct wlan_dfs *dfs)
545 {
546 	qdf_flush_work(&dfs->dfs_nol_elem_free_work);
547 }
548 
549 int dfs_get_use_nol(struct wlan_dfs *dfs)
550 {
551 	return dfs->dfs_use_nol;
552 }
553 
554 int dfs_get_nol_timeout(struct wlan_dfs *dfs)
555 {
556 	return dfs->wlan_dfs_nol_timeout;
557 }
558 
559 void dfs_getnol(struct wlan_dfs *dfs, void *dfs_nolinfo)
560 {
561 	struct dfsreq_nolinfo *nolinfo = (struct dfsreq_nolinfo *)dfs_nolinfo;
562 
563 	DFS_GET_NOL_LOCKED(dfs, nolinfo->dfs_nol, &(nolinfo->dfs_ch_nchans));
564 }
565 
566 void dfs_clear_nolhistory(struct wlan_dfs *dfs)
567 {
568 	/* We should have a dfs_clear_nolhistory API from Regdomain. */
569 	struct dfs_channel *c, lc;
570 	int i;
571 	int nchans = 0;
572 
573 	c = &lc;
574 	dfs_mlme_get_dfs_ch_nchans(dfs->dfs_pdev_obj, &nchans);
575 	for (i = 0; i < nchans; i++) {
576 		dfs_mlme_get_dfs_ch_channels(dfs->dfs_pdev_obj,
577 				&(c->dfs_ch_freq),
578 				&(c->dfs_ch_flags),
579 				&(c->dfs_ch_flagext),
580 				&(c->dfs_ch_ieee),
581 				&(c->dfs_ch_vhtop_ch_freq_seg1),
582 				&(c->dfs_ch_vhtop_ch_freq_seg2),
583 				i);
584 		WLAN_CHAN_CLR_HISTORY_RADAR(c);
585 	}
586 }
587