2 * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
32 * IEEE 802.11 DFS/Radar support.
37 #include <sys/param.h>
38 #include <sys/systm.h>
40 #include <sys/malloc.h>
41 #include <sys/kernel.h>
43 #include <sys/socket.h>
44 #include <sys/sockio.h>
45 #include <sys/endian.h>
46 #include <sys/errno.h>
48 #include <sys/sysctl.h>
51 #include <net/if_media.h>
53 #include <net80211/ieee80211_var.h>
55 static MALLOC_DEFINE(M_80211_DFS, "80211dfs", "802.11 DFS state");
57 static int ieee80211_nol_timeout = 30*60; /* 30 minutes */
58 SYSCTL_INT(_net_wlan, OID_AUTO, nol_timeout, CTLFLAG_RW,
59 &ieee80211_nol_timeout, 0, "NOL timeout (secs)");
60 #define NOL_TIMEOUT msecs_to_ticks(ieee80211_nol_timeout*1000)
62 static int ieee80211_cac_timeout = 60; /* 60 seconds */
63 SYSCTL_INT(_net_wlan, OID_AUTO, cac_timeout, CTLFLAG_RW,
64 &ieee80211_cac_timeout, 0, "CAC timeout (secs)");
65 #define CAC_TIMEOUT msecs_to_ticks(ieee80211_cac_timeout*1000)
68 null_set_quiet(struct ieee80211_node *ni, u_int8_t *quiet_elm)
74 ieee80211_dfs_attach(struct ieee80211com *ic)
76 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
78 callout_init_mtx(&dfs->nol_timer, IEEE80211_LOCK_OBJ(ic), 0);
79 callout_init_mtx(&dfs->cac_timer, IEEE80211_LOCK_OBJ(ic), 0);
81 ic->ic_set_quiet = null_set_quiet;
85 ieee80211_dfs_detach(struct ieee80211com *ic)
87 /* NB: we assume no locking is needed */
88 ieee80211_dfs_reset(ic);
92 ieee80211_dfs_reset(struct ieee80211com *ic)
94 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
97 /* NB: we assume no locking is needed */
98 /* NB: cac_timer should be cleared by the state machine */
99 callout_drain(&dfs->nol_timer);
100 for (i = 0; i < ic->ic_nchans; i++)
101 ic->ic_channels[i].ic_state = 0;
102 dfs->lastchan = NULL;
106 cac_timeout(void *arg)
108 struct ieee80211vap *vap = arg;
109 struct ieee80211com *ic = vap->iv_ic;
110 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
113 IEEE80211_LOCK_ASSERT(ic);
115 if (vap->iv_state != IEEE80211_S_CAC) /* NB: just in case */
118 * When radar is detected during a CAC we are woken
119 * up prematurely to switch to a new channel.
120 * Check the channel to decide how to act.
122 if (IEEE80211_IS_CHAN_RADAR(ic->ic_curchan)) {
123 ieee80211_notify_cac(ic, ic->ic_curchan,
124 IEEE80211_NOTIFY_CAC_RADAR);
126 if_printf(vap->iv_ifp,
127 "CAC timer on channel %u (%u MHz) stopped due to radar\n",
128 ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
130 /* XXX clobbers any existing desired channel */
131 /* NB: dfs->newchan may be NULL, that's ok */
132 vap->iv_des_chan = dfs->newchan;
133 /* XXX recursive lock need ieee80211_new_state_locked */
134 ieee80211_new_state(vap, IEEE80211_S_SCAN, 0);
136 if_printf(vap->iv_ifp,
137 "CAC timer on channel %u (%u MHz) expired; "
138 "no radar detected\n",
139 ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
141 * Mark all channels with the current frequency
142 * as having completed CAC; this keeps us from
143 * doing it again until we change channels.
145 for (i = 0; i < ic->ic_nchans; i++) {
146 struct ieee80211_channel *c = &ic->ic_channels[i];
147 if (c->ic_freq == ic->ic_curchan->ic_freq)
148 c->ic_state |= IEEE80211_CHANSTATE_CACDONE;
150 ieee80211_notify_cac(ic, ic->ic_curchan,
151 IEEE80211_NOTIFY_CAC_EXPIRE);
152 ieee80211_cac_completeswitch(vap);
157 * Initiate the CAC timer. The driver is responsible
158 * for setting up the hardware to scan for radar on the
159 * channnel, we just handle timing things out.
162 ieee80211_dfs_cac_start(struct ieee80211vap *vap)
164 struct ieee80211com *ic = vap->iv_ic;
165 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
167 IEEE80211_LOCK_ASSERT(ic);
169 callout_reset(&dfs->cac_timer, CAC_TIMEOUT, cac_timeout, vap);
170 if_printf(vap->iv_ifp, "start %d second CAC timer on channel %u (%u MHz)\n",
171 ticks_to_secs(CAC_TIMEOUT),
172 ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
173 ieee80211_notify_cac(ic, ic->ic_curchan, IEEE80211_NOTIFY_CAC_START);
177 * Clear the CAC timer.
180 ieee80211_dfs_cac_stop(struct ieee80211vap *vap)
182 struct ieee80211com *ic = vap->iv_ic;
183 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
185 IEEE80211_LOCK_ASSERT(ic);
187 /* NB: racey but not important */
188 if (callout_pending(&dfs->cac_timer)) {
189 if_printf(vap->iv_ifp, "stop CAC timer on channel %u (%u MHz)\n",
190 ic->ic_curchan->ic_ieee, ic->ic_curchan->ic_freq);
191 ieee80211_notify_cac(ic, ic->ic_curchan,
192 IEEE80211_NOTIFY_CAC_STOP);
194 callout_stop(&dfs->cac_timer);
198 ieee80211_dfs_cac_clear(struct ieee80211com *ic,
199 const struct ieee80211_channel *chan)
203 for (i = 0; i < ic->ic_nchans; i++) {
204 struct ieee80211_channel *c = &ic->ic_channels[i];
205 if (c->ic_freq == chan->ic_freq)
206 c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
211 dfs_timeout(void *arg)
213 struct ieee80211com *ic = arg;
214 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
215 struct ieee80211_channel *c;
218 IEEE80211_LOCK_ASSERT(ic);
220 now = oldest = ticks;
221 for (i = 0; i < ic->ic_nchans; i++) {
222 c = &ic->ic_channels[i];
223 if (IEEE80211_IS_CHAN_RADAR(c)) {
224 if (time_after_eq(now, dfs->nol_event[i]+NOL_TIMEOUT)) {
225 c->ic_state &= ~IEEE80211_CHANSTATE_RADAR;
226 if (c->ic_state & IEEE80211_CHANSTATE_NORADAR) {
228 * NB: do this here so we get only one
229 * msg instead of one for every channel
232 if_printf(ic->ic_ifp, "radar on channel"
233 " %u (%u MHz) cleared after timeout\n",
234 c->ic_ieee, c->ic_freq);
235 /* notify user space */
237 ~IEEE80211_CHANSTATE_NORADAR;
238 ieee80211_notify_radar(ic, c);
240 } else if (dfs->nol_event[i] < oldest)
241 oldest = dfs->nol_event[i];
245 /* arrange to process next channel up for a status change */
246 callout_schedule(&dfs->nol_timer, oldest + NOL_TIMEOUT - now);
251 announce_radar(struct ifnet *ifp, const struct ieee80211_channel *curchan,
252 const struct ieee80211_channel *newchan)
255 if_printf(ifp, "radar detected on channel %u (%u MHz)\n",
256 curchan->ic_ieee, curchan->ic_freq);
258 if_printf(ifp, "radar detected on channel %u (%u MHz), "
259 "moving to channel %u (%u MHz)\n",
260 curchan->ic_ieee, curchan->ic_freq,
261 newchan->ic_ieee, newchan->ic_freq);
265 * Handle a radar detection event on a channel. The channel is
266 * added to the NOL list and we record the time of the event.
267 * Entries are aged out after NOL_TIMEOUT. If radar was
268 * detected while doing CAC we force a state/channel change.
269 * Otherwise radar triggers a channel switch using the CSA
270 * mechanism (when the channel is the bss channel).
273 ieee80211_dfs_notify_radar(struct ieee80211com *ic, struct ieee80211_channel *chan)
275 struct ieee80211_dfs_state *dfs = &ic->ic_dfs;
278 IEEE80211_LOCK_ASSERT(ic);
281 * Mark all entries with this frequency. Notify user
282 * space and arrange for notification when the radar
283 * indication is cleared. Then kick the NOL processing
284 * thread if not already running.
287 for (i = 0; i < ic->ic_nchans; i++) {
288 struct ieee80211_channel *c = &ic->ic_channels[i];
289 if (c->ic_freq == chan->ic_freq) {
290 c->ic_state &= ~IEEE80211_CHANSTATE_CACDONE;
291 c->ic_state |= IEEE80211_CHANSTATE_RADAR;
292 dfs->nol_event[i] = now;
295 ieee80211_notify_radar(ic, chan);
296 chan->ic_state |= IEEE80211_CHANSTATE_NORADAR;
297 if (!callout_pending(&dfs->nol_timer))
298 callout_reset(&dfs->nol_timer, NOL_TIMEOUT, dfs_timeout, ic);
301 * If radar is detected on the bss channel while
302 * doing CAC; force a state change by scheduling the
303 * callout to be dispatched asap. Otherwise, if this
304 * event is for the bss channel then we must quiet
305 * traffic and schedule a channel switch.
307 * Note this allows us to receive notification about
308 * channels other than the bss channel; not sure
309 * that can/will happen but it's simple to support.
311 if (chan == ic->ic_bsschan) {
312 /* XXX need a way to defer to user app */
313 dfs->newchan = ieee80211_dfs_pickchannel(ic);
315 announce_radar(ic->ic_ifp, chan, dfs->newchan);
317 if (callout_pending(&dfs->cac_timer))
318 callout_schedule(&dfs->cac_timer, 0);
319 else if (dfs->newchan != NULL) {
320 /* XXX mode 1, switch count 2 */
321 /* XXX calculate switch count based on max
322 switch time and beacon interval? */
323 ieee80211_csa_startswitch(ic, dfs->newchan, 1, 2);
326 * Spec says to stop all transmissions and
327 * wait on the current channel for an entry
328 * on the NOL to expire.
331 if_printf(ic->ic_ifp, "%s: No free channels; waiting for entry "
332 "on NOL to expire\n", __func__);
336 * Issue rate-limited console msgs.
338 if (dfs->lastchan != chan) {
339 dfs->lastchan = chan;
341 announce_radar(ic->ic_ifp, chan, NULL);
342 } else if (ppsratecheck(&dfs->lastevent, &dfs->cureps, 1)) {
343 announce_radar(ic->ic_ifp, chan, NULL);
348 struct ieee80211_channel *
349 ieee80211_dfs_pickchannel(struct ieee80211com *ic)
351 struct ieee80211_channel *c;
356 * Consult the scan cache first.
358 flags = ic->ic_curchan->ic_flags & IEEE80211_CHAN_ALL;
360 * XXX if curchan is HT this will never find a channel
361 * XXX 'cuz we scan only legacy channels
363 c = ieee80211_scan_pickchannel(ic, flags);
367 * No channel found in scan cache; select a compatible
368 * one at random (skipping channels where radar has
371 get_random_bytes(&v, sizeof(v));
373 for (i = v; i < ic->ic_nchans; i++) {
374 c = &ic->ic_channels[i];
375 if (!IEEE80211_IS_CHAN_RADAR(c) &&
376 (c->ic_flags & flags) == flags)
379 for (i = 0; i < v; i++) {
380 c = &ic->ic_channels[i];
381 if (!IEEE80211_IS_CHAN_RADAR(c) &&
382 (c->ic_flags & flags) == flags)
385 if_printf(ic->ic_ifp, "HELP, no channel located to switch to!\n");