1170530Ssam/*- 2178354Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 3170530Ssam * All rights reserved. 4170530Ssam * 5170530Ssam * Redistribution and use in source and binary forms, with or without 6170530Ssam * modification, are permitted provided that the following conditions 7170530Ssam * are met: 8170530Ssam * 1. Redistributions of source code must retain the above copyright 9170530Ssam * notice, this list of conditions and the following disclaimer. 10170530Ssam * 2. Redistributions in binary form must reproduce the above copyright 11170530Ssam * notice, this list of conditions and the following disclaimer in the 12170530Ssam * documentation and/or other materials provided with the distribution. 13170530Ssam * 14170530Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15170530Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16170530Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17170530Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18170530Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19170530Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20170530Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21170530Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22170530Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23170530Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24170530Ssam */ 25170530Ssam 26170530Ssam#include <sys/cdefs.h> 27170530Ssam__FBSDID("$FreeBSD: stable/11/sys/net80211/ieee80211_scan_sw.c 330460 2018-03-05 08:22:24Z eadler $"); 28170530Ssam 29170530Ssam/* 30170530Ssam * IEEE 802.11 scanning support. 31170530Ssam */ 32178354Ssam#include "opt_wlan.h" 33178354Ssam 34170530Ssam#include <sys/param.h> 35170530Ssam#include <sys/systm.h> 36191746Sthompsa#include <sys/proc.h> 37170530Ssam#include <sys/kernel.h> 38295126Sglebius#include <sys/malloc.h> 39191746Sthompsa#include <sys/condvar.h> 40170530Ssam 41170530Ssam#include <sys/socket.h> 42170530Ssam 43170530Ssam#include <net/if.h> 44257176Sglebius#include <net/if_var.h> 45170530Ssam#include <net/if_media.h> 46170530Ssam#include <net/ethernet.h> 47170530Ssam 48170530Ssam#include <net80211/ieee80211_var.h> 49170530Ssam 50276730Sadrian#include <net80211/ieee80211_scan_sw.h> 51276730Sadrian 52170530Ssam#include <net/bpf.h> 53170530Ssam 54170530Ssamstruct scan_state { 55170530Ssam struct ieee80211_scan_state base; /* public state */ 56170530Ssam 57296235Savos u_int ss_iflags; /* flags used internally */ 58296235Savos#define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ 59296235Savos#define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ 60296235Savos#define ISCAN_CANCEL 0x0004 /* cancel current scan */ 61296235Savos#define ISCAN_ABORT 0x0008 /* end the scan immediately */ 62296235Savos#define ISCAN_RUNNING 0x0010 /* scan was started */ 63296235Savos 64296235Savos unsigned long ss_chanmindwell; /* min dwell on curchan */ 65296235Savos unsigned long ss_scanend; /* time scan must stop */ 66296235Savos u_int ss_duration; /* duration for next scan */ 67296235Savos struct task ss_scan_start; /* scan start */ 68296235Savos struct timeout_task ss_scan_curchan; /* scan execution */ 69170530Ssam}; 70170530Ssam#define SCAN_PRIVATE(ss) ((struct scan_state *) ss) 71170530Ssam 72170530Ssam/* 73170530Ssam * Amount of time to go off-channel during a background 74170530Ssam * scan. This value should be large enough to catch most 75170530Ssam * ap's but short enough that we can return on-channel 76170530Ssam * before our listen interval expires. 77170530Ssam * 78170530Ssam * XXX tunable 79170530Ssam * XXX check against configured listen interval 80170530Ssam */ 81170530Ssam#define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) 82170530Ssam 83178354Ssamstatic void scan_curchan(struct ieee80211_scan_state *, unsigned long); 84178354Ssamstatic void scan_mindwell(struct ieee80211_scan_state *); 85296234Savosstatic void scan_signal(struct ieee80211_scan_state *, int); 86296234Savosstatic void scan_signal_locked(struct ieee80211_scan_state *, int); 87296232Savosstatic void scan_start(void *, int); 88296232Savosstatic void scan_curchan_task(void *, int); 89296231Savosstatic void scan_end(struct ieee80211_scan_state *, int); 90296230Savosstatic void scan_done(struct ieee80211_scan_state *, int); 91170530Ssam 92170530SsamMALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); 93170530Ssam 94284143Sadrianstatic void 95276730Sadrianieee80211_swscan_detach(struct ieee80211com *ic) 96170530Ssam{ 97170530Ssam struct ieee80211_scan_state *ss = ic->ic_scan; 98170530Ssam 99170530Ssam if (ss != NULL) { 100296234Savos scan_signal(ss, ISCAN_ABORT); 101296232Savos ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); 102296232Savos taskqueue_drain_timeout(ic->ic_tq, 103296232Savos &SCAN_PRIVATE(ss)->ss_scan_curchan); 104191746Sthompsa KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, 105191746Sthompsa ("scan still running")); 106276730Sadrian 107276730Sadrian /* 108276730Sadrian * For now, do the ss_ops detach here rather 109276730Sadrian * than ieee80211_scan_detach(). 110276730Sadrian * 111276730Sadrian * I'll figure out how to cleanly split things up 112276730Sadrian * at a later date. 113276730Sadrian */ 114170530Ssam if (ss->ss_ops != NULL) { 115170530Ssam ss->ss_ops->scan_detach(ss); 116170530Ssam ss->ss_ops = NULL; 117170530Ssam } 118170530Ssam ic->ic_scan = NULL; 119283538Sadrian IEEE80211_FREE(SCAN_PRIVATE(ss), M_80211_SCAN); 120170530Ssam } 121170530Ssam} 122170530Ssam 123284143Sadrianstatic void 124276730Sadrianieee80211_swscan_vattach(struct ieee80211vap *vap) 125178354Ssam{ 126276730Sadrian /* nothing to do for now */ 127276730Sadrian /* 128276730Sadrian * TODO: all of the vap scan calls should be methods! 129276730Sadrian */ 130178354Ssam 131178354Ssam} 132178354Ssam 133284143Sadrianstatic void 134276730Sadrianieee80211_swscan_vdetach(struct ieee80211vap *vap) 135178354Ssam{ 136178354Ssam struct ieee80211com *ic = vap->iv_ic; 137296235Savos struct ieee80211_scan_state *ss = ic->ic_scan; 138178354Ssam 139276730Sadrian IEEE80211_LOCK_ASSERT(ic); 140296235Savos 141296235Savos if (ss != NULL && ss->ss_vap == vap && 142296235Savos (ic->ic_flags & IEEE80211_F_SCAN)) 143296235Savos scan_signal_locked(ss, ISCAN_ABORT); 144178354Ssam} 145178354Ssam 146284143Sadrianstatic void 147276730Sadrianieee80211_swscan_set_scan_duration(struct ieee80211vap *vap, u_int duration) 148170530Ssam{ 149276730Sadrian struct ieee80211com *ic = vap->iv_ic; 150276730Sadrian struct ieee80211_scan_state *ss = ic->ic_scan; 151170530Ssam 152276730Sadrian IEEE80211_LOCK_ASSERT(ic); 153170530Ssam 154276730Sadrian /* NB: flush frames rx'd before 1st channel change */ 155276730Sadrian SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 156276730Sadrian SCAN_PRIVATE(ss)->ss_duration = duration; 157170530Ssam} 158170530Ssam 159178354Ssam/* 160178354Ssam * Start a scan unless one is already going. 161282705Sadrian */ 162282705Sadrianstatic int 163282705Sadrianieee80211_swscan_start_scan_locked(const struct ieee80211_scanner *scan, 164282705Sadrian struct ieee80211vap *vap, int flags, u_int duration, 165282705Sadrian u_int mindwell, u_int maxdwell, 166282705Sadrian u_int nssid, const struct ieee80211_scan_ssid ssids[]) 167282705Sadrian{ 168282705Sadrian struct ieee80211com *ic = vap->iv_ic; 169282705Sadrian struct ieee80211_scan_state *ss = ic->ic_scan; 170282705Sadrian 171282705Sadrian IEEE80211_LOCK_ASSERT(ic); 172282705Sadrian 173282705Sadrian if (ic->ic_flags & IEEE80211_F_CSAPENDING) { 174282705Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 175282705Sadrian "%s: scan inhibited by pending channel change\n", __func__); 176282705Sadrian } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 177282705Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 178282705Sadrian "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" 179282705Sadrian , __func__ 180282705Sadrian , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" 181282705Sadrian , duration, mindwell, maxdwell 182282705Sadrian , ieee80211_phymode_name[vap->iv_des_mode] 183282705Sadrian , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" 184282705Sadrian , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" 185282705Sadrian , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" 186282705Sadrian , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" 187282705Sadrian , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" 188282705Sadrian , flags & IEEE80211_SCAN_ONCE ? ", once" : "" 189282705Sadrian ); 190282705Sadrian 191282705Sadrian ieee80211_scan_update_locked(vap, scan); 192282705Sadrian if (ss->ss_ops != NULL) { 193282705Sadrian if ((flags & IEEE80211_SCAN_NOSSID) == 0) 194282705Sadrian ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); 195282705Sadrian 196282705Sadrian /* NB: top 4 bits for internal use */ 197282705Sadrian ss->ss_flags = flags & 0xfff; 198282705Sadrian if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 199282705Sadrian vap->iv_stats.is_scan_active++; 200282705Sadrian else 201282705Sadrian vap->iv_stats.is_scan_passive++; 202282705Sadrian if (flags & IEEE80211_SCAN_FLUSH) 203282705Sadrian ss->ss_ops->scan_flush(ss); 204282705Sadrian if (flags & IEEE80211_SCAN_BGSCAN) 205282705Sadrian ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; 206282705Sadrian 207282705Sadrian /* Set duration for this particular scan */ 208282705Sadrian ieee80211_swscan_set_scan_duration(vap, duration); 209282705Sadrian 210282705Sadrian ss->ss_next = 0; 211282705Sadrian ss->ss_mindwell = mindwell; 212282705Sadrian ss->ss_maxdwell = maxdwell; 213282705Sadrian /* NB: scan_start must be before the scan runtask */ 214282705Sadrian ss->ss_ops->scan_start(ss, vap); 215282705Sadrian#ifdef IEEE80211_DEBUG 216282705Sadrian if (ieee80211_msg_scan(vap)) 217282705Sadrian ieee80211_scan_dump(ss); 218282705Sadrian#endif /* IEEE80211_DEBUG */ 219282705Sadrian ic->ic_flags |= IEEE80211_F_SCAN; 220282705Sadrian 221282705Sadrian /* Start scan task */ 222296232Savos ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_start); 223282705Sadrian } 224282705Sadrian return 1; 225282705Sadrian } else { 226282705Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 227282705Sadrian "%s: %s scan already in progress\n", __func__, 228282705Sadrian ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); 229282705Sadrian } 230282705Sadrian return 0; 231282705Sadrian} 232282705Sadrian 233282705Sadrian 234282705Sadrian/* 235282705Sadrian * Start a scan unless one is already going. 236276730Sadrian * 237276730Sadrian * Called without the comlock held; grab the comlock as appropriate. 238178354Ssam */ 239284143Sadrianstatic int 240276730Sadrianieee80211_swscan_start_scan(const struct ieee80211_scanner *scan, 241276730Sadrian struct ieee80211vap *vap, int flags, 242276730Sadrian u_int duration, u_int mindwell, u_int maxdwell, 243276730Sadrian u_int nssid, const struct ieee80211_scan_ssid ssids[]) 244178354Ssam{ 245178354Ssam struct ieee80211com *ic = vap->iv_ic; 246178354Ssam int result; 247178354Ssam 248276730Sadrian IEEE80211_UNLOCK_ASSERT(ic); 249178354Ssam 250178354Ssam IEEE80211_LOCK(ic); 251282705Sadrian result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration, 252178354Ssam mindwell, maxdwell, nssid, ssids); 253170530Ssam IEEE80211_UNLOCK(ic); 254170530Ssam 255178354Ssam return result; 256170530Ssam} 257170530Ssam 258170530Ssam/* 259170530Ssam * Check the scan cache for an ap/channel to use; if that 260170530Ssam * fails then kick off a new scan. 261276730Sadrian * 262276730Sadrian * Called with the comlock held. 263276730Sadrian * 264276730Sadrian * XXX TODO: split out! 265170530Ssam */ 266284143Sadrianstatic int 267276730Sadrianieee80211_swscan_check_scan(const struct ieee80211_scanner *scan, 268276730Sadrian struct ieee80211vap *vap, int flags, 269276730Sadrian u_int duration, u_int mindwell, u_int maxdwell, 270276730Sadrian u_int nssid, const struct ieee80211_scan_ssid ssids[]) 271170530Ssam{ 272178354Ssam struct ieee80211com *ic = vap->iv_ic; 273170530Ssam struct ieee80211_scan_state *ss = ic->ic_scan; 274179389Ssam int result; 275170530Ssam 276276730Sadrian IEEE80211_LOCK_ASSERT(ic); 277178354Ssam 278170530Ssam if (ss->ss_ops != NULL) { 279178354Ssam /* XXX verify ss_ops matches vap->iv_opmode */ 280170530Ssam if ((flags & IEEE80211_SCAN_NOSSID) == 0) { 281170530Ssam /* 282170530Ssam * Update the ssid list and mark flags so if 283170530Ssam * we call start_scan it doesn't duplicate work. 284170530Ssam */ 285276730Sadrian ieee80211_scan_copy_ssid(vap, ss, nssid, ssids); 286170530Ssam flags |= IEEE80211_SCAN_NOSSID; 287170530Ssam } 288170530Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && 289179389Ssam (flags & IEEE80211_SCAN_FLUSH) == 0 && 290297405Sadrian ieee80211_time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { 291170530Ssam /* 292170530Ssam * We're not currently scanning and the cache is 293170530Ssam * deemed hot enough to consult. Lock out others 294170530Ssam * by marking IEEE80211_F_SCAN while we decide if 295170530Ssam * something is already in the scan cache we can 296170530Ssam * use. Also discard any frames that might come 297170530Ssam * in while temporarily marked as scanning. 298170530Ssam */ 299170530Ssam SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 300170530Ssam ic->ic_flags |= IEEE80211_F_SCAN; 301179389Ssam 302179389Ssam /* NB: need to use supplied flags in check */ 303178354Ssam ss->ss_flags = flags & 0xff; 304179389Ssam result = ss->ss_ops->scan_end(ss, vap); 305179389Ssam 306170530Ssam ic->ic_flags &= ~IEEE80211_F_SCAN; 307179389Ssam SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; 308179389Ssam if (result) { 309179389Ssam ieee80211_notify_scan_done(vap); 310179389Ssam return 1; 311179389Ssam } 312170530Ssam } 313170530Ssam } 314282705Sadrian result = ieee80211_swscan_start_scan_locked(scan, vap, flags, duration, 315178354Ssam mindwell, maxdwell, nssid, ssids); 316178354Ssam 317178354Ssam return result; 318170530Ssam} 319170530Ssam 320170530Ssam/* 321170530Ssam * Restart a previous scan. If the previous scan completed 322170530Ssam * then we start again using the existing channel list. 323170530Ssam */ 324284143Sadrianstatic int 325276730Sadrianieee80211_swscan_bg_scan(const struct ieee80211_scanner *scan, 326276730Sadrian struct ieee80211vap *vap, int flags) 327170530Ssam{ 328178354Ssam struct ieee80211com *ic = vap->iv_ic; 329170530Ssam struct ieee80211_scan_state *ss = ic->ic_scan; 330170530Ssam 331276730Sadrian /* XXX assert unlocked? */ 332276730Sadrian // IEEE80211_UNLOCK_ASSERT(ic); 333178354Ssam 334170530Ssam IEEE80211_LOCK(ic); 335170530Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 336170530Ssam u_int duration; 337170530Ssam /* 338170530Ssam * Go off-channel for a fixed interval that is large 339170530Ssam * enough to catch most ap's but short enough that 340170530Ssam * we can return on-channel before our listen interval 341170530Ssam * expires. 342170530Ssam */ 343170530Ssam duration = IEEE80211_SCAN_OFFCHANNEL; 344170530Ssam 345178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 346293164Sadrian "%s: %s scan, ticks %u duration %u\n", __func__, 347170530Ssam ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", 348170530Ssam ticks, duration); 349170530Ssam 350276730Sadrian ieee80211_scan_update_locked(vap, scan); 351170530Ssam if (ss->ss_ops != NULL) { 352178354Ssam ss->ss_vap = vap; 353170530Ssam /* 354170530Ssam * A background scan does not select a new sta; it 355170530Ssam * just refreshes the scan cache. Also, indicate 356170530Ssam * the scan logic should follow the beacon schedule: 357170530Ssam * we go off-channel and scan for a while, then 358170530Ssam * return to the bss channel to receive a beacon, 359170530Ssam * then go off-channel again. All during this time 360170530Ssam * we notify the ap we're in power save mode. When 361170530Ssam * the scan is complete we leave power save mode. 362170530Ssam * If any beacon indicates there are frames pending 363170530Ssam * for us then we drop out of power save mode 364170530Ssam * (and background scan) automatically by way of the 365170530Ssam * usual sta power save logic. 366170530Ssam */ 367170530Ssam ss->ss_flags |= IEEE80211_SCAN_NOPICK 368178354Ssam | IEEE80211_SCAN_BGSCAN 369178354Ssam | flags 370178354Ssam ; 371170530Ssam /* if previous scan completed, restart */ 372170530Ssam if (ss->ss_next >= ss->ss_last) { 373170530Ssam if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 374178354Ssam vap->iv_stats.is_scan_active++; 375170530Ssam else 376178354Ssam vap->iv_stats.is_scan_passive++; 377178354Ssam /* 378178354Ssam * NB: beware of the scan cache being flushed; 379178354Ssam * if the channel list is empty use the 380178354Ssam * scan_start method to populate it. 381178354Ssam */ 382178354Ssam ss->ss_next = 0; 383178354Ssam if (ss->ss_last != 0) 384178354Ssam ss->ss_ops->scan_restart(ss, vap); 385178354Ssam else { 386178354Ssam ss->ss_ops->scan_start(ss, vap); 387178354Ssam#ifdef IEEE80211_DEBUG 388178354Ssam if (ieee80211_msg_scan(vap)) 389276730Sadrian ieee80211_scan_dump(ss); 390178354Ssam#endif /* IEEE80211_DEBUG */ 391178354Ssam } 392170530Ssam } 393276730Sadrian ieee80211_swscan_set_scan_duration(vap, duration); 394170530Ssam ss->ss_maxdwell = duration; 395191746Sthompsa ic->ic_flags |= IEEE80211_F_SCAN; 396191746Sthompsa ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; 397296232Savos ieee80211_runtask(ic, 398296232Savos &SCAN_PRIVATE(ss)->ss_scan_start); 399170530Ssam } else { 400170530Ssam /* XXX msg+stat */ 401170530Ssam } 402170530Ssam } else { 403178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 404170530Ssam "%s: %s scan already in progress\n", __func__, 405170530Ssam ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); 406170530Ssam } 407170530Ssam IEEE80211_UNLOCK(ic); 408170530Ssam 409170530Ssam /* NB: racey, does it matter? */ 410170530Ssam return (ic->ic_flags & IEEE80211_F_SCAN); 411170530Ssam} 412170530Ssam 413330232Seadler/* 414330232Seadler * Taskqueue work to cancel a scan. 415330232Seadler * 416330232Seadler * Note: for offload scan devices, we may want to call into the 417330232Seadler * driver to try and cancel scanning, however it may not be cancelable. 418330232Seadler */ 419284143Sadrianstatic void 420296227Savoscancel_scan(struct ieee80211vap *vap, int any, const char *func) 421178354Ssam{ 422178354Ssam struct ieee80211com *ic = vap->iv_ic; 423178354Ssam struct ieee80211_scan_state *ss = ic->ic_scan; 424178354Ssam 425178354Ssam IEEE80211_LOCK(ic); 426178354Ssam if ((ic->ic_flags & IEEE80211_F_SCAN) && 427296227Savos (any || ss->ss_vap == vap) && 428178354Ssam (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { 429178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 430296227Savos "%s: cancel %s scan\n", func, 431178354Ssam ss->ss_flags & IEEE80211_SCAN_ACTIVE ? 432178354Ssam "active" : "passive"); 433178354Ssam 434296234Savos /* clear bg scan NOPICK */ 435178354Ssam ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; 436296234Savos /* mark cancel request and wake up the scan task */ 437296234Savos scan_signal_locked(ss, ISCAN_CANCEL); 438275973Sadrian } else { 439275973Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 440275973Sadrian "%s: called; F_SCAN=%d, vap=%s, CANCEL=%d\n", 441296227Savos func, 442275973Sadrian !! (ic->ic_flags & IEEE80211_F_SCAN), 443275973Sadrian (ss->ss_vap == vap ? "match" : "nomatch"), 444275973Sadrian !! (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL)); 445178354Ssam } 446178354Ssam IEEE80211_UNLOCK(ic); 447178354Ssam} 448178354Ssam 449178354Ssam/* 450296227Savos * Cancel any scan currently going on for the specified vap. 451296227Savos */ 452296227Savosstatic void 453296227Savosieee80211_swscan_cancel_scan(struct ieee80211vap *vap) 454296227Savos{ 455296227Savos cancel_scan(vap, 0, __func__); 456296227Savos} 457296227Savos 458296227Savos/* 459170530Ssam * Cancel any scan currently going on. 460170530Ssam */ 461284143Sadrianstatic void 462276730Sadrianieee80211_swscan_cancel_anyscan(struct ieee80211vap *vap) 463170530Ssam{ 464330460Seadler 465330460Seadler /* XXX for now - just don't do this per packet. */ 466330460Seadler if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) 467330460Seadler return; 468330460Seadler 469296227Savos cancel_scan(vap, 1, __func__); 470170530Ssam} 471170530Ssam 472170530Ssam/* 473298392Savos * Manually switch to the next channel in the channel list. 474298392Savos * Provided for drivers that manage scanning themselves 475298392Savos * (e.g. for firmware-based devices). 476170530Ssam */ 477284143Sadrianstatic void 478276730Sadrianieee80211_swscan_scan_next(struct ieee80211vap *vap) 479170530Ssam{ 480296234Savos struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 481178354Ssam 482275974Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: called\n", __func__); 483275974Sadrian 484191746Sthompsa /* wake up the scan task */ 485296234Savos scan_signal(ss, 0); 486170530Ssam} 487170530Ssam 488170530Ssam/* 489298392Savos * Manually stop a scan that is currently running. 490298392Savos * Provided for drivers that are not able to scan single channels 491298392Savos * (e.g. for firmware-based devices). 492171125Sthompsa */ 493284143Sadrianstatic void 494276730Sadrianieee80211_swscan_scan_done(struct ieee80211vap *vap) 495171125Sthompsa{ 496178354Ssam struct ieee80211com *ic = vap->iv_ic; 497296235Savos struct ieee80211_scan_state *ss = ic->ic_scan; 498171125Sthompsa 499276730Sadrian IEEE80211_LOCK_ASSERT(ic); 500275974Sadrian 501296234Savos scan_signal_locked(ss, 0); 502171125Sthompsa} 503171125Sthompsa 504171125Sthompsa/* 505298392Savos * Probe the current channel, if allowed, while scanning. 506178354Ssam * If the channel is not marked passive-only then send 507178354Ssam * a probe request immediately. Otherwise mark state and 508178354Ssam * listen for beacons on the channel; if we receive something 509178354Ssam * then we'll transmit a probe request. 510178354Ssam */ 511284143Sadrianstatic void 512276730Sadrianieee80211_swscan_probe_curchan(struct ieee80211vap *vap, int force) 513178354Ssam{ 514178354Ssam struct ieee80211com *ic = vap->iv_ic; 515178354Ssam struct ieee80211_scan_state *ss = ic->ic_scan; 516178354Ssam struct ifnet *ifp = vap->iv_ifp; 517178354Ssam int i; 518178354Ssam 519178354Ssam /* 520330232Seadler * Full-offload scan devices don't require this. 521330232Seadler */ 522330232Seadler if (vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) 523330232Seadler return; 524330232Seadler 525330232Seadler /* 526178354Ssam * Send directed probe requests followed by any 527178354Ssam * broadcast probe request. 528178354Ssam * XXX remove dependence on ic/vap->iv_bss 529178354Ssam */ 530178354Ssam for (i = 0; i < ss->ss_nssid; i++) 531178354Ssam ieee80211_send_probereq(vap->iv_bss, 532178354Ssam vap->iv_myaddr, ifp->if_broadcastaddr, 533178354Ssam ifp->if_broadcastaddr, 534178354Ssam ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); 535178354Ssam if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) 536178354Ssam ieee80211_send_probereq(vap->iv_bss, 537178354Ssam vap->iv_myaddr, ifp->if_broadcastaddr, 538178354Ssam ifp->if_broadcastaddr, 539178354Ssam "", 0); 540178354Ssam} 541178354Ssam 542178354Ssam/* 543170530Ssam * Scan curchan. If this is an active scan and the channel 544170530Ssam * is not marked passive then send probe request frame(s). 545170530Ssam * Arrange for the channel change after maxdwell ticks. 546170530Ssam */ 547170530Ssamstatic void 548178354Ssamscan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 549170530Ssam{ 550178354Ssam struct ieee80211vap *vap = ss->ss_vap; 551296235Savos struct ieee80211com *ic = ss->ss_ic; 552170530Ssam 553275974Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 554275974Sadrian "%s: calling; maxdwell=%lu\n", 555275974Sadrian __func__, 556275974Sadrian maxdwell); 557296235Savos IEEE80211_LOCK(ic); 558178354Ssam if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 559178354Ssam ieee80211_probe_curchan(vap, 0); 560296235Savos taskqueue_enqueue_timeout(ic->ic_tq, 561296232Savos &SCAN_PRIVATE(ss)->ss_scan_curchan, maxdwell); 562296235Savos IEEE80211_UNLOCK(ic); 563170530Ssam} 564170530Ssam 565191746Sthompsastatic void 566296234Savosscan_signal(struct ieee80211_scan_state *ss, int iflags) 567191746Sthompsa{ 568296234Savos struct ieee80211com *ic = ss->ss_ic; 569296234Savos 570296234Savos IEEE80211_UNLOCK_ASSERT(ic); 571296234Savos 572296234Savos IEEE80211_LOCK(ic); 573296234Savos scan_signal_locked(ss, iflags); 574296234Savos IEEE80211_UNLOCK(ic); 575296234Savos} 576296234Savos 577296234Savosstatic void 578296234Savosscan_signal_locked(struct ieee80211_scan_state *ss, int iflags) 579296234Savos{ 580296232Savos struct scan_state *ss_priv = SCAN_PRIVATE(ss); 581296232Savos struct timeout_task *scan_task = &ss_priv->ss_scan_curchan; 582296232Savos struct ieee80211com *ic = ss->ss_ic; 583191746Sthompsa 584296232Savos IEEE80211_LOCK_ASSERT(ic); 585296232Savos 586296234Savos ss_priv->ss_iflags |= iflags; 587296232Savos if (ss_priv->ss_iflags & ISCAN_RUNNING) { 588296232Savos if (taskqueue_cancel_timeout(ic->ic_tq, scan_task, NULL) == 0) 589296232Savos taskqueue_enqueue_timeout(ic->ic_tq, scan_task, 0); 590296232Savos } 591191746Sthompsa} 592191746Sthompsa 593170530Ssam/* 594170530Ssam * Handle mindwell requirements completed; initiate a channel 595170530Ssam * change to the next channel asap. 596170530Ssam */ 597170530Ssamstatic void 598178354Ssamscan_mindwell(struct ieee80211_scan_state *ss) 599170530Ssam{ 600191746Sthompsa 601296241Sglebius IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: called\n", 602296241Sglebius __func__); 603275974Sadrian 604296234Savos scan_signal(ss, 0); 605170530Ssam} 606170530Ssam 607170530Ssamstatic void 608296232Savosscan_start(void *arg, int pending) 609170530Ssam{ 610191746Sthompsa#define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) 611170530Ssam struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; 612296228Savos struct scan_state *ss_priv = SCAN_PRIVATE(ss); 613178354Ssam struct ieee80211vap *vap = ss->ss_vap; 614191746Sthompsa struct ieee80211com *ic = ss->ss_ic; 615170530Ssam 616191746Sthompsa IEEE80211_LOCK(ic); 617191746Sthompsa if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || 618296228Savos (ss_priv->ss_iflags & ISCAN_ABORT)) { 619191746Sthompsa /* Cancelled before we started */ 620296230Savos scan_done(ss, 0); 621296230Savos return; 622191746Sthompsa } 623178354Ssam 624191746Sthompsa if (ss->ss_next == ss->ss_last) { 625191746Sthompsa IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 626191746Sthompsa "%s: no channels to scan\n", __func__); 627296230Savos scan_done(ss, 1); 628296230Savos return; 629191746Sthompsa } 630191746Sthompsa 631330232Seadler /* 632330232Seadler * Put the station into power save mode. 633330232Seadler * 634330232Seadler * This is only required if we're not a full-offload devices; 635330232Seadler * those devices manage scan/traffic differently. 636330232Seadler */ 637330232Seadler if (((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) && 638330232Seadler vap->iv_opmode == IEEE80211_M_STA && 639191746Sthompsa vap->iv_state == IEEE80211_S_RUN) { 640191746Sthompsa if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { 641191746Sthompsa /* Enable station power save mode */ 642241138Sadrian vap->iv_sta_ps(vap, 1); 643296233Savos /* Wait until null data frame will be ACK'ed */ 644296232Savos mtx_sleep(vap, IEEE80211_LOCK_OBJ(ic), PCATCH, 645296233Savos "sta_ps", msecs_to_ticks(10)); 646296230Savos if (ss_priv->ss_iflags & ISCAN_ABORT) { 647296230Savos scan_done(ss, 0); 648296230Savos return; 649296230Savos } 650191746Sthompsa } 651191746Sthompsa } 652191746Sthompsa 653296229Savos ss_priv->ss_scanend = ticks + ss_priv->ss_duration; 654275964Sadrian 655275964Sadrian /* XXX scan state can change! Re-validate scan state! */ 656275964Sadrian 657191746Sthompsa IEEE80211_UNLOCK(ic); 658296232Savos 659191746Sthompsa ic->ic_scan_start(ic); /* notify driver */ 660191746Sthompsa 661296232Savos scan_curchan_task(ss, 0); 662296232Savos} 663275974Sadrian 664296232Savosstatic void 665296232Savosscan_curchan_task(void *arg, int pending) 666296232Savos{ 667296232Savos struct ieee80211_scan_state *ss = arg; 668296232Savos struct scan_state *ss_priv = SCAN_PRIVATE(ss); 669296232Savos struct ieee80211com *ic = ss->ss_ic; 670296232Savos struct ieee80211_channel *chan; 671296232Savos unsigned long maxdwell; 672296232Savos int scandone; 673275974Sadrian 674296232Savos IEEE80211_LOCK(ic); 675296232Savosend: 676296232Savos scandone = (ss->ss_next >= ss->ss_last) || 677296232Savos (ss_priv->ss_iflags & ISCAN_CANCEL) != 0; 678275974Sadrian 679296241Sglebius IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, 680296232Savos "%s: loop start; scandone=%d\n", 681296232Savos __func__, 682296232Savos scandone); 683191746Sthompsa 684296232Savos if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || 685296232Savos (ss_priv->ss_iflags & ISCAN_ABORT) || 686297405Sadrian ieee80211_time_after(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { 687296232Savos ss_priv->ss_iflags &= ~ISCAN_RUNNING; 688296232Savos scan_end(ss, scandone); 689296232Savos return; 690296232Savos } else 691296232Savos ss_priv->ss_iflags |= ISCAN_RUNNING; 692170530Ssam 693296232Savos chan = ss->ss_chans[ss->ss_next++]; 694170530Ssam 695296232Savos /* 696296232Savos * Watch for truncation due to the scan end time. 697296232Savos */ 698297405Sadrian if (ieee80211_time_after(ticks + ss->ss_maxdwell, ss_priv->ss_scanend)) 699296232Savos maxdwell = ss_priv->ss_scanend - ticks; 700296232Savos else 701296232Savos maxdwell = ss->ss_maxdwell; 702170530Ssam 703296241Sglebius IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, 704296232Savos "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", 705296232Savos __func__, 706296232Savos ieee80211_chan2ieee(ic, ic->ic_curchan), 707296232Savos ieee80211_channel_type_char(ic->ic_curchan), 708296232Savos ieee80211_chan2ieee(ic, chan), 709296232Savos ieee80211_channel_type_char(chan), 710296232Savos (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && 711296232Savos (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? 712296232Savos "active" : "passive", 713296232Savos ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); 714170530Ssam 715296232Savos /* 716296232Savos * Potentially change channel and phy mode. 717296232Savos */ 718296232Savos ic->ic_curchan = chan; 719296232Savos ic->ic_rt = ieee80211_get_ratetable(chan); 720296232Savos IEEE80211_UNLOCK(ic); 721296232Savos /* 722296232Savos * Perform the channel change and scan unlocked so the driver 723296232Savos * may sleep. Once set_channel returns the hardware has 724296232Savos * completed the channel change. 725296232Savos */ 726296232Savos ic->ic_set_channel(ic); 727296232Savos ieee80211_radiotap_chan_change(ic); 728170530Ssam 729296232Savos /* 730296232Savos * Scan curchan. Drivers for "intelligent hardware" 731296232Savos * override ic_scan_curchan to tell the device to do 732296232Savos * the work. Otherwise we manage the work ourselves; 733296232Savos * sending a probe request (as needed), and arming the 734296232Savos * timeout to switch channels after maxdwell ticks. 735296232Savos * 736296232Savos * scan_curchan should only pause for the time required to 737296232Savos * prepare/initiate the hardware for the scan (if at all). 738296232Savos */ 739296232Savos ic->ic_scan_curchan(ss, maxdwell); 740296232Savos IEEE80211_LOCK(ic); 741275964Sadrian 742296232Savos /* XXX scan state can change! Re-validate scan state! */ 743170530Ssam 744296232Savos ss_priv->ss_chanmindwell = ticks + ss->ss_mindwell; 745296232Savos /* clear mindwell lock and initial channel change flush */ 746296232Savos ss_priv->ss_iflags &= ~ISCAN_REP; 747170530Ssam 748298293Savos if (ss_priv->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT)) { 749298293Savos taskqueue_cancel_timeout(ic->ic_tq, &ss_priv->ss_scan_curchan, 750298293Savos NULL); 751296232Savos goto end; 752298293Savos } 753296232Savos 754296241Sglebius IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, "%s: waiting\n", 755296241Sglebius __func__); 756296232Savos IEEE80211_UNLOCK(ic); 757296231Savos} 758275974Sadrian 759296231Savosstatic void 760296231Savosscan_end(struct ieee80211_scan_state *ss, int scandone) 761296231Savos{ 762296231Savos struct scan_state *ss_priv = SCAN_PRIVATE(ss); 763296231Savos struct ieee80211vap *vap = ss->ss_vap; 764296231Savos struct ieee80211com *ic = ss->ss_ic; 765296231Savos 766296231Savos IEEE80211_LOCK_ASSERT(ic); 767296231Savos 768275974Sadrian IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s: out\n", __func__); 769275974Sadrian 770296230Savos if (ss_priv->ss_iflags & ISCAN_ABORT) { 771296230Savos scan_done(ss, scandone); 772296230Savos return; 773296230Savos } 774170530Ssam 775191746Sthompsa IEEE80211_UNLOCK(ic); 776191746Sthompsa ic->ic_scan_end(ic); /* notify driver */ 777191746Sthompsa IEEE80211_LOCK(ic); 778275964Sadrian /* XXX scan state can change! Re-validate scan state! */ 779170530Ssam 780191746Sthompsa /* 781298995Spfg * Since a cancellation may have occurred during one of the 782232373Sadrian * driver calls (whilst unlocked), update scandone. 783232373Sadrian */ 784296228Savos if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { 785232373Sadrian /* XXX printf? */ 786232373Sadrian if_printf(vap->iv_ifp, 787275964Sadrian "%s: OOPS! scan cancelled during driver call (1)!\n", 788232373Sadrian __func__); 789275964Sadrian scandone = 1; 790232373Sadrian } 791232373Sadrian 792232373Sadrian /* 793191746Sthompsa * Record scan complete time. Note that we also do 794191746Sthompsa * this when canceled so any background scan will 795191746Sthompsa * not be restarted for a while. 796191746Sthompsa */ 797191746Sthompsa if (scandone) 798191746Sthompsa ic->ic_lastscan = ticks; 799191746Sthompsa /* return to the bss channel */ 800191746Sthompsa if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 801191746Sthompsa ic->ic_curchan != ic->ic_bsschan) { 802191746Sthompsa ieee80211_setupcurchan(ic, ic->ic_bsschan); 803191746Sthompsa IEEE80211_UNLOCK(ic); 804191746Sthompsa ic->ic_set_channel(ic); 805192468Ssam ieee80211_radiotap_chan_change(ic); 806191746Sthompsa IEEE80211_LOCK(ic); 807191746Sthompsa } 808191746Sthompsa /* clear internal flags and any indication of a pick */ 809296228Savos ss_priv->ss_iflags &= ~ISCAN_REP; 810191746Sthompsa ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; 811191746Sthompsa 812191746Sthompsa /* 813191746Sthompsa * If not canceled and scan completed, do post-processing. 814191746Sthompsa * If the callback function returns 0, then it wants to 815191746Sthompsa * continue/restart scanning. Unfortunately we needed to 816191746Sthompsa * notify the driver to end the scan above to avoid having 817191746Sthompsa * rx frames alter the scan candidate list. 818191746Sthompsa */ 819296228Savos if ((ss_priv->ss_iflags & ISCAN_CANCEL) == 0 && 820191746Sthompsa !ss->ss_ops->scan_end(ss, vap) && 821191746Sthompsa (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && 822297405Sadrian ieee80211_time_before(ticks + ss->ss_mindwell, ss_priv->ss_scanend)) { 823191746Sthompsa IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 824191746Sthompsa "%s: done, restart " 825191746Sthompsa "[ticks %u, dwell min %lu scanend %lu]\n", 826191746Sthompsa __func__, 827296229Savos ticks, ss->ss_mindwell, ss_priv->ss_scanend); 828298995Spfg ss->ss_next = 0; /* reset to beginning */ 829191746Sthompsa if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 830191746Sthompsa vap->iv_stats.is_scan_active++; 831191746Sthompsa else 832191746Sthompsa vap->iv_stats.is_scan_passive++; 833191746Sthompsa 834191746Sthompsa ss->ss_ops->scan_restart(ss, vap); /* XXX? */ 835296232Savos ieee80211_runtask(ic, &ss_priv->ss_scan_start); 836191746Sthompsa IEEE80211_UNLOCK(ic); 837191746Sthompsa return; 838191746Sthompsa } 839191746Sthompsa 840191746Sthompsa /* past here, scandone is ``true'' if not in bg mode */ 841191746Sthompsa if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) 842191746Sthompsa scandone = 1; 843191746Sthompsa 844191746Sthompsa IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 845191746Sthompsa "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", 846191746Sthompsa __func__, scandone ? "done" : "stopped", 847296229Savos ticks, ss->ss_mindwell, ss_priv->ss_scanend); 848191746Sthompsa 849191746Sthompsa /* 850298995Spfg * Since a cancellation may have occurred during one of the 851275964Sadrian * driver calls (whilst unlocked), update scandone. 852275964Sadrian */ 853296228Savos if (scandone == 0 && (ss_priv->ss_iflags & ISCAN_CANCEL) != 0) { 854275964Sadrian /* XXX printf? */ 855275964Sadrian if_printf(vap->iv_ifp, 856275964Sadrian "%s: OOPS! scan cancelled during driver call (2)!\n", 857275964Sadrian __func__); 858275964Sadrian scandone = 1; 859275964Sadrian } 860275964Sadrian 861296230Savos scan_done(ss, scandone); 862296230Savos} 863296230Savos 864296230Savosstatic void 865296230Savosscan_done(struct ieee80211_scan_state *ss, int scandone) 866296230Savos{ 867296230Savos struct scan_state *ss_priv = SCAN_PRIVATE(ss); 868296230Savos struct ieee80211com *ic = ss->ss_ic; 869296230Savos struct ieee80211vap *vap = ss->ss_vap; 870296230Savos 871296230Savos IEEE80211_LOCK_ASSERT(ic); 872296230Savos 873275964Sadrian /* 874191746Sthompsa * Clear the SCAN bit first in case frames are 875191746Sthompsa * pending on the station power save queue. If 876191746Sthompsa * we defer this then the dispatch of the frames 877191746Sthompsa * may generate a request to cancel scanning. 878191746Sthompsa */ 879191746Sthompsa ic->ic_flags &= ~IEEE80211_F_SCAN; 880296230Savos 881191746Sthompsa /* 882191746Sthompsa * Drop out of power save mode when a scan has 883191746Sthompsa * completed. If this scan was prematurely terminated 884191746Sthompsa * because it is a background scan then don't notify 885191746Sthompsa * the ap; we'll either return to scanning after we 886191746Sthompsa * receive the beacon frame or we'll drop out of power 887191746Sthompsa * save mode because the beacon indicates we have frames 888191746Sthompsa * waiting for us. 889191746Sthompsa */ 890191746Sthompsa if (scandone) { 891330232Seadler /* 892330232Seadler * If we're not a scan offload device, come back out of 893330232Seadler * station powersave. Offload devices handle this themselves. 894330232Seadler */ 895330232Seadler if ((vap->iv_flags_ext & IEEE80211_FEXT_SCAN_OFFLOAD) == 0) 896330232Seadler vap->iv_sta_ps(vap, 0); 897300383Savos if (ss->ss_next >= ss->ss_last) 898191746Sthompsa ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; 899300383Savos 900300383Savos ieee80211_notify_scan_done(vap); 901170530Ssam } 902296228Savos ss_priv->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); 903296229Savos ss_priv->ss_scanend = 0; 904191746Sthompsa ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); 905191746Sthompsa IEEE80211_UNLOCK(ic); 906170530Ssam#undef ISCAN_REP 907170530Ssam} 908170530Ssam 909170530Ssam/* 910170530Ssam * Process a beacon or probe response frame. 911170530Ssam */ 912284143Sadrianstatic void 913276730Sadrianieee80211_swscan_add_scan(struct ieee80211vap *vap, 914282742Sadrian struct ieee80211_channel *curchan, 915170530Ssam const struct ieee80211_scanparams *sp, 916170530Ssam const struct ieee80211_frame *wh, 917192468Ssam int subtype, int rssi, int noise) 918170530Ssam{ 919178354Ssam struct ieee80211com *ic = vap->iv_ic; 920170530Ssam struct ieee80211_scan_state *ss = ic->ic_scan; 921170530Ssam 922178354Ssam /* XXX locking */ 923170530Ssam /* 924170530Ssam * Frames received during startup are discarded to avoid 925170530Ssam * using scan state setup on the initial entry to the timer 926170530Ssam * callback. This can occur because the device may enable 927170530Ssam * rx prior to our doing the initial channel change in the 928178354Ssam * timer routine. 929170530Ssam */ 930170530Ssam if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) 931170530Ssam return; 932170530Ssam#ifdef IEEE80211_DEBUG 933178354Ssam if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) 934276730Sadrian ieee80211_scan_dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); 935170530Ssam#endif 936170530Ssam if (ss->ss_ops != NULL && 937282742Sadrian ss->ss_ops->scan_add(ss, curchan, sp, wh, subtype, rssi, noise)) { 938170530Ssam /* 939170530Ssam * If we've reached the min dwell time terminate 940170530Ssam * the timer so we'll switch to the next channel. 941170530Ssam */ 942170530Ssam if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && 943297405Sadrian ieee80211_time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { 944178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 945170530Ssam "%s: chan %3d%c min dwell met (%u > %lu)\n", 946170530Ssam __func__, 947170530Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 948276757Sadrian ieee80211_channel_type_char(ic->ic_curchan), 949170530Ssam ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); 950170530Ssam SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; 951170530Ssam /* 952170530Ssam * NB: trigger at next clock tick or wait for the 953178354Ssam * hardware. 954170530Ssam */ 955178354Ssam ic->ic_scan_mindwell(ss); 956170530Ssam } 957170530Ssam } 958170530Ssam} 959170530Ssam 960284143Sadrianstatic struct ieee80211_scan_methods swscan_methods = { 961284143Sadrian .sc_attach = ieee80211_swscan_attach, 962284143Sadrian .sc_detach = ieee80211_swscan_detach, 963284143Sadrian .sc_vattach = ieee80211_swscan_vattach, 964284143Sadrian .sc_vdetach = ieee80211_swscan_vdetach, 965284143Sadrian .sc_set_scan_duration = ieee80211_swscan_set_scan_duration, 966284143Sadrian .sc_start_scan = ieee80211_swscan_start_scan, 967284143Sadrian .sc_check_scan = ieee80211_swscan_check_scan, 968284143Sadrian .sc_bg_scan = ieee80211_swscan_bg_scan, 969284143Sadrian .sc_cancel_scan = ieee80211_swscan_cancel_scan, 970284143Sadrian .sc_cancel_anyscan = ieee80211_swscan_cancel_anyscan, 971284143Sadrian .sc_scan_next = ieee80211_swscan_scan_next, 972284143Sadrian .sc_scan_done = ieee80211_swscan_scan_done, 973284143Sadrian .sc_scan_probe_curchan = ieee80211_swscan_probe_curchan, 974284143Sadrian .sc_add_scan = ieee80211_swscan_add_scan 975284143Sadrian}; 976284143Sadrian 977284143Sadrian/* 978284143Sadrian * Default scan attach method. 979284143Sadrian */ 980284143Sadrianvoid 981284143Sadrianieee80211_swscan_attach(struct ieee80211com *ic) 982284143Sadrian{ 983284143Sadrian struct scan_state *ss; 984284143Sadrian 985284143Sadrian /* 986284143Sadrian * Setup the default methods 987284143Sadrian */ 988284143Sadrian ic->ic_scan_methods = &swscan_methods; 989284143Sadrian 990284143Sadrian /* Allocate initial scan state */ 991284143Sadrian ss = (struct scan_state *) IEEE80211_MALLOC(sizeof(struct scan_state), 992284143Sadrian M_80211_SCAN, IEEE80211_M_NOWAIT | IEEE80211_M_ZERO); 993284143Sadrian if (ss == NULL) { 994284143Sadrian ic->ic_scan = NULL; 995284143Sadrian return; 996284143Sadrian } 997296232Savos TASK_INIT(&ss->ss_scan_start, 0, scan_start, ss); 998296232Savos TIMEOUT_TASK_INIT(ic->ic_tq, &ss->ss_scan_curchan, 0, 999296232Savos scan_curchan_task, ss); 1000284143Sadrian 1001284143Sadrian ic->ic_scan = &ss->base; 1002284143Sadrian ss->base.ss_ic = ic; 1003284143Sadrian 1004284143Sadrian ic->ic_scan_curchan = scan_curchan; 1005284143Sadrian ic->ic_scan_mindwell = scan_mindwell; 1006284143Sadrian} 1007