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