xref: /haiku/src/libs/compat/freebsd_wlan/net80211/ieee80211_dfs.c (revision 8244a9baad63102214028afbb3b1e2326f4c4cca)
1753c7e08SAugustin Cavalier /*-
2*8244a9baSAugustin Cavalier  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*8244a9baSAugustin Cavalier  *
4753c7e08SAugustin Cavalier  * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
5753c7e08SAugustin Cavalier  * All rights reserved.
6753c7e08SAugustin Cavalier  *
7753c7e08SAugustin Cavalier  * Redistribution and use in source and binary forms, with or without
8753c7e08SAugustin Cavalier  * modification, are permitted provided that the following conditions
9753c7e08SAugustin Cavalier  * are met:
10753c7e08SAugustin Cavalier  * 1. Redistributions of source code must retain the above copyright
11753c7e08SAugustin Cavalier  *    notice, this list of conditions and the following disclaimer.
12753c7e08SAugustin Cavalier  * 2. Redistributions in binary form must reproduce the above copyright
13753c7e08SAugustin Cavalier  *    notice, this list of conditions and the following disclaimer in the
14753c7e08SAugustin Cavalier  *    documentation and/or other materials provided with the distribution.
15753c7e08SAugustin Cavalier  *
16753c7e08SAugustin Cavalier  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17753c7e08SAugustin Cavalier  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18753c7e08SAugustin Cavalier  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19753c7e08SAugustin Cavalier  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20753c7e08SAugustin Cavalier  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21753c7e08SAugustin Cavalier  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22753c7e08SAugustin Cavalier  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23753c7e08SAugustin Cavalier  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24753c7e08SAugustin Cavalier  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25753c7e08SAugustin Cavalier  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26753c7e08SAugustin Cavalier  */
27753c7e08SAugustin Cavalier 
28753c7e08SAugustin Cavalier #include <sys/cdefs.h>
29753c7e08SAugustin Cavalier #ifdef __FreeBSD__
30*8244a9baSAugustin Cavalier __FBSDID("$FreeBSD: releng/12.0/sys/net80211/ieee80211_dfs.c 326272 2017-11-27 15:23:17Z pfg $");
31753c7e08SAugustin Cavalier #endif
32753c7e08SAugustin Cavalier 
33753c7e08SAugustin Cavalier /*
34753c7e08SAugustin Cavalier  * IEEE 802.11 DFS/Radar support.
35753c7e08SAugustin Cavalier  */
36753c7e08SAugustin Cavalier #include "opt_inet.h"
37753c7e08SAugustin Cavalier #include "opt_wlan.h"
38753c7e08SAugustin Cavalier 
39753c7e08SAugustin Cavalier #include <sys/param.h>
40753c7e08SAugustin Cavalier #include <sys/systm.h>
41753c7e08SAugustin Cavalier #include <sys/mbuf.h>
42753c7e08SAugustin Cavalier #include <sys/malloc.h>
43753c7e08SAugustin Cavalier #include <sys/kernel.h>
44753c7e08SAugustin Cavalier 
45753c7e08SAugustin Cavalier #include <sys/socket.h>
46753c7e08SAugustin Cavalier #include <sys/sockio.h>
47753c7e08SAugustin Cavalier #include <sys/endian.h>
48753c7e08SAugustin Cavalier #include <sys/errno.h>
49753c7e08SAugustin Cavalier #include <sys/proc.h>
50753c7e08SAugustin Cavalier #include <sys/sysctl.h>
51753c7e08SAugustin Cavalier 
52753c7e08SAugustin Cavalier #include <net/if.h>
53753c7e08SAugustin Cavalier #include <net/if_var.h>
54753c7e08SAugustin Cavalier #include <net/if_media.h>
55753c7e08SAugustin Cavalier #include <net/ethernet.h>
56753c7e08SAugustin Cavalier 
57753c7e08SAugustin Cavalier #include <net80211/ieee80211_var.h>
58753c7e08SAugustin Cavalier 
59753c7e08SAugustin Cavalier static MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
60753c7e08SAugustin Cavalier 
61753c7e08SAugustin Cavalier static	int ieee80211_nol_timeout = 30*60;		/* 30 minutes */
62753c7e08SAugustin Cavalier SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
63753c7e08SAugustin Cavalier 	&ieee80211_nol_timeout, 0, "NOL timeout (secs)");
64753c7e08SAugustin Cavalier #define	NOL_TIMEOUT	msecs_to_ticks(ieee80211_nol_timeout*1000)
65753c7e08SAugustin Cavalier 
66753c7e08SAugustin Cavalier static	int ieee80211_cac_timeout = 60;		/* 60 seconds */
67753c7e08SAugustin Cavalier SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
68753c7e08SAugustin Cavalier 	&ieee80211_cac_timeout, 0, "CAC timeout (secs)");
69753c7e08SAugustin Cavalier #define	CAC_TIMEOUT	msecs_to_ticks(ieee80211_cac_timeout*1000)
70753c7e08SAugustin Cavalier 
71753c7e08SAugustin Cavalier /*
72753c7e08SAugustin Cavalier  DFS* In order to facilitate  debugging, a couple of operating
73753c7e08SAugustin Cavalier  * modes aside from the default are needed.
74753c7e08SAugustin Cavalier  *
75753c7e08SAugustin Cavalier  * 0 - default CAC/NOL behaviour - ie, start CAC, place
76753c7e08SAugustin Cavalier  *     channel on NOL list.
77753c7e08SAugustin Cavalier  * 1 - send CAC, but don't change channel or add the channel
78753c7e08SAugustin Cavalier  *     to the NOL list.
79753c7e08SAugustin Cavalier  * 2 - just match on radar, don't send CAC or place channel in
80753c7e08SAugustin Cavalier  *     the NOL list.
81753c7e08SAugustin Cavalier  */
82753c7e08SAugustin Cavalier static	int ieee80211_dfs_debug = DFS_DBG_NONE;
83753c7e08SAugustin Cavalier 
84753c7e08SAugustin Cavalier /*
85753c7e08SAugustin Cavalier  * This option must not be included in the default kernel
86753c7e08SAugustin Cavalier  * as it allows users to plainly disable CAC/NOL handling.
87753c7e08SAugustin Cavalier  */
88753c7e08SAugustin Cavalier #ifdef	IEEE80211_DFS_DEBUG
89753c7e08SAugustin Cavalier SYSCTL_INT(_net_wlan, OID_AUTO, dfs_debug, CTLFLAG_RW,
90753c7e08SAugustin Cavalier 	&ieee80211_dfs_debug, 0, "DFS debug behaviour");
91753c7e08SAugustin Cavalier #endif
92753c7e08SAugustin Cavalier 
93753c7e08SAugustin Cavalier static int
94753c7e08SAugustin Cavalier null_set_quiet(struct ieee80211_node *ni, u_int8_t *quiet_elm)
95753c7e08SAugustin Cavalier {
96753c7e08SAugustin Cavalier 	return ENOSYS;
97753c7e08SAugustin Cavalier }
98753c7e08SAugustin Cavalier 
99753c7e08SAugustin Cavalier void
100753c7e08SAugustin Cavalier ieee80211_dfs_attach(struct ieee80211com *ic)
101753c7e08SAugustin Cavalier {
102753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
103753c7e08SAugustin Cavalier 
104753c7e08SAugustin Cavalier 	callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
105753c7e08SAugustin Cavalier 	callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
106753c7e08SAugustin Cavalier 
107753c7e08SAugustin Cavalier 	ic->ic_set_quiet = null_set_quiet;
108753c7e08SAugustin Cavalier }
109753c7e08SAugustin Cavalier 
110753c7e08SAugustin Cavalier void
111753c7e08SAugustin Cavalier ieee80211_dfs_detach(struct ieee80211com *ic)
112753c7e08SAugustin Cavalier {
113753c7e08SAugustin Cavalier 	/* NB: we assume no locking is needed */
114753c7e08SAugustin Cavalier 	ieee80211_dfs_reset(ic);
115753c7e08SAugustin Cavalier }
116753c7e08SAugustin Cavalier 
117753c7e08SAugustin Cavalier void
118753c7e08SAugustin Cavalier ieee80211_dfs_reset(struct ieee80211com *ic)
119753c7e08SAugustin Cavalier {
120753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
121753c7e08SAugustin Cavalier 	int i;
122753c7e08SAugustin Cavalier 
123753c7e08SAugustin Cavalier 	/* NB: we assume no locking is needed */
124753c7e08SAugustin Cavalier 	/* NB: cac_timer should be cleared by the state machine */
125753c7e08SAugustin Cavalier 	callout_drain(&dfs->nol_timer);
126753c7e08SAugustin Cavalier 	for (i = 0; i < ic->ic_nchans; i++)
127753c7e08SAugustin Cavalier 		ic->ic_channels[i].ic_state = 0;
128753c7e08SAugustin Cavalier 	dfs->lastchan = NULL;
129753c7e08SAugustin Cavalier }
130753c7e08SAugustin Cavalier 
131753c7e08SAugustin Cavalier static void
132753c7e08SAugustin Cavalier cac_timeout(void *arg)
133753c7e08SAugustin Cavalier {
134753c7e08SAugustin Cavalier 	struct ieee80211vap *vap = arg;
135753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
136753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
137753c7e08SAugustin Cavalier 	int i;
138753c7e08SAugustin Cavalier 
139753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
140753c7e08SAugustin Cavalier 
141753c7e08SAugustin Cavalier 	if (vap->iv_state != IEEE80211_S_CAC)	/* NB: just in case */
142753c7e08SAugustin Cavalier 		return;
143753c7e08SAugustin Cavalier 	/*
144753c7e08SAugustin Cavalier 	 * When radar is detected during a CAC we are woken
145753c7e08SAugustin Cavalier 	 * up prematurely to switch to a new channel.
146753c7e08SAugustin Cavalier 	 * Check the channel to decide how to act.
147753c7e08SAugustin Cavalier 	 */
148753c7e08SAugustin Cavalier 	if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
149753c7e08SAugustin Cavalier 		ieee80211_notify_cac(ic, ic->ic_curchan,
150753c7e08SAugustin Cavalier 		    IEEE80211_NOTIFY_CAC_RADAR);
151753c7e08SAugustin Cavalier 
152753c7e08SAugustin Cavalier 		if_printf(vap->iv_ifp,
153753c7e08SAugustin Cavalier 		    "CAC timer on channel %u (%u MHz) stopped due to radar\n",
154753c7e08SAugustin Cavalier 		    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
155753c7e08SAugustin Cavalier 
156753c7e08SAugustin Cavalier 		/* XXX clobbers any existing desired channel */
157753c7e08SAugustin Cavalier 		/* NB: dfs->newchan may be NULL, that's ok */
158753c7e08SAugustin Cavalier 		vap->iv_des_chan = dfs->newchan;
159753c7e08SAugustin Cavalier 		/* XXX recursive lock need ieee80211_new_state_locked */
160753c7e08SAugustin Cavalier 		ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
161753c7e08SAugustin Cavalier 	} else {
162753c7e08SAugustin Cavalier 		if_printf(vap->iv_ifp,
163753c7e08SAugustin Cavalier 		    "CAC timer on channel %u (%u MHz) expired; "
164753c7e08SAugustin Cavalier 		    "no radar detected\n",
165753c7e08SAugustin Cavalier 		    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
166753c7e08SAugustin Cavalier 		/*
167753c7e08SAugustin Cavalier 		 * Mark all channels with the current frequency
168753c7e08SAugustin Cavalier 		 * as having completed CAC; this keeps us from
169753c7e08SAugustin Cavalier 		 * doing it again until we change channels.
170753c7e08SAugustin Cavalier 		 */
171753c7e08SAugustin Cavalier 		for (i = 0; i < ic->ic_nchans; i++) {
172753c7e08SAugustin Cavalier 			struct ieee80211_channel *c = &ic->ic_channels[i];
173753c7e08SAugustin Cavalier 			if (c->ic_freq == ic->ic_curchan->ic_freq)
174753c7e08SAugustin Cavalier 				c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
175753c7e08SAugustin Cavalier 		}
176753c7e08SAugustin Cavalier 		ieee80211_notify_cac(ic, ic->ic_curchan,
177753c7e08SAugustin Cavalier 		    IEEE80211_NOTIFY_CAC_EXPIRE);
178753c7e08SAugustin Cavalier 		ieee80211_cac_completeswitch(vap);
179753c7e08SAugustin Cavalier 	}
180753c7e08SAugustin Cavalier }
181753c7e08SAugustin Cavalier 
182753c7e08SAugustin Cavalier /*
183753c7e08SAugustin Cavalier  * Initiate the CAC timer.  The driver is responsible
184753c7e08SAugustin Cavalier  * for setting up the hardware to scan for radar on the
185753c7e08SAugustin Cavalier  * channnel, we just handle timing things out.
186753c7e08SAugustin Cavalier  */
187753c7e08SAugustin Cavalier void
188753c7e08SAugustin Cavalier ieee80211_dfs_cac_start(struct ieee80211vap *vap)
189753c7e08SAugustin Cavalier {
190753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
191753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
192753c7e08SAugustin Cavalier 
193753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
194753c7e08SAugustin Cavalier 
195753c7e08SAugustin Cavalier 	callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
196753c7e08SAugustin Cavalier 	if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
197753c7e08SAugustin Cavalier 	    ticks_to_secs(CAC_TIMEOUT),
198753c7e08SAugustin Cavalier 	    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
199753c7e08SAugustin Cavalier 	ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
200753c7e08SAugustin Cavalier }
201753c7e08SAugustin Cavalier 
202753c7e08SAugustin Cavalier /*
203753c7e08SAugustin Cavalier  * Clear the CAC timer.
204753c7e08SAugustin Cavalier  */
205753c7e08SAugustin Cavalier void
206753c7e08SAugustin Cavalier ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
207753c7e08SAugustin Cavalier {
208753c7e08SAugustin Cavalier 	struct ieee80211com *ic = vap->iv_ic;
209753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
210753c7e08SAugustin Cavalier 
211753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
212753c7e08SAugustin Cavalier 
213753c7e08SAugustin Cavalier 	/* NB: racey but not important */
214753c7e08SAugustin Cavalier 	if (callout_pending(&dfs->cac_timer)) {
215753c7e08SAugustin Cavalier 		if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
216753c7e08SAugustin Cavalier 		    ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
217753c7e08SAugustin Cavalier 		ieee80211_notify_cac(ic, ic->ic_curchan,
218753c7e08SAugustin Cavalier 		    IEEE80211_NOTIFY_CAC_STOP);
219753c7e08SAugustin Cavalier 	}
220753c7e08SAugustin Cavalier 	callout_stop(&dfs->cac_timer);
221753c7e08SAugustin Cavalier }
222753c7e08SAugustin Cavalier 
223753c7e08SAugustin Cavalier void
224753c7e08SAugustin Cavalier ieee80211_dfs_cac_clear(struct ieee80211com *ic,
225753c7e08SAugustin Cavalier 	const struct ieee80211_channel *chan)
226753c7e08SAugustin Cavalier {
227753c7e08SAugustin Cavalier 	int i;
228753c7e08SAugustin Cavalier 
229753c7e08SAugustin Cavalier 	for (i = 0; i < ic->ic_nchans; i++) {
230753c7e08SAugustin Cavalier 		struct ieee80211_channel *c = &ic->ic_channels[i];
231753c7e08SAugustin Cavalier 		if (c->ic_freq == chan->ic_freq)
232753c7e08SAugustin Cavalier 			c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
233753c7e08SAugustin Cavalier 	}
234753c7e08SAugustin Cavalier }
235753c7e08SAugustin Cavalier 
236753c7e08SAugustin Cavalier static void
237753c7e08SAugustin Cavalier dfs_timeout(void *arg)
238753c7e08SAugustin Cavalier {
239753c7e08SAugustin Cavalier 	struct ieee80211com *ic = arg;
240753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
241753c7e08SAugustin Cavalier 	struct ieee80211_channel *c;
242753c7e08SAugustin Cavalier 	int i, oldest, now;
243753c7e08SAugustin Cavalier 
244753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
245753c7e08SAugustin Cavalier 
246753c7e08SAugustin Cavalier 	now = oldest = ticks;
247753c7e08SAugustin Cavalier 	for (i = 0; i < ic->ic_nchans; i++) {
248753c7e08SAugustin Cavalier 		c = &ic->ic_channels[i];
249753c7e08SAugustin Cavalier 		if (IEEE80211_IS_CHAN_RADAR(c)) {
250753c7e08SAugustin Cavalier 			if (ieee80211_time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
251753c7e08SAugustin Cavalier 				c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
252753c7e08SAugustin Cavalier 				if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
253753c7e08SAugustin Cavalier 					/*
254753c7e08SAugustin Cavalier 					 * NB: do this here so we get only one
255753c7e08SAugustin Cavalier 					 * msg instead of one for every channel
256753c7e08SAugustin Cavalier 					 * table entry.
257753c7e08SAugustin Cavalier 					 */
258753c7e08SAugustin Cavalier 					ic_printf(ic, "radar on channel %u "
259753c7e08SAugustin Cavalier 					    "(%u MHz) cleared after timeout\n",
260753c7e08SAugustin Cavalier 					    c->ic_ieee, c->ic_freq);
261753c7e08SAugustin Cavalier 					/* notify user space */
262753c7e08SAugustin Cavalier 					c->ic_state &=
263753c7e08SAugustin Cavalier 					    ~IEEE80211_CHANSTATE_NORADAR;
264753c7e08SAugustin Cavalier 					ieee80211_notify_radar(ic, c);
265753c7e08SAugustin Cavalier 				}
266753c7e08SAugustin Cavalier 			} else if (dfs->nol_event[i] < oldest)
267753c7e08SAugustin Cavalier 				oldest = dfs->nol_event[i];
268753c7e08SAugustin Cavalier 		}
269753c7e08SAugustin Cavalier 	}
270753c7e08SAugustin Cavalier 	if (oldest != now) {
271753c7e08SAugustin Cavalier 		/* arrange to process next channel up for a status change */
272753c7e08SAugustin Cavalier 		callout_schedule(&dfs->nol_timer, oldest + NOL_TIMEOUT - now);
273753c7e08SAugustin Cavalier 	}
274753c7e08SAugustin Cavalier }
275753c7e08SAugustin Cavalier 
276753c7e08SAugustin Cavalier static void
277753c7e08SAugustin Cavalier announce_radar(struct ieee80211com *ic, const struct ieee80211_channel *curchan,
278753c7e08SAugustin Cavalier 	const struct ieee80211_channel *newchan)
279753c7e08SAugustin Cavalier {
280753c7e08SAugustin Cavalier 	if (newchan == NULL)
281753c7e08SAugustin Cavalier 		ic_printf(ic, "radar detected on channel %u (%u MHz)\n",
282753c7e08SAugustin Cavalier 		    curchan->ic_ieee, curchan->ic_freq);
283753c7e08SAugustin Cavalier 	else
284753c7e08SAugustin Cavalier 		ic_printf(ic, "radar detected on channel %u (%u MHz), "
285753c7e08SAugustin Cavalier 		    "moving to channel %u (%u MHz)\n",
286753c7e08SAugustin Cavalier 		    curchan->ic_ieee, curchan->ic_freq,
287753c7e08SAugustin Cavalier 		    newchan->ic_ieee, newchan->ic_freq);
288753c7e08SAugustin Cavalier }
289753c7e08SAugustin Cavalier 
290753c7e08SAugustin Cavalier /*
291753c7e08SAugustin Cavalier  * Handle a radar detection event on a channel. The channel is
292753c7e08SAugustin Cavalier  * added to the NOL list and we record the time of the event.
293753c7e08SAugustin Cavalier  * Entries are aged out after NOL_TIMEOUT.  If radar was
294753c7e08SAugustin Cavalier  * detected while doing CAC we force a state/channel change.
295753c7e08SAugustin Cavalier  * Otherwise radar triggers a channel switch using the CSA
296753c7e08SAugustin Cavalier  * mechanism (when the channel is the bss channel).
297753c7e08SAugustin Cavalier  */
298753c7e08SAugustin Cavalier void
299753c7e08SAugustin Cavalier ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
300753c7e08SAugustin Cavalier {
301753c7e08SAugustin Cavalier 	struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
302753c7e08SAugustin Cavalier 	int i, now;
303753c7e08SAugustin Cavalier 
304753c7e08SAugustin Cavalier 	IEEE80211_LOCK_ASSERT(ic);
305753c7e08SAugustin Cavalier 
306753c7e08SAugustin Cavalier 	/*
307753c7e08SAugustin Cavalier 	 * If doing DFS debugging (mode 2), don't bother
308753c7e08SAugustin Cavalier 	 * running the rest of this function.
309753c7e08SAugustin Cavalier 	 *
310753c7e08SAugustin Cavalier 	 * Simply announce the presence of the radar and continue
311753c7e08SAugustin Cavalier 	 * along merrily.
312753c7e08SAugustin Cavalier 	 */
313753c7e08SAugustin Cavalier 	if (ieee80211_dfs_debug == DFS_DBG_NOCSANOL) {
314753c7e08SAugustin Cavalier 		announce_radar(ic, chan, chan);
315753c7e08SAugustin Cavalier 		ieee80211_notify_radar(ic, chan);
316753c7e08SAugustin Cavalier 		return;
317753c7e08SAugustin Cavalier 	}
318753c7e08SAugustin Cavalier 
319753c7e08SAugustin Cavalier 	/*
320753c7e08SAugustin Cavalier 	 * Don't mark the channel and don't put it into NOL
321753c7e08SAugustin Cavalier 	 * if we're doing DFS debugging.
322753c7e08SAugustin Cavalier 	 */
323753c7e08SAugustin Cavalier 	if (ieee80211_dfs_debug == DFS_DBG_NONE) {
324753c7e08SAugustin Cavalier 		/*
325753c7e08SAugustin Cavalier 		 * Mark all entries with this frequency.  Notify user
326753c7e08SAugustin Cavalier 		 * space and arrange for notification when the radar
327753c7e08SAugustin Cavalier 		 * indication is cleared.  Then kick the NOL processing
328753c7e08SAugustin Cavalier 		 * thread if not already running.
329753c7e08SAugustin Cavalier 		 */
330753c7e08SAugustin Cavalier 		now = ticks;
331753c7e08SAugustin Cavalier 		for (i = 0; i < ic->ic_nchans; i++) {
332753c7e08SAugustin Cavalier 			struct ieee80211_channel *c = &ic->ic_channels[i];
333753c7e08SAugustin Cavalier 			if (c->ic_freq == chan->ic_freq) {
334753c7e08SAugustin Cavalier 				c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
335753c7e08SAugustin Cavalier 				c->ic_state |= IEEE80211_CHANSTATE_RADAR;
336753c7e08SAugustin Cavalier 				dfs->nol_event[i] = now;
337753c7e08SAugustin Cavalier 			}
338753c7e08SAugustin Cavalier 		}
339753c7e08SAugustin Cavalier 		ieee80211_notify_radar(ic, chan);
340753c7e08SAugustin Cavalier 		chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
341753c7e08SAugustin Cavalier 		if (!callout_pending(&dfs->nol_timer))
342753c7e08SAugustin Cavalier 			callout_reset(&dfs->nol_timer, NOL_TIMEOUT,
343753c7e08SAugustin Cavalier 			    dfs_timeout, ic);
344753c7e08SAugustin Cavalier 	}
345753c7e08SAugustin Cavalier 
346753c7e08SAugustin Cavalier 	/*
347753c7e08SAugustin Cavalier 	 * If radar is detected on the bss channel while
348753c7e08SAugustin Cavalier 	 * doing CAC; force a state change by scheduling the
349753c7e08SAugustin Cavalier 	 * callout to be dispatched asap.  Otherwise, if this
350753c7e08SAugustin Cavalier 	 * event is for the bss channel then we must quiet
351753c7e08SAugustin Cavalier 	 * traffic and schedule a channel switch.
352753c7e08SAugustin Cavalier 	 *
353753c7e08SAugustin Cavalier 	 * Note this allows us to receive notification about
354753c7e08SAugustin Cavalier 	 * channels other than the bss channel; not sure
355753c7e08SAugustin Cavalier 	 * that can/will happen but it's simple to support.
356753c7e08SAugustin Cavalier 	 */
357753c7e08SAugustin Cavalier 	if (chan == ic->ic_bsschan) {
358753c7e08SAugustin Cavalier 		/* XXX need a way to defer to user app */
359753c7e08SAugustin Cavalier 
360753c7e08SAugustin Cavalier 		/*
361753c7e08SAugustin Cavalier 		 * Don't flip over to a new channel if
362753c7e08SAugustin Cavalier 		 * we are currently doing DFS debugging.
363753c7e08SAugustin Cavalier 		 */
364753c7e08SAugustin Cavalier 		if (ieee80211_dfs_debug == DFS_DBG_NONE)
365753c7e08SAugustin Cavalier 			dfs->newchan = ieee80211_dfs_pickchannel(ic);
366753c7e08SAugustin Cavalier 		else
367753c7e08SAugustin Cavalier 			dfs->newchan = chan;
368753c7e08SAugustin Cavalier 
369753c7e08SAugustin Cavalier 		announce_radar(ic, chan, dfs->newchan);
370753c7e08SAugustin Cavalier 
371753c7e08SAugustin Cavalier 		if (callout_pending(&dfs->cac_timer))
372753c7e08SAugustin Cavalier 			callout_schedule(&dfs->cac_timer, 0);
373753c7e08SAugustin Cavalier 		else if (dfs->newchan != NULL) {
374753c7e08SAugustin Cavalier 			/* XXX mode 1, switch count 2 */
375753c7e08SAugustin Cavalier 			/* XXX calculate switch count based on max
376753c7e08SAugustin Cavalier 			  switch time and beacon interval? */
377753c7e08SAugustin Cavalier 			ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
378753c7e08SAugustin Cavalier 		} else {
379753c7e08SAugustin Cavalier 			/*
380753c7e08SAugustin Cavalier 			 * Spec says to stop all transmissions and
381753c7e08SAugustin Cavalier 			 * wait on the current channel for an entry
382753c7e08SAugustin Cavalier 			 * on the NOL to expire.
383753c7e08SAugustin Cavalier 			 */
384753c7e08SAugustin Cavalier 			/*XXX*/
385753c7e08SAugustin Cavalier 			ic_printf(ic, "%s: No free channels; waiting for entry "
386753c7e08SAugustin Cavalier 			    "on NOL to expire\n", __func__);
387753c7e08SAugustin Cavalier 		}
388753c7e08SAugustin Cavalier 	} else {
389753c7e08SAugustin Cavalier 		/*
390753c7e08SAugustin Cavalier 		 * Issue rate-limited console msgs.
391753c7e08SAugustin Cavalier 		 */
392753c7e08SAugustin Cavalier 		if (dfs->lastchan != chan) {
393753c7e08SAugustin Cavalier 			dfs->lastchan = chan;
394753c7e08SAugustin Cavalier 			dfs->cureps = 0;
395753c7e08SAugustin Cavalier 			announce_radar(ic, chan, NULL);
396753c7e08SAugustin Cavalier 		} else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
397753c7e08SAugustin Cavalier 			announce_radar(ic, chan, NULL);
398753c7e08SAugustin Cavalier 		}
399753c7e08SAugustin Cavalier 	}
400753c7e08SAugustin Cavalier }
401753c7e08SAugustin Cavalier 
402753c7e08SAugustin Cavalier struct ieee80211_channel *
403753c7e08SAugustin Cavalier ieee80211_dfs_pickchannel(struct ieee80211com *ic)
404753c7e08SAugustin Cavalier {
405753c7e08SAugustin Cavalier 	struct ieee80211_channel *c;
406753c7e08SAugustin Cavalier 	int i, flags;
407753c7e08SAugustin Cavalier 	uint16_t v;
408753c7e08SAugustin Cavalier 
409753c7e08SAugustin Cavalier 	/*
410753c7e08SAugustin Cavalier 	 * Consult the scan cache first.
411753c7e08SAugustin Cavalier 	 */
412753c7e08SAugustin Cavalier 	flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
413753c7e08SAugustin Cavalier 	/*
414753c7e08SAugustin Cavalier 	 * XXX if curchan is HT this will never find a channel
415753c7e08SAugustin Cavalier 	 * XXX 'cuz we scan only legacy channels
416753c7e08SAugustin Cavalier 	 */
417753c7e08SAugustin Cavalier 	c = ieee80211_scan_pickchannel(ic, flags);
418753c7e08SAugustin Cavalier 	if (c != NULL)
419753c7e08SAugustin Cavalier 		return c;
420753c7e08SAugustin Cavalier 	/*
421753c7e08SAugustin Cavalier 	 * No channel found in scan cache; select a compatible
422753c7e08SAugustin Cavalier 	 * one at random (skipping channels where radar has
423753c7e08SAugustin Cavalier 	 * been detected).
424753c7e08SAugustin Cavalier 	 */
425753c7e08SAugustin Cavalier 	get_random_bytes(&v, sizeof(v));
426753c7e08SAugustin Cavalier 	v %= ic->ic_nchans;
427753c7e08SAugustin Cavalier 	for (i = v; i < ic->ic_nchans; i++) {
428753c7e08SAugustin Cavalier 		c = &ic->ic_channels[i];
429753c7e08SAugustin Cavalier 		if (!IEEE80211_IS_CHAN_RADAR(c) &&
430753c7e08SAugustin Cavalier 		   (c->ic_flags & flags) == flags)
431753c7e08SAugustin Cavalier 			return c;
432753c7e08SAugustin Cavalier 	}
433753c7e08SAugustin Cavalier 	for (i = 0; i < v; i++) {
434753c7e08SAugustin Cavalier 		c = &ic->ic_channels[i];
435753c7e08SAugustin Cavalier 		if (!IEEE80211_IS_CHAN_RADAR(c) &&
436753c7e08SAugustin Cavalier 		   (c->ic_flags & flags) == flags)
437753c7e08SAugustin Cavalier 			return c;
438753c7e08SAugustin Cavalier 	}
439753c7e08SAugustin Cavalier 	ic_printf(ic, "HELP, no channel located to switch to!\n");
440753c7e08SAugustin Cavalier 	return NULL;
441753c7e08SAugustin Cavalier }
442