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