xref: /freebsd/sys/net80211/ieee80211_dfs.c (revision d745c852becf3792a2185003947324721209195a)
1b032f27cSSam Leffler /*-
2b032f27cSSam Leffler  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
3b032f27cSSam Leffler  * All rights reserved.
4b032f27cSSam Leffler  *
5b032f27cSSam Leffler  * Redistribution and use in source and binary forms, with or without
6b032f27cSSam Leffler  * modification, are permitted provided that the following conditions
7b032f27cSSam Leffler  * are met:
8b032f27cSSam Leffler  * 1. Redistributions of source code must retain the above copyright
9b032f27cSSam Leffler  *    notice, this list of conditions and the following disclaimer.
10b032f27cSSam Leffler  * 2. Redistributions in binary form must reproduce the above copyright
11b032f27cSSam Leffler  *    notice, this list of conditions and the following disclaimer in the
12b032f27cSSam Leffler  *    documentation and/or other materials provided with the distribution.
13b032f27cSSam Leffler  *
14b032f27cSSam Leffler  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15b032f27cSSam Leffler  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16b032f27cSSam Leffler  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17b032f27cSSam Leffler  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18b032f27cSSam Leffler  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19b032f27cSSam Leffler  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20b032f27cSSam Leffler  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21b032f27cSSam Leffler  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22b032f27cSSam Leffler  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23b032f27cSSam Leffler  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24b032f27cSSam Leffler  */
25b032f27cSSam Leffler 
26b032f27cSSam Leffler #include <sys/cdefs.h>
27b032f27cSSam Leffler #ifdef __FreeBSD__
28b032f27cSSam Leffler __FBSDID("$FreeBSD$");
29b032f27cSSam Leffler #endif
30b032f27cSSam Leffler 
31b032f27cSSam Leffler /*
32b032f27cSSam Leffler  * IEEE 802.11 DFS/Radar support.
33b032f27cSSam Leffler  */
34b032f27cSSam Leffler #include "opt_inet.h"
35b032f27cSSam Leffler #include "opt_wlan.h"
36b032f27cSSam Leffler 
37b032f27cSSam Leffler #include <sys/param.h>
38b032f27cSSam Leffler #include <sys/systm.h>
39b032f27cSSam Leffler #include <sys/mbuf.h>
40b032f27cSSam Leffler #include <sys/malloc.h>
41b032f27cSSam Leffler #include <sys/kernel.h>
42b032f27cSSam Leffler 
43b032f27cSSam Leffler #include <sys/socket.h>
44b032f27cSSam Leffler #include <sys/sockio.h>
45b032f27cSSam Leffler #include <sys/endian.h>
46b032f27cSSam Leffler #include <sys/errno.h>
47b032f27cSSam Leffler #include <sys/proc.h>
48b032f27cSSam Leffler #include <sys/sysctl.h>
49b032f27cSSam Leffler 
50b032f27cSSam Leffler #include <net/if.h>
51b032f27cSSam Leffler #include <net/if_media.h>
52b032f27cSSam Leffler 
53b032f27cSSam Leffler #include <net80211/ieee80211_var.h>
54b032f27cSSam Leffler 
55*d745c852SEd Schouten static MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
56b032f27cSSam Leffler 
57a77b10b3SSam Leffler static	int ieee80211_nol_timeout = 30*60;		/* 30 minutes */
58a77b10b3SSam Leffler SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
59a77b10b3SSam Leffler 	&ieee80211_nol_timeout, 0, "NOL timeout (secs)");
60b032f27cSSam Leffler #define	NOL_TIMEOUT	msecs_to_ticks(ieee80211_nol_timeout*1000)
61a77b10b3SSam Leffler 
62a77b10b3SSam Leffler static	int ieee80211_cac_timeout = 60;		/* 60 seconds */
63a77b10b3SSam Leffler SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
64a77b10b3SSam Leffler 	&ieee80211_cac_timeout, 0, "CAC timeout (secs)");
65b032f27cSSam Leffler #define	CAC_TIMEOUT	msecs_to_ticks(ieee80211_cac_timeout*1000)
66b032f27cSSam Leffler 
67b032f27cSSam Leffler void
68b032f27cSSam Leffler ieee80211_dfs_attach(struct ieee80211com *ic)
69b032f27cSSam Leffler {
70b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
71b032f27cSSam Leffler 
7292c4a81cSSam Leffler 	callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
7392c4a81cSSam Leffler 	callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
74b032f27cSSam Leffler }
75b032f27cSSam Leffler 
76b032f27cSSam Leffler void
77b032f27cSSam Leffler ieee80211_dfs_detach(struct ieee80211com *ic)
78b032f27cSSam Leffler {
79b032f27cSSam Leffler 	/* NB: we assume no locking is needed */
80b032f27cSSam Leffler 	ieee80211_dfs_reset(ic);
81b032f27cSSam Leffler }
82b032f27cSSam Leffler 
83b032f27cSSam Leffler void
84b032f27cSSam Leffler ieee80211_dfs_reset(struct ieee80211com *ic)
85b032f27cSSam Leffler {
86b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
87b032f27cSSam Leffler 	int i;
88b032f27cSSam Leffler 
89b032f27cSSam Leffler 	/* NB: we assume no locking is needed */
90b032f27cSSam Leffler 	/* NB: cac_timer should be cleared by the state machine */
91b032f27cSSam Leffler 	callout_drain(&dfs->nol_timer);
92b032f27cSSam Leffler 	for (i = 0; i < ic->ic_nchans; i++)
93b032f27cSSam Leffler 		ic->ic_channels[i].ic_state = 0;
94b032f27cSSam Leffler 	dfs->lastchan = NULL;
95b032f27cSSam Leffler }
96b032f27cSSam Leffler 
97b032f27cSSam Leffler static void
98b032f27cSSam Leffler cac_timeout(void *arg)
99b032f27cSSam Leffler {
100b032f27cSSam Leffler 	struct ieee80211vap *vap = arg;
101b032f27cSSam Leffler 	struct ieee80211com *ic = vap->iv_ic;
102b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
103b032f27cSSam Leffler 	int i;
104b032f27cSSam Leffler 
10592c4a81cSSam Leffler 	IEEE80211_LOCK_ASSERT(ic);
10692c4a81cSSam Leffler 
107b032f27cSSam Leffler 	if (vap->iv_state != IEEE80211_S_CAC)	/* NB: just in case */
108b032f27cSSam Leffler 		return;
109b032f27cSSam Leffler 	/*
110b032f27cSSam Leffler 	 * When radar is detected during a CAC we are woken
111b032f27cSSam Leffler 	 * up prematurely to switch to a new channel.
112b032f27cSSam Leffler 	 * Check the channel to decide how to act.
113b032f27cSSam Leffler 	 */
114b032f27cSSam Leffler 	if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
115b032f27cSSam Leffler 		ieee80211_notify_cac(ic, ic->ic_curchan,
116b032f27cSSam Leffler 		    IEEE80211_NOTIFY_CAC_RADAR);
117b032f27cSSam Leffler 
118b032f27cSSam Leffler 		if_printf(vap->iv_ifp,
119b032f27cSSam Leffler 		    "CAC timer on channel %u (%u MHz) stopped due to radar\n",
120b032f27cSSam Leffler 		    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
121b032f27cSSam Leffler 
122b032f27cSSam Leffler 		/* XXX clobbers any existing desired channel */
123b032f27cSSam Leffler 		/* NB: dfs->newchan may be NULL, that's ok */
124b032f27cSSam Leffler 		vap->iv_des_chan = dfs->newchan;
12592c4a81cSSam Leffler 		/* XXX recursive lock need ieee80211_new_state_locked */
126b032f27cSSam Leffler 		ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
127b032f27cSSam Leffler 	} else {
128b032f27cSSam Leffler 		if_printf(vap->iv_ifp,
129b032f27cSSam Leffler 		    "CAC timer on channel %u (%u MHz) expired; "
130b032f27cSSam Leffler 		    "no radar detected\n",
131b032f27cSSam Leffler 		    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
132b032f27cSSam Leffler 		/*
133b032f27cSSam Leffler 		 * Mark all channels with the current frequency
134b032f27cSSam Leffler 		 * as having completed CAC; this keeps us from
135b032f27cSSam Leffler 		 * doing it again until we change channels.
136b032f27cSSam Leffler 		 */
137b032f27cSSam Leffler 		for (i = 0; i < ic->ic_nchans; i++) {
138b032f27cSSam Leffler 			struct ieee80211_channel *c = &ic->ic_channels[i];
139b032f27cSSam Leffler 			if (c->ic_freq == ic->ic_curchan->ic_freq)
140b032f27cSSam Leffler 				c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
141b032f27cSSam Leffler 		}
142b032f27cSSam Leffler 		ieee80211_notify_cac(ic, ic->ic_curchan,
143b032f27cSSam Leffler 		    IEEE80211_NOTIFY_CAC_EXPIRE);
144b032f27cSSam Leffler 		ieee80211_cac_completeswitch(vap);
145b032f27cSSam Leffler 	}
146b032f27cSSam Leffler }
147b032f27cSSam Leffler 
148b032f27cSSam Leffler /*
149b032f27cSSam Leffler  * Initiate the CAC timer.  The driver is responsible
150b032f27cSSam Leffler  * for setting up the hardware to scan for radar on the
151b032f27cSSam Leffler  * channnel, we just handle timing things out.
152b032f27cSSam Leffler  */
153b032f27cSSam Leffler void
154b032f27cSSam Leffler ieee80211_dfs_cac_start(struct ieee80211vap *vap)
155b032f27cSSam Leffler {
156b032f27cSSam Leffler 	struct ieee80211com *ic = vap->iv_ic;
157b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
158b032f27cSSam Leffler 
159b032f27cSSam Leffler 	IEEE80211_LOCK_ASSERT(ic);
160b032f27cSSam Leffler 
161b032f27cSSam Leffler 	callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
162b032f27cSSam Leffler 	if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
163b032f27cSSam Leffler 	    ticks_to_secs(CAC_TIMEOUT),
164b032f27cSSam Leffler 	    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
165b032f27cSSam Leffler 	ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
166b032f27cSSam Leffler }
167b032f27cSSam Leffler 
168b032f27cSSam Leffler /*
169b032f27cSSam Leffler  * Clear the CAC timer.
170b032f27cSSam Leffler  */
171b032f27cSSam Leffler void
172b032f27cSSam Leffler ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
173b032f27cSSam Leffler {
174b032f27cSSam Leffler 	struct ieee80211com *ic = vap->iv_ic;
175b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
176b032f27cSSam Leffler 
177b032f27cSSam Leffler 	IEEE80211_LOCK_ASSERT(ic);
178b032f27cSSam Leffler 
179b032f27cSSam Leffler 	/* NB: racey but not important */
180b032f27cSSam Leffler 	if (callout_pending(&dfs->cac_timer)) {
181b032f27cSSam Leffler 		if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
182b032f27cSSam Leffler 		    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
183b032f27cSSam Leffler 		ieee80211_notify_cac(ic, ic->ic_curchan,
184b032f27cSSam Leffler 		    IEEE80211_NOTIFY_CAC_STOP);
185b032f27cSSam Leffler 	}
186b032f27cSSam Leffler 	callout_stop(&dfs->cac_timer);
187b032f27cSSam Leffler }
188b032f27cSSam Leffler 
189b032f27cSSam Leffler void
190b032f27cSSam Leffler ieee80211_dfs_cac_clear(struct ieee80211com *ic,
191b032f27cSSam Leffler 	const struct ieee80211_channel *chan)
192b032f27cSSam Leffler {
193b032f27cSSam Leffler 	int i;
194b032f27cSSam Leffler 
195b032f27cSSam Leffler 	for (i = 0; i < ic->ic_nchans; i++) {
196b032f27cSSam Leffler 		struct ieee80211_channel *c = &ic->ic_channels[i];
197b032f27cSSam Leffler 		if (c->ic_freq == chan->ic_freq)
198b032f27cSSam Leffler 			c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
199b032f27cSSam Leffler 	}
200b032f27cSSam Leffler }
201b032f27cSSam Leffler 
202b032f27cSSam Leffler static void
203b032f27cSSam Leffler dfs_timeout(void *arg)
204b032f27cSSam Leffler {
205b032f27cSSam Leffler 	struct ieee80211com *ic = arg;
206b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
207b032f27cSSam Leffler 	struct ieee80211_channel *c;
208b032f27cSSam Leffler 	int i, oldest, now;
209b032f27cSSam Leffler 
21092c4a81cSSam Leffler 	IEEE80211_LOCK_ASSERT(ic);
21192c4a81cSSam Leffler 
212b032f27cSSam Leffler 	now = oldest = ticks;
213b032f27cSSam Leffler 	for (i = 0; i < ic->ic_nchans; i++) {
214b032f27cSSam Leffler 		c = &ic->ic_channels[i];
215b032f27cSSam Leffler 		if (IEEE80211_IS_CHAN_RADAR(c)) {
216b032f27cSSam Leffler 			if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
217b032f27cSSam Leffler 				c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
218b032f27cSSam Leffler 				if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
219b032f27cSSam Leffler 					/*
220b032f27cSSam Leffler 					 * NB: do this here so we get only one
221b032f27cSSam Leffler 					 * msg instead of one for every channel
222b032f27cSSam Leffler 					 * table entry.
223b032f27cSSam Leffler 					 */
224b032f27cSSam Leffler 					if_printf(ic->ic_ifp, "radar on channel"
225b032f27cSSam Leffler 					    " %u (%u MHz) cleared after timeout\n",
226b032f27cSSam Leffler 					    c->ic_ieee, c->ic_freq);
227b032f27cSSam Leffler 					/* notify user space */
228b032f27cSSam Leffler 					c->ic_state &=
229b032f27cSSam Leffler 					    ~IEEE80211_CHANSTATE_NORADAR;
230b032f27cSSam Leffler 					ieee80211_notify_radar(ic, c);
231b032f27cSSam Leffler 				}
232b032f27cSSam Leffler 			} else if (dfs->nol_event[i] < oldest)
233b032f27cSSam Leffler 				oldest = dfs->nol_event[i];
234b032f27cSSam Leffler 		}
235b032f27cSSam Leffler 	}
236b032f27cSSam Leffler 	if (oldest != now) {
237b032f27cSSam Leffler 		/* arrange to process next channel up for a status change */
2388460188cSSam Leffler 		callout_schedule(&dfs->nol_timer, oldest + NOL_TIMEOUT - now);
239b032f27cSSam Leffler 	}
240b032f27cSSam Leffler }
241b032f27cSSam Leffler 
242b032f27cSSam Leffler static void
243b032f27cSSam Leffler announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
244b032f27cSSam Leffler 	const struct ieee80211_channel *newchan)
245b032f27cSSam Leffler {
246b032f27cSSam Leffler 	if (newchan == NULL)
247b032f27cSSam Leffler 		if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
248b032f27cSSam Leffler 		    curchan->ic_ieee, curchan->ic_freq);
249b032f27cSSam Leffler 	else
250b032f27cSSam Leffler 		if_printf(ifp, "radar detected on channel %u (%u MHz), "
251b032f27cSSam Leffler 		    "moving to channel %u (%u MHz)\n",
252b032f27cSSam Leffler 		    curchan->ic_ieee, curchan->ic_freq,
253b032f27cSSam Leffler 		    newchan->ic_ieee, newchan->ic_freq);
254b032f27cSSam Leffler }
255b032f27cSSam Leffler 
256b032f27cSSam Leffler /*
257b032f27cSSam Leffler  * Handle a radar detection event on a channel. The channel is
258b032f27cSSam Leffler  * added to the NOL list and we record the time of the event.
259b032f27cSSam Leffler  * Entries are aged out after NOL_TIMEOUT.  If radar was
260b032f27cSSam Leffler  * detected while doing CAC we force a state/channel change.
261b032f27cSSam Leffler  * Otherwise radar triggers a channel switch using the CSA
262b032f27cSSam Leffler  * mechanism (when the channel is the bss channel).
263b032f27cSSam Leffler  */
264b032f27cSSam Leffler void
265b032f27cSSam Leffler ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
266b032f27cSSam Leffler {
267b032f27cSSam Leffler 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
268b032f27cSSam Leffler 	int i, now;
269b032f27cSSam Leffler 
270b032f27cSSam Leffler 	IEEE80211_LOCK_ASSERT(ic);
271b032f27cSSam Leffler 
272b032f27cSSam Leffler 	/*
273b032f27cSSam Leffler 	 * Mark all entries with this frequency.  Notify user
274b032f27cSSam Leffler 	 * space and arrange for notification when the radar
275b032f27cSSam Leffler 	 * indication is cleared.  Then kick the NOL processing
276b032f27cSSam Leffler 	 * thread if not already running.
277b032f27cSSam Leffler 	 */
278b032f27cSSam Leffler 	now = ticks;
279b032f27cSSam Leffler 	for (i = 0; i < ic->ic_nchans; i++) {
280b032f27cSSam Leffler 		struct ieee80211_channel *c = &ic->ic_channels[i];
281b032f27cSSam Leffler 		if (c->ic_freq == chan->ic_freq) {
282b032f27cSSam Leffler 			c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
283b032f27cSSam Leffler 			c->ic_state |= IEEE80211_CHANSTATE_RADAR;
284b032f27cSSam Leffler 			dfs->nol_event[i] = now;
285b032f27cSSam Leffler 		}
286b032f27cSSam Leffler 	}
287b032f27cSSam Leffler 	ieee80211_notify_radar(ic, chan);
288b032f27cSSam Leffler 	chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
289b032f27cSSam Leffler 	if (!callout_pending(&dfs->nol_timer))
290b032f27cSSam Leffler 		callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
291b032f27cSSam Leffler 
292b032f27cSSam Leffler 	/*
293b032f27cSSam Leffler 	 * If radar is detected on the bss channel while
294b032f27cSSam Leffler 	 * doing CAC; force a state change by scheduling the
295b032f27cSSam Leffler 	 * callout to be dispatched asap.  Otherwise, if this
296b032f27cSSam Leffler 	 * event is for the bss channel then we must quiet
297b032f27cSSam Leffler 	 * traffic and schedule a channel switch.
298b032f27cSSam Leffler 	 *
299b032f27cSSam Leffler 	 * Note this allows us to receive notification about
300b032f27cSSam Leffler 	 * channels other than the bss channel; not sure
301b032f27cSSam Leffler 	 * that can/will happen but it's simple to support.
302b032f27cSSam Leffler 	 */
303b032f27cSSam Leffler 	if (chan == ic->ic_bsschan) {
304b032f27cSSam Leffler 		/* XXX need a way to defer to user app */
305b032f27cSSam Leffler 		dfs->newchan = ieee80211_dfs_pickchannel(ic);
306b032f27cSSam Leffler 
307b032f27cSSam Leffler 		announce_radar(ic->ic_ifp, chan, dfs->newchan);
308b032f27cSSam Leffler 
309b032f27cSSam Leffler 		if (callout_pending(&dfs->cac_timer))
3105c9b0f1dSSam Leffler 			callout_schedule(&dfs->cac_timer, 0);
311b032f27cSSam Leffler 		else if (dfs->newchan != NULL) {
312b032f27cSSam Leffler 			/* XXX mode 1, switch count 2 */
313b032f27cSSam Leffler 			/* XXX calculate switch count based on max
314b032f27cSSam Leffler 			  switch time and beacon interval? */
315b032f27cSSam Leffler 			ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
316b032f27cSSam Leffler 		} else {
317b032f27cSSam Leffler 			/*
318b032f27cSSam Leffler 			 * Spec says to stop all transmissions and
319b032f27cSSam Leffler 			 * wait on the current channel for an entry
320b032f27cSSam Leffler 			 * on the NOL to expire.
321b032f27cSSam Leffler 			 */
322b032f27cSSam Leffler 			/*XXX*/
323e1ab183cSAdrian Chadd 			if_printf(ic->ic_ifp, "%s: No free channels; waiting for entry "
324e1ab183cSAdrian Chadd 			    "on NOL to expire\n", __func__);
325b032f27cSSam Leffler 		}
326b032f27cSSam Leffler 	} else {
327b032f27cSSam Leffler 		/*
328b032f27cSSam Leffler 		 * Issue rate-limited console msgs.
329b032f27cSSam Leffler 		 */
330b032f27cSSam Leffler 		if (dfs->lastchan != chan) {
331b032f27cSSam Leffler 			dfs->lastchan = chan;
332b032f27cSSam Leffler 			dfs->cureps = 0;
333b032f27cSSam Leffler 			announce_radar(ic->ic_ifp, chan, NULL);
334b032f27cSSam Leffler 		} else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
335b032f27cSSam Leffler 			announce_radar(ic->ic_ifp, chan, NULL);
336b032f27cSSam Leffler 		}
337b032f27cSSam Leffler 	}
338b032f27cSSam Leffler }
339b032f27cSSam Leffler 
340b032f27cSSam Leffler struct ieee80211_channel *
341b032f27cSSam Leffler ieee80211_dfs_pickchannel(struct ieee80211com *ic)
342b032f27cSSam Leffler {
343b032f27cSSam Leffler 	struct ieee80211_channel *c;
344b032f27cSSam Leffler 	int i, flags;
345b032f27cSSam Leffler 	uint16_t v;
346b032f27cSSam Leffler 
347b032f27cSSam Leffler 	/*
348b032f27cSSam Leffler 	 * Consult the scan cache first.
349b032f27cSSam Leffler 	 */
350b032f27cSSam Leffler 	flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
351b032f27cSSam Leffler 	/*
352b032f27cSSam Leffler 	 * XXX if curchan is HT this will never find a channel
353b032f27cSSam Leffler 	 * XXX 'cuz we scan only legacy channels
354b032f27cSSam Leffler 	 */
355b032f27cSSam Leffler 	c = ieee80211_scan_pickchannel(ic, flags);
356b032f27cSSam Leffler 	if (c != NULL)
357b032f27cSSam Leffler 		return c;
358b032f27cSSam Leffler 	/*
359b032f27cSSam Leffler 	 * No channel found in scan cache; select a compatible
360b032f27cSSam Leffler 	 * one at random (skipping channels where radar has
361b032f27cSSam Leffler 	 * been detected).
362b032f27cSSam Leffler 	 */
363b032f27cSSam Leffler 	get_random_bytes(&v, sizeof(v));
364b032f27cSSam Leffler 	v %= ic->ic_nchans;
365b032f27cSSam Leffler 	for (i = v; i < ic->ic_nchans; i++) {
366b032f27cSSam Leffler 		c = &ic->ic_channels[i];
367b032f27cSSam Leffler 		if (!IEEE80211_IS_CHAN_RADAR(c) &&
368b032f27cSSam Leffler 		   (c->ic_flags & flags) == flags)
369b032f27cSSam Leffler 			return c;
370b032f27cSSam Leffler 	}
371b032f27cSSam Leffler 	for (i = 0; i < v; i++) {
372b032f27cSSam Leffler 		c = &ic->ic_channels[i];
373b032f27cSSam Leffler 		if (!IEEE80211_IS_CHAN_RADAR(c) &&
374b032f27cSSam Leffler 		   (c->ic_flags & flags) == flags)
375b032f27cSSam Leffler 			return c;
376b032f27cSSam Leffler 	}
377b032f27cSSam Leffler 	if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");
378b032f27cSSam Leffler 	return NULL;
379b032f27cSSam Leffler }
380