168e8e04eSSam Leffler /*- 2b032f27cSSam Leffler * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 368e8e04eSSam Leffler * All rights reserved. 468e8e04eSSam Leffler * 568e8e04eSSam Leffler * Redistribution and use in source and binary forms, with or without 668e8e04eSSam Leffler * modification, are permitted provided that the following conditions 768e8e04eSSam Leffler * are met: 868e8e04eSSam Leffler * 1. Redistributions of source code must retain the above copyright 968e8e04eSSam Leffler * notice, this list of conditions and the following disclaimer. 1068e8e04eSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 1168e8e04eSSam Leffler * notice, this list of conditions and the following disclaimer in the 1268e8e04eSSam Leffler * documentation and/or other materials provided with the distribution. 1368e8e04eSSam Leffler * 1468e8e04eSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1568e8e04eSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1668e8e04eSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1768e8e04eSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1868e8e04eSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1968e8e04eSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2068e8e04eSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2168e8e04eSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2268e8e04eSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2368e8e04eSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2468e8e04eSSam Leffler */ 2568e8e04eSSam Leffler 2668e8e04eSSam Leffler #include <sys/cdefs.h> 2768e8e04eSSam Leffler __FBSDID("$FreeBSD$"); 2868e8e04eSSam Leffler 2968e8e04eSSam Leffler /* 3068e8e04eSSam Leffler * IEEE 802.11 scanning support. 3168e8e04eSSam Leffler */ 32b032f27cSSam Leffler #include "opt_wlan.h" 33b032f27cSSam Leffler 3468e8e04eSSam Leffler #include <sys/param.h> 3568e8e04eSSam Leffler #include <sys/systm.h> 365efea30fSAndrew Thompson #include <sys/proc.h> 3768e8e04eSSam Leffler #include <sys/kernel.h> 385efea30fSAndrew Thompson #include <sys/condvar.h> 3968e8e04eSSam Leffler 4068e8e04eSSam Leffler #include <sys/socket.h> 4168e8e04eSSam Leffler 4268e8e04eSSam Leffler #include <net/if.h> 4368e8e04eSSam Leffler #include <net/if_media.h> 4468e8e04eSSam Leffler #include <net/ethernet.h> 4568e8e04eSSam Leffler 4668e8e04eSSam Leffler #include <net80211/ieee80211_var.h> 4768e8e04eSSam Leffler 4868e8e04eSSam Leffler #include <net/bpf.h> 4968e8e04eSSam Leffler 5068e8e04eSSam Leffler struct scan_state { 5168e8e04eSSam Leffler struct ieee80211_scan_state base; /* public state */ 5268e8e04eSSam Leffler 5368e8e04eSSam Leffler u_int ss_iflags; /* flags used internally */ 5468e8e04eSSam Leffler #define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ 5568e8e04eSSam Leffler #define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ 5668e8e04eSSam Leffler #define ISCAN_CANCEL 0x0004 /* cancel current scan */ 575efea30fSAndrew Thompson #define ISCAN_ABORT 0x0008 /* end the scan immediately */ 5868e8e04eSSam Leffler unsigned long ss_chanmindwell; /* min dwell on curchan */ 5968e8e04eSSam Leffler unsigned long ss_scanend; /* time scan must stop */ 6068e8e04eSSam Leffler u_int ss_duration; /* duration for next scan */ 615efea30fSAndrew Thompson struct task ss_scan_task; /* scan execution */ 625efea30fSAndrew Thompson struct cv ss_scan_cv; /* scan signal */ 6368e8e04eSSam Leffler struct callout ss_scan_timer; /* scan timer */ 6468e8e04eSSam Leffler }; 6568e8e04eSSam Leffler #define SCAN_PRIVATE(ss) ((struct scan_state *) ss) 6668e8e04eSSam Leffler 6768e8e04eSSam Leffler /* 6868e8e04eSSam Leffler * Amount of time to go off-channel during a background 6968e8e04eSSam Leffler * scan. This value should be large enough to catch most 7068e8e04eSSam Leffler * ap's but short enough that we can return on-channel 7168e8e04eSSam Leffler * before our listen interval expires. 7268e8e04eSSam Leffler * 7368e8e04eSSam Leffler * XXX tunable 7468e8e04eSSam Leffler * XXX check against configured listen interval 7568e8e04eSSam Leffler */ 7668e8e04eSSam Leffler #define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) 7768e8e04eSSam Leffler 7868e8e04eSSam Leffler /* 7968e8e04eSSam Leffler * Roaming-related defaults. RSSI thresholds are as returned by the 8082c990a4SSam Leffler * driver (.5dBm). Transmit rate thresholds are IEEE rate codes (i.e 81b032f27cSSam Leffler * .5M units) or MCS. 8268e8e04eSSam Leffler */ 8382c990a4SSam Leffler /* rssi thresholds */ 8482c990a4SSam Leffler #define ROAM_RSSI_11A_DEFAULT 14 /* 11a bss */ 8582c990a4SSam Leffler #define ROAM_RSSI_11B_DEFAULT 14 /* 11b bss */ 8682c990a4SSam Leffler #define ROAM_RSSI_11BONLY_DEFAULT 14 /* 11b-only bss */ 8782c990a4SSam Leffler /* transmit rate thresholds */ 8882c990a4SSam Leffler #define ROAM_RATE_11A_DEFAULT 2*12 /* 11a bss */ 8982c990a4SSam Leffler #define ROAM_RATE_11B_DEFAULT 2*5 /* 11b bss */ 9082c990a4SSam Leffler #define ROAM_RATE_11BONLY_DEFAULT 2*1 /* 11b-only bss */ 916a76ae21SSam Leffler #define ROAM_RATE_HALF_DEFAULT 2*6 /* half-width 11a/g bss */ 926a76ae21SSam Leffler #define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */ 9382c990a4SSam Leffler #define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */ 9468e8e04eSSam Leffler 95b032f27cSSam Leffler static void scan_curchan(struct ieee80211_scan_state *, unsigned long); 96b032f27cSSam Leffler static void scan_mindwell(struct ieee80211_scan_state *); 975efea30fSAndrew Thompson static void scan_signal(void *); 985efea30fSAndrew Thompson static void scan_task(void *, int); 9968e8e04eSSam Leffler 10068e8e04eSSam Leffler MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); 10168e8e04eSSam Leffler 10268e8e04eSSam Leffler void 10368e8e04eSSam Leffler ieee80211_scan_attach(struct ieee80211com *ic) 10468e8e04eSSam Leffler { 10568e8e04eSSam Leffler struct scan_state *ss; 10668e8e04eSSam Leffler 107e2126decSSam Leffler ss = (struct scan_state *) malloc(sizeof(struct scan_state), 10868e8e04eSSam Leffler M_80211_SCAN, M_NOWAIT | M_ZERO); 10968e8e04eSSam Leffler if (ss == NULL) { 11068e8e04eSSam Leffler ic->ic_scan = NULL; 11168e8e04eSSam Leffler return; 11268e8e04eSSam Leffler } 113978359b3SSam Leffler callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0); 1145efea30fSAndrew Thompson cv_init(&ss->ss_scan_cv, "scan"); 1155efea30fSAndrew Thompson TASK_INIT(&ss->ss_scan_task, 0, scan_task, ss); 11668e8e04eSSam Leffler ic->ic_scan = &ss->base; 1175efea30fSAndrew Thompson ss->base.ss_ic = ic; 11868e8e04eSSam Leffler 11968e8e04eSSam Leffler ic->ic_scan_curchan = scan_curchan; 12068e8e04eSSam Leffler ic->ic_scan_mindwell = scan_mindwell; 12168e8e04eSSam Leffler } 12268e8e04eSSam Leffler 12368e8e04eSSam Leffler void 12468e8e04eSSam Leffler ieee80211_scan_detach(struct ieee80211com *ic) 12568e8e04eSSam Leffler { 12668e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 12768e8e04eSSam Leffler 12868e8e04eSSam Leffler if (ss != NULL) { 1295efea30fSAndrew Thompson IEEE80211_LOCK(ic); 1305efea30fSAndrew Thompson SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; 1315efea30fSAndrew Thompson scan_signal(ss); 1325efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 1335efea30fSAndrew Thompson ieee80211_draintask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); 1345efea30fSAndrew Thompson KASSERT((ic->ic_flags & IEEE80211_F_SCAN) == 0, 1355efea30fSAndrew Thompson ("scan still running")); 13668e8e04eSSam Leffler if (ss->ss_ops != NULL) { 13768e8e04eSSam Leffler ss->ss_ops->scan_detach(ss); 13868e8e04eSSam Leffler ss->ss_ops = NULL; 13968e8e04eSSam Leffler } 14068e8e04eSSam Leffler ic->ic_scan = NULL; 141e2126decSSam Leffler free(SCAN_PRIVATE(ss), M_80211_SCAN); 14268e8e04eSSam Leffler } 14368e8e04eSSam Leffler } 14468e8e04eSSam Leffler 14582c990a4SSam Leffler static const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = { 14682c990a4SSam Leffler [IEEE80211_MODE_11A] = { .rssi = ROAM_RSSI_11A_DEFAULT, 14782c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 14882c990a4SSam Leffler [IEEE80211_MODE_11G] = { .rssi = ROAM_RSSI_11B_DEFAULT, 14982c990a4SSam Leffler .rate = ROAM_RATE_11B_DEFAULT }, 15082c990a4SSam Leffler [IEEE80211_MODE_11B] = { .rssi = ROAM_RSSI_11BONLY_DEFAULT, 15182c990a4SSam Leffler .rate = ROAM_RATE_11BONLY_DEFAULT }, 15282c990a4SSam Leffler [IEEE80211_MODE_TURBO_A]= { .rssi = ROAM_RSSI_11A_DEFAULT, 15382c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 15482c990a4SSam Leffler [IEEE80211_MODE_TURBO_G]= { .rssi = ROAM_RSSI_11A_DEFAULT, 15582c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 15682c990a4SSam Leffler [IEEE80211_MODE_STURBO_A]={ .rssi = ROAM_RSSI_11A_DEFAULT, 15782c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 1586a76ae21SSam Leffler [IEEE80211_MODE_HALF] = { .rssi = ROAM_RSSI_11A_DEFAULT, 1596a76ae21SSam Leffler .rate = ROAM_RATE_HALF_DEFAULT }, 1606a76ae21SSam Leffler [IEEE80211_MODE_QUARTER]= { .rssi = ROAM_RSSI_11A_DEFAULT, 1616a76ae21SSam Leffler .rate = ROAM_RATE_QUARTER_DEFAULT }, 16282c990a4SSam Leffler [IEEE80211_MODE_11NA] = { .rssi = ROAM_RSSI_11A_DEFAULT, 16382c990a4SSam Leffler .rate = ROAM_MCS_11N_DEFAULT }, 16482c990a4SSam Leffler [IEEE80211_MODE_11NG] = { .rssi = ROAM_RSSI_11B_DEFAULT, 16582c990a4SSam Leffler .rate = ROAM_MCS_11N_DEFAULT }, 16682c990a4SSam Leffler }; 167b032f27cSSam Leffler 168b032f27cSSam Leffler void 169b032f27cSSam Leffler ieee80211_scan_vattach(struct ieee80211vap *vap) 170b032f27cSSam Leffler { 171b032f27cSSam Leffler vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz; 172b032f27cSSam Leffler vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz; 173b032f27cSSam Leffler vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz; 174b032f27cSSam Leffler 175b032f27cSSam Leffler vap->iv_roaming = IEEE80211_ROAMING_AUTO; 17682c990a4SSam Leffler memcpy(vap->iv_roamparms, defroam, sizeof(defroam)); 177b032f27cSSam Leffler } 178b032f27cSSam Leffler 179b032f27cSSam Leffler void 180b032f27cSSam Leffler ieee80211_scan_vdetach(struct ieee80211vap *vap) 181b032f27cSSam Leffler { 182b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 183b032f27cSSam Leffler struct ieee80211_scan_state *ss; 184b032f27cSSam Leffler 185b032f27cSSam Leffler IEEE80211_LOCK(ic); 186b032f27cSSam Leffler ss = ic->ic_scan; 187b032f27cSSam Leffler if (ss != NULL && ss->ss_vap == vap) { 188b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 1895efea30fSAndrew Thompson SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_ABORT; 1905efea30fSAndrew Thompson scan_signal(ss); 191b032f27cSSam Leffler } 192b032f27cSSam Leffler if (ss->ss_ops != NULL) { 193b032f27cSSam Leffler ss->ss_ops->scan_detach(ss); 194b032f27cSSam Leffler ss->ss_ops = NULL; 195b032f27cSSam Leffler } 196b032f27cSSam Leffler ss->ss_vap = NULL; 197b032f27cSSam Leffler } 198b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 199b032f27cSSam Leffler } 200b032f27cSSam Leffler 20168e8e04eSSam Leffler /* 20268e8e04eSSam Leffler * Simple-minded scanner module support. 20368e8e04eSSam Leffler */ 204b032f27cSSam Leffler static const char *scan_modnames[IEEE80211_OPMODE_MAX] = { 20568e8e04eSSam Leffler "wlan_scan_sta", /* IEEE80211_M_IBSS */ 20668e8e04eSSam Leffler "wlan_scan_sta", /* IEEE80211_M_STA */ 20768e8e04eSSam Leffler "wlan_scan_wds", /* IEEE80211_M_WDS */ 20868e8e04eSSam Leffler "wlan_scan_sta", /* IEEE80211_M_AHDEMO */ 20968e8e04eSSam Leffler "wlan_scan_ap", /* IEEE80211_M_HOSTAP */ 21068e8e04eSSam Leffler "wlan_scan_monitor", /* IEEE80211_M_MONITOR */ 21168e8e04eSSam Leffler }; 212b032f27cSSam Leffler static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX]; 21368e8e04eSSam Leffler 21468e8e04eSSam Leffler const struct ieee80211_scanner * 21568e8e04eSSam Leffler ieee80211_scanner_get(enum ieee80211_opmode mode) 21668e8e04eSSam Leffler { 217b032f27cSSam Leffler if (mode >= IEEE80211_OPMODE_MAX) 21868e8e04eSSam Leffler return NULL; 219b032f27cSSam Leffler if (scanners[mode] == NULL) 22068e8e04eSSam Leffler ieee80211_load_module(scan_modnames[mode]); 22168e8e04eSSam Leffler return scanners[mode]; 22268e8e04eSSam Leffler } 22368e8e04eSSam Leffler 22468e8e04eSSam Leffler void 22568e8e04eSSam Leffler ieee80211_scanner_register(enum ieee80211_opmode mode, 22668e8e04eSSam Leffler const struct ieee80211_scanner *scan) 22768e8e04eSSam Leffler { 228b032f27cSSam Leffler if (mode >= IEEE80211_OPMODE_MAX) 22968e8e04eSSam Leffler return; 23068e8e04eSSam Leffler scanners[mode] = scan; 23168e8e04eSSam Leffler } 23268e8e04eSSam Leffler 23368e8e04eSSam Leffler void 23468e8e04eSSam Leffler ieee80211_scanner_unregister(enum ieee80211_opmode mode, 23568e8e04eSSam Leffler const struct ieee80211_scanner *scan) 23668e8e04eSSam Leffler { 237b032f27cSSam Leffler if (mode >= IEEE80211_OPMODE_MAX) 23868e8e04eSSam Leffler return; 23968e8e04eSSam Leffler if (scanners[mode] == scan) 24068e8e04eSSam Leffler scanners[mode] = NULL; 24168e8e04eSSam Leffler } 24268e8e04eSSam Leffler 24368e8e04eSSam Leffler void 24468e8e04eSSam Leffler ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) 24568e8e04eSSam Leffler { 24668e8e04eSSam Leffler int m; 24768e8e04eSSam Leffler 248b032f27cSSam Leffler for (m = 0; m < IEEE80211_OPMODE_MAX; m++) 24968e8e04eSSam Leffler if (scanners[m] == scan) 25068e8e04eSSam Leffler scanners[m] = NULL; 25168e8e04eSSam Leffler } 25268e8e04eSSam Leffler 25368e8e04eSSam Leffler /* 25468e8e04eSSam Leffler * Update common scanner state to reflect the current 25568e8e04eSSam Leffler * operating mode. This is called when the state machine 25668e8e04eSSam Leffler * is transitioned to RUN state w/o scanning--e.g. when 25768e8e04eSSam Leffler * operating in monitor mode. The purpose of this is to 25868e8e04eSSam Leffler * ensure later callbacks find ss_ops set to properly 25968e8e04eSSam Leffler * reflect current operating mode. 26068e8e04eSSam Leffler */ 261b032f27cSSam Leffler static void 262b032f27cSSam Leffler scan_update_locked(struct ieee80211vap *vap, 263b032f27cSSam Leffler const struct ieee80211_scanner *scan) 26468e8e04eSSam Leffler { 265b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 26668e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 26768e8e04eSSam Leffler 268b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 269b032f27cSSam Leffler 270b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 271b032f27cSSam Leffler if (ss->ss_vap != vap || ss->ss_ops != scan) { 272b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 273b032f27cSSam Leffler "%s: current scanner is <%s:%s>, switch to <%s:%s>\n", 274b032f27cSSam Leffler __func__, 275b032f27cSSam Leffler ss->ss_vap != NULL ? 276b032f27cSSam Leffler ss->ss_vap->iv_ifp->if_xname : "none", 277b032f27cSSam Leffler ss->ss_vap != NULL ? 278b032f27cSSam Leffler ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none", 279b032f27cSSam Leffler vap->iv_ifp->if_xname, 280b032f27cSSam Leffler ieee80211_opmode_name[vap->iv_opmode]); 28168e8e04eSSam Leffler } 282b032f27cSSam Leffler #endif 283b032f27cSSam Leffler ss->ss_vap = vap; 28468e8e04eSSam Leffler if (ss->ss_ops != scan) { 285b032f27cSSam Leffler /* 286b032f27cSSam Leffler * Switch scanners; detach old, attach new. Special 287b032f27cSSam Leffler * case where a single scan module implements multiple 288b032f27cSSam Leffler * policies by using different scan ops but a common 289b032f27cSSam Leffler * core. We assume if the old and new attach methods 290b032f27cSSam Leffler * are identical then it's ok to just change ss_ops 291b032f27cSSam Leffler * and not flush the internal state of the module. 292b032f27cSSam Leffler */ 293b032f27cSSam Leffler if (scan == NULL || ss->ss_ops == NULL || 294b032f27cSSam Leffler ss->ss_ops->scan_attach != scan->scan_attach) { 29568e8e04eSSam Leffler if (ss->ss_ops != NULL) 29668e8e04eSSam Leffler ss->ss_ops->scan_detach(ss); 29768e8e04eSSam Leffler if (scan != NULL && !scan->scan_attach(ss)) { 29868e8e04eSSam Leffler /* XXX attach failure */ 29968e8e04eSSam Leffler /* XXX stat+msg */ 300b032f27cSSam Leffler scan = NULL; 301b032f27cSSam Leffler } 302b032f27cSSam Leffler } 30368e8e04eSSam Leffler ss->ss_ops = scan; 30468e8e04eSSam Leffler } 30568e8e04eSSam Leffler } 30668e8e04eSSam Leffler 30768e8e04eSSam Leffler static char 30868e8e04eSSam Leffler channel_type(const struct ieee80211_channel *c) 30968e8e04eSSam Leffler { 31068e8e04eSSam Leffler if (IEEE80211_IS_CHAN_ST(c)) 31168e8e04eSSam Leffler return 'S'; 31268e8e04eSSam Leffler if (IEEE80211_IS_CHAN_108A(c)) 31368e8e04eSSam Leffler return 'T'; 31468e8e04eSSam Leffler if (IEEE80211_IS_CHAN_108G(c)) 31568e8e04eSSam Leffler return 'G'; 31668e8e04eSSam Leffler if (IEEE80211_IS_CHAN_HT(c)) 31768e8e04eSSam Leffler return 'n'; 31868e8e04eSSam Leffler if (IEEE80211_IS_CHAN_A(c)) 31968e8e04eSSam Leffler return 'a'; 32068e8e04eSSam Leffler if (IEEE80211_IS_CHAN_ANYG(c)) 32168e8e04eSSam Leffler return 'g'; 32268e8e04eSSam Leffler if (IEEE80211_IS_CHAN_B(c)) 32368e8e04eSSam Leffler return 'b'; 32468e8e04eSSam Leffler return 'f'; 32568e8e04eSSam Leffler } 32668e8e04eSSam Leffler 32768e8e04eSSam Leffler void 32868e8e04eSSam Leffler ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) 32968e8e04eSSam Leffler { 3305efea30fSAndrew Thompson struct ieee80211com *ic = ss->ss_ic; 33168e8e04eSSam Leffler const char *sep; 33268e8e04eSSam Leffler int i; 33368e8e04eSSam Leffler 33468e8e04eSSam Leffler sep = ""; 33568e8e04eSSam Leffler for (i = ss->ss_next; i < ss->ss_last; i++) { 33668e8e04eSSam Leffler const struct ieee80211_channel *c = ss->ss_chans[i]; 33768e8e04eSSam Leffler 33868e8e04eSSam Leffler printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c), 33968e8e04eSSam Leffler channel_type(c)); 34068e8e04eSSam Leffler sep = ", "; 34168e8e04eSSam Leffler } 34268e8e04eSSam Leffler } 34368e8e04eSSam Leffler 344b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 345b032f27cSSam Leffler static void 346b032f27cSSam Leffler scan_dump(struct ieee80211_scan_state *ss) 347b032f27cSSam Leffler { 348b032f27cSSam Leffler struct ieee80211vap *vap = ss->ss_vap; 349b032f27cSSam Leffler 350b032f27cSSam Leffler if_printf(vap->iv_ifp, "scan set "); 351b032f27cSSam Leffler ieee80211_scan_dump_channels(ss); 352110a70e3SSam Leffler printf(" dwell min %lums max %lums\n", 353110a70e3SSam Leffler ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(ss->ss_maxdwell)); 354b032f27cSSam Leffler } 355b032f27cSSam Leffler #endif /* IEEE80211_DEBUG */ 356b032f27cSSam Leffler 35768e8e04eSSam Leffler static void 358b032f27cSSam Leffler copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, 35968e8e04eSSam Leffler int nssid, const struct ieee80211_scan_ssid ssids[]) 36068e8e04eSSam Leffler { 36168e8e04eSSam Leffler if (nssid > IEEE80211_SCAN_MAX_SSID) { 36268e8e04eSSam Leffler /* XXX printf */ 363b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 36468e8e04eSSam Leffler "%s: too many ssid %d, ignoring all of them\n", 36568e8e04eSSam Leffler __func__, nssid); 36668e8e04eSSam Leffler return; 36768e8e04eSSam Leffler } 36868e8e04eSSam Leffler memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0])); 36968e8e04eSSam Leffler ss->ss_nssid = nssid; 37068e8e04eSSam Leffler } 37168e8e04eSSam Leffler 37268e8e04eSSam Leffler /* 37368e8e04eSSam Leffler * Start a scan unless one is already going. 37468e8e04eSSam Leffler */ 375b032f27cSSam Leffler static int 376b032f27cSSam Leffler start_scan_locked(const struct ieee80211_scanner *scan, 377b032f27cSSam Leffler struct ieee80211vap *vap, int flags, u_int duration, 378b032f27cSSam Leffler u_int mindwell, u_int maxdwell, 37968e8e04eSSam Leffler u_int nssid, const struct ieee80211_scan_ssid ssids[]) 38068e8e04eSSam Leffler { 381b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 38268e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 38368e8e04eSSam Leffler 384b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 38568e8e04eSSam Leffler 386b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_CSAPENDING) { 387b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 388b032f27cSSam Leffler "%s: scan inhibited by pending channel change\n", __func__); 389b032f27cSSam Leffler } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 390b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 391b032f27cSSam Leffler "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" 39268e8e04eSSam Leffler , __func__ 39368e8e04eSSam Leffler , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" 394b032f27cSSam Leffler , duration, mindwell, maxdwell 395b032f27cSSam Leffler , ieee80211_phymode_name[vap->iv_des_mode] 39668e8e04eSSam Leffler , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" 39768e8e04eSSam Leffler , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" 398b032f27cSSam Leffler , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" 399b032f27cSSam Leffler , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" 40068e8e04eSSam Leffler , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" 40168e8e04eSSam Leffler , flags & IEEE80211_SCAN_ONCE ? ", once" : "" 40268e8e04eSSam Leffler ); 40368e8e04eSSam Leffler 404b032f27cSSam Leffler scan_update_locked(vap, scan); 40568e8e04eSSam Leffler if (ss->ss_ops != NULL) { 40668e8e04eSSam Leffler if ((flags & IEEE80211_SCAN_NOSSID) == 0) 407b032f27cSSam Leffler copy_ssid(vap, ss, nssid, ssids); 40868e8e04eSSam Leffler 40968e8e04eSSam Leffler /* NB: top 4 bits for internal use */ 41068e8e04eSSam Leffler ss->ss_flags = flags & 0xfff; 41168e8e04eSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 412b032f27cSSam Leffler vap->iv_stats.is_scan_active++; 41368e8e04eSSam Leffler else 414b032f27cSSam Leffler vap->iv_stats.is_scan_passive++; 41568e8e04eSSam Leffler if (flags & IEEE80211_SCAN_FLUSH) 41668e8e04eSSam Leffler ss->ss_ops->scan_flush(ss); 41768e8e04eSSam Leffler 41868e8e04eSSam Leffler /* NB: flush frames rx'd before 1st channel change */ 41968e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 4205efea30fSAndrew Thompson SCAN_PRIVATE(ss)->ss_duration = duration; 421b032f27cSSam Leffler ss->ss_next = 0; 422b032f27cSSam Leffler ss->ss_mindwell = mindwell; 423b032f27cSSam Leffler ss->ss_maxdwell = maxdwell; 4245efea30fSAndrew Thompson /* NB: scan_start must be before the scan runtask */ 425b032f27cSSam Leffler ss->ss_ops->scan_start(ss, vap); 426b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 427b032f27cSSam Leffler if (ieee80211_msg_scan(vap)) 428b032f27cSSam Leffler scan_dump(ss); 429b032f27cSSam Leffler #endif /* IEEE80211_DEBUG */ 43068e8e04eSSam Leffler ic->ic_flags |= IEEE80211_F_SCAN; 4315efea30fSAndrew Thompson ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); 43268e8e04eSSam Leffler } 43368e8e04eSSam Leffler } else { 434b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 43568e8e04eSSam Leffler "%s: %s scan already in progress\n", __func__, 43668e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); 43768e8e04eSSam Leffler } 438b032f27cSSam Leffler return (ic->ic_flags & IEEE80211_F_SCAN); 439b032f27cSSam Leffler } 440b032f27cSSam Leffler 441b032f27cSSam Leffler /* 442b032f27cSSam Leffler * Start a scan unless one is already going. 443b032f27cSSam Leffler */ 444b032f27cSSam Leffler int 445b032f27cSSam Leffler ieee80211_start_scan(struct ieee80211vap *vap, int flags, 446b032f27cSSam Leffler u_int duration, u_int mindwell, u_int maxdwell, 447b032f27cSSam Leffler u_int nssid, const struct ieee80211_scan_ssid ssids[]) 448b032f27cSSam Leffler { 449b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 450b032f27cSSam Leffler const struct ieee80211_scanner *scan; 451b032f27cSSam Leffler int result; 452b032f27cSSam Leffler 453b032f27cSSam Leffler scan = ieee80211_scanner_get(vap->iv_opmode); 454b032f27cSSam Leffler if (scan == NULL) { 455b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 456b032f27cSSam Leffler "%s: no scanner support for %s mode\n", 457b032f27cSSam Leffler __func__, ieee80211_opmode_name[vap->iv_opmode]); 458b032f27cSSam Leffler /* XXX stat */ 459b032f27cSSam Leffler return 0; 460b032f27cSSam Leffler } 461b032f27cSSam Leffler 462b032f27cSSam Leffler IEEE80211_LOCK(ic); 463b032f27cSSam Leffler result = start_scan_locked(scan, vap, flags, duration, 464b032f27cSSam Leffler mindwell, maxdwell, nssid, ssids); 46568e8e04eSSam Leffler IEEE80211_UNLOCK(ic); 46668e8e04eSSam Leffler 467b032f27cSSam Leffler return result; 46868e8e04eSSam Leffler } 46968e8e04eSSam Leffler 47068e8e04eSSam Leffler /* 47168e8e04eSSam Leffler * Check the scan cache for an ap/channel to use; if that 47268e8e04eSSam Leffler * fails then kick off a new scan. 47368e8e04eSSam Leffler */ 47468e8e04eSSam Leffler int 475b032f27cSSam Leffler ieee80211_check_scan(struct ieee80211vap *vap, int flags, 476b032f27cSSam Leffler u_int duration, u_int mindwell, u_int maxdwell, 47768e8e04eSSam Leffler u_int nssid, const struct ieee80211_scan_ssid ssids[]) 47868e8e04eSSam Leffler { 479b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 48068e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 481b032f27cSSam Leffler const struct ieee80211_scanner *scan; 482aa0fbc73SSam Leffler int result; 483b032f27cSSam Leffler 484b032f27cSSam Leffler scan = ieee80211_scanner_get(vap->iv_opmode); 485b032f27cSSam Leffler if (scan == NULL) { 486b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 487b032f27cSSam Leffler "%s: no scanner support for %s mode\n", 488b032f27cSSam Leffler __func__, vap->iv_opmode); 489b032f27cSSam Leffler /* XXX stat */ 490b032f27cSSam Leffler return 0; 491b032f27cSSam Leffler } 49268e8e04eSSam Leffler 49368e8e04eSSam Leffler /* 49468e8e04eSSam Leffler * Check if there's a list of scan candidates already. 49568e8e04eSSam Leffler * XXX want more than the ap we're currently associated with 49668e8e04eSSam Leffler */ 49768e8e04eSSam Leffler 49868e8e04eSSam Leffler IEEE80211_LOCK(ic); 499b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 500b032f27cSSam Leffler "%s: %s scan, %s%s%s%s%s\n" 50168e8e04eSSam Leffler , __func__ 50268e8e04eSSam Leffler , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" 50368e8e04eSSam Leffler , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" 50468e8e04eSSam Leffler , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" 505b032f27cSSam Leffler , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" 50668e8e04eSSam Leffler , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" 50768e8e04eSSam Leffler , flags & IEEE80211_SCAN_ONCE ? ", once" : "" 50868e8e04eSSam Leffler ); 50968e8e04eSSam Leffler 510b032f27cSSam Leffler if (ss->ss_ops != scan) { 511b032f27cSSam Leffler /* XXX re-use cache contents? e.g. adhoc<->sta */ 512b032f27cSSam Leffler flags |= IEEE80211_SCAN_FLUSH; 513b032f27cSSam Leffler } 514b032f27cSSam Leffler scan_update_locked(vap, scan); 51568e8e04eSSam Leffler if (ss->ss_ops != NULL) { 516b032f27cSSam Leffler /* XXX verify ss_ops matches vap->iv_opmode */ 51768e8e04eSSam Leffler if ((flags & IEEE80211_SCAN_NOSSID) == 0) { 51868e8e04eSSam Leffler /* 51968e8e04eSSam Leffler * Update the ssid list and mark flags so if 52068e8e04eSSam Leffler * we call start_scan it doesn't duplicate work. 52168e8e04eSSam Leffler */ 522b032f27cSSam Leffler copy_ssid(vap, ss, nssid, ssids); 52368e8e04eSSam Leffler flags |= IEEE80211_SCAN_NOSSID; 52468e8e04eSSam Leffler } 52568e8e04eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && 526b032f27cSSam Leffler (flags & IEEE80211_SCAN_FLUSH) == 0 && 527b032f27cSSam Leffler time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { 52868e8e04eSSam Leffler /* 52968e8e04eSSam Leffler * We're not currently scanning and the cache is 53068e8e04eSSam Leffler * deemed hot enough to consult. Lock out others 53168e8e04eSSam Leffler * by marking IEEE80211_F_SCAN while we decide if 53268e8e04eSSam Leffler * something is already in the scan cache we can 53368e8e04eSSam Leffler * use. Also discard any frames that might come 53468e8e04eSSam Leffler * in while temporarily marked as scanning. 53568e8e04eSSam Leffler */ 53668e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 53768e8e04eSSam Leffler ic->ic_flags |= IEEE80211_F_SCAN; 538aa0fbc73SSam Leffler 539aa0fbc73SSam Leffler /* NB: need to use supplied flags in check */ 540b032f27cSSam Leffler ss->ss_flags = flags & 0xff; 541aa0fbc73SSam Leffler result = ss->ss_ops->scan_end(ss, vap); 542aa0fbc73SSam Leffler 54368e8e04eSSam Leffler ic->ic_flags &= ~IEEE80211_F_SCAN; 544aa0fbc73SSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; 545aa0fbc73SSam Leffler if (result) { 546b032f27cSSam Leffler ieee80211_notify_scan_done(vap); 547b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 54868e8e04eSSam Leffler return 1; 54968e8e04eSSam Leffler } 550aa0fbc73SSam Leffler } 55168e8e04eSSam Leffler } 552b032f27cSSam Leffler result = start_scan_locked(scan, vap, flags, duration, 553b032f27cSSam Leffler mindwell, maxdwell, nssid, ssids); 554b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 555b032f27cSSam Leffler 556b032f27cSSam Leffler return result; 557b032f27cSSam Leffler } 558b032f27cSSam Leffler 559b032f27cSSam Leffler /* 560b032f27cSSam Leffler * Check the scan cache for an ap/channel to use; if that fails 561b032f27cSSam Leffler * then kick off a scan using the current settings. 562b032f27cSSam Leffler */ 563b032f27cSSam Leffler int 564b032f27cSSam Leffler ieee80211_check_scan_current(struct ieee80211vap *vap) 565b032f27cSSam Leffler { 566b032f27cSSam Leffler return ieee80211_check_scan(vap, 567b032f27cSSam Leffler IEEE80211_SCAN_ACTIVE, 568b032f27cSSam Leffler IEEE80211_SCAN_FOREVER, 0, 0, 569b032f27cSSam Leffler vap->iv_des_nssid, vap->iv_des_ssid); 57068e8e04eSSam Leffler } 57168e8e04eSSam Leffler 57268e8e04eSSam Leffler /* 57368e8e04eSSam Leffler * Restart a previous scan. If the previous scan completed 57468e8e04eSSam Leffler * then we start again using the existing channel list. 57568e8e04eSSam Leffler */ 57668e8e04eSSam Leffler int 577b032f27cSSam Leffler ieee80211_bg_scan(struct ieee80211vap *vap, int flags) 57868e8e04eSSam Leffler { 579b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 58068e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 581b032f27cSSam Leffler const struct ieee80211_scanner *scan; 582b032f27cSSam Leffler 583b032f27cSSam Leffler scan = ieee80211_scanner_get(vap->iv_opmode); 584b032f27cSSam Leffler if (scan == NULL) { 585b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 586b032f27cSSam Leffler "%s: no scanner support for %s mode\n", 587b032f27cSSam Leffler __func__, vap->iv_opmode); 588b032f27cSSam Leffler /* XXX stat */ 589b032f27cSSam Leffler return 0; 590b032f27cSSam Leffler } 59168e8e04eSSam Leffler 59268e8e04eSSam Leffler IEEE80211_LOCK(ic); 59368e8e04eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 59468e8e04eSSam Leffler u_int duration; 59568e8e04eSSam Leffler /* 59668e8e04eSSam Leffler * Go off-channel for a fixed interval that is large 59768e8e04eSSam Leffler * enough to catch most ap's but short enough that 59868e8e04eSSam Leffler * we can return on-channel before our listen interval 59968e8e04eSSam Leffler * expires. 60068e8e04eSSam Leffler */ 60168e8e04eSSam Leffler duration = IEEE80211_SCAN_OFFCHANNEL; 60268e8e04eSSam Leffler 603b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 60468e8e04eSSam Leffler "%s: %s scan, ticks %u duration %lu\n", __func__, 60568e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", 60668e8e04eSSam Leffler ticks, duration); 60768e8e04eSSam Leffler 608b032f27cSSam Leffler scan_update_locked(vap, scan); 60968e8e04eSSam Leffler if (ss->ss_ops != NULL) { 610b032f27cSSam Leffler ss->ss_vap = vap; 61168e8e04eSSam Leffler /* 61268e8e04eSSam Leffler * A background scan does not select a new sta; it 61368e8e04eSSam Leffler * just refreshes the scan cache. Also, indicate 61468e8e04eSSam Leffler * the scan logic should follow the beacon schedule: 61568e8e04eSSam Leffler * we go off-channel and scan for a while, then 61668e8e04eSSam Leffler * return to the bss channel to receive a beacon, 61768e8e04eSSam Leffler * then go off-channel again. All during this time 61868e8e04eSSam Leffler * we notify the ap we're in power save mode. When 61968e8e04eSSam Leffler * the scan is complete we leave power save mode. 62068e8e04eSSam Leffler * If any beacon indicates there are frames pending 62168e8e04eSSam Leffler * for us then we drop out of power save mode 62268e8e04eSSam Leffler * (and background scan) automatically by way of the 62368e8e04eSSam Leffler * usual sta power save logic. 62468e8e04eSSam Leffler */ 62568e8e04eSSam Leffler ss->ss_flags |= IEEE80211_SCAN_NOPICK 626b032f27cSSam Leffler | IEEE80211_SCAN_BGSCAN 627b032f27cSSam Leffler | flags 628b032f27cSSam Leffler ; 62968e8e04eSSam Leffler /* if previous scan completed, restart */ 63068e8e04eSSam Leffler if (ss->ss_next >= ss->ss_last) { 63168e8e04eSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 632b032f27cSSam Leffler vap->iv_stats.is_scan_active++; 63368e8e04eSSam Leffler else 634b032f27cSSam Leffler vap->iv_stats.is_scan_passive++; 635b032f27cSSam Leffler /* 636b032f27cSSam Leffler * NB: beware of the scan cache being flushed; 637b032f27cSSam Leffler * if the channel list is empty use the 638b032f27cSSam Leffler * scan_start method to populate it. 639b032f27cSSam Leffler */ 640b032f27cSSam Leffler ss->ss_next = 0; 641b032f27cSSam Leffler if (ss->ss_last != 0) 642b032f27cSSam Leffler ss->ss_ops->scan_restart(ss, vap); 643b032f27cSSam Leffler else { 644b032f27cSSam Leffler ss->ss_ops->scan_start(ss, vap); 645b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 646b032f27cSSam Leffler if (ieee80211_msg_scan(vap)) 647b032f27cSSam Leffler scan_dump(ss); 648b032f27cSSam Leffler #endif /* IEEE80211_DEBUG */ 649b032f27cSSam Leffler } 65068e8e04eSSam Leffler } 65168e8e04eSSam Leffler /* NB: flush frames rx'd before 1st channel change */ 65268e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 6535efea30fSAndrew Thompson SCAN_PRIVATE(ss)->ss_duration = duration; 65468e8e04eSSam Leffler ss->ss_maxdwell = duration; 65568e8e04eSSam Leffler ic->ic_flags |= IEEE80211_F_SCAN; 65668e8e04eSSam Leffler ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; 6575efea30fSAndrew Thompson ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); 65868e8e04eSSam Leffler } else { 65968e8e04eSSam Leffler /* XXX msg+stat */ 66068e8e04eSSam Leffler } 66168e8e04eSSam Leffler } else { 662b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 66368e8e04eSSam Leffler "%s: %s scan already in progress\n", __func__, 66468e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); 66568e8e04eSSam Leffler } 66668e8e04eSSam Leffler IEEE80211_UNLOCK(ic); 66768e8e04eSSam Leffler 66868e8e04eSSam Leffler /* NB: racey, does it matter? */ 66968e8e04eSSam Leffler return (ic->ic_flags & IEEE80211_F_SCAN); 67068e8e04eSSam Leffler } 67168e8e04eSSam Leffler 67268e8e04eSSam Leffler /* 673b032f27cSSam Leffler * Cancel any scan currently going on for the specified vap. 674b032f27cSSam Leffler */ 675b032f27cSSam Leffler void 676b032f27cSSam Leffler ieee80211_cancel_scan(struct ieee80211vap *vap) 677b032f27cSSam Leffler { 678b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 679b032f27cSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 680b032f27cSSam Leffler 681b032f27cSSam Leffler IEEE80211_LOCK(ic); 682b032f27cSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) && 683b032f27cSSam Leffler ss->ss_vap == vap && 684b032f27cSSam Leffler (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { 685b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 686b032f27cSSam Leffler "%s: cancel %s scan\n", __func__, 687b032f27cSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? 688b032f27cSSam Leffler "active" : "passive"); 689b032f27cSSam Leffler 690b032f27cSSam Leffler /* clear bg scan NOPICK and mark cancel request */ 691b032f27cSSam Leffler ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; 692b032f27cSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; 6935efea30fSAndrew Thompson /* wake up the scan task */ 6945efea30fSAndrew Thompson scan_signal(ss); 695b032f27cSSam Leffler } 696b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 697b032f27cSSam Leffler } 698b032f27cSSam Leffler 699b032f27cSSam Leffler /* 70068e8e04eSSam Leffler * Cancel any scan currently going on. 70168e8e04eSSam Leffler */ 70268e8e04eSSam Leffler void 703b032f27cSSam Leffler ieee80211_cancel_anyscan(struct ieee80211vap *vap) 70468e8e04eSSam Leffler { 705b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 70668e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 70768e8e04eSSam Leffler 70868e8e04eSSam Leffler IEEE80211_LOCK(ic); 70968e8e04eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) && 71068e8e04eSSam Leffler (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { 711b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 71268e8e04eSSam Leffler "%s: cancel %s scan\n", __func__, 71368e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? 71468e8e04eSSam Leffler "active" : "passive"); 71568e8e04eSSam Leffler 71668e8e04eSSam Leffler /* clear bg scan NOPICK and mark cancel request */ 71768e8e04eSSam Leffler ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; 71868e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; 7195efea30fSAndrew Thompson /* wake up the scan task */ 7205efea30fSAndrew Thompson scan_signal(ss); 72168e8e04eSSam Leffler } 72268e8e04eSSam Leffler IEEE80211_UNLOCK(ic); 72368e8e04eSSam Leffler } 72468e8e04eSSam Leffler 72568e8e04eSSam Leffler /* 72668e8e04eSSam Leffler * Public access to scan_next for drivers that manage 72768e8e04eSSam Leffler * scanning themselves (e.g. for firmware-based devices). 72868e8e04eSSam Leffler */ 72968e8e04eSSam Leffler void 730b032f27cSSam Leffler ieee80211_scan_next(struct ieee80211vap *vap) 73168e8e04eSSam Leffler { 732b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 733b032f27cSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 734b032f27cSSam Leffler 7355efea30fSAndrew Thompson /* wake up the scan task */ 7365efea30fSAndrew Thompson IEEE80211_LOCK(ic); 7375efea30fSAndrew Thompson scan_signal(ss); 7385efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 73968e8e04eSSam Leffler } 74068e8e04eSSam Leffler 74168e8e04eSSam Leffler /* 742d81b3a55SAndrew Thompson * Public access to scan_next for drivers that are not able to scan single 743d81b3a55SAndrew Thompson * channels (e.g. for firmware-based devices). 744d81b3a55SAndrew Thompson */ 745d81b3a55SAndrew Thompson void 746b032f27cSSam Leffler ieee80211_scan_done(struct ieee80211vap *vap) 747d81b3a55SAndrew Thompson { 748b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 749b032f27cSSam Leffler struct ieee80211_scan_state *ss; 750d81b3a55SAndrew Thompson 751b032f27cSSam Leffler IEEE80211_LOCK(ic); 752b032f27cSSam Leffler ss = ic->ic_scan; 753d81b3a55SAndrew Thompson ss->ss_next = ss->ss_last; /* all channels are complete */ 7545efea30fSAndrew Thompson scan_signal(ss); 755b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 756b032f27cSSam Leffler } 757b032f27cSSam Leffler 758b032f27cSSam Leffler /* 759b032f27cSSam Leffler * Probe the curent channel, if allowed, while scanning. 760b032f27cSSam Leffler * If the channel is not marked passive-only then send 761b032f27cSSam Leffler * a probe request immediately. Otherwise mark state and 762b032f27cSSam Leffler * listen for beacons on the channel; if we receive something 763b032f27cSSam Leffler * then we'll transmit a probe request. 764b032f27cSSam Leffler */ 765b032f27cSSam Leffler void 766b032f27cSSam Leffler ieee80211_probe_curchan(struct ieee80211vap *vap, int force) 767b032f27cSSam Leffler { 768b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 769b032f27cSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 770b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ifp; 771b032f27cSSam Leffler int i; 772b032f27cSSam Leffler 773b032f27cSSam Leffler if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) { 774b032f27cSSam Leffler ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; 775b032f27cSSam Leffler return; 776b032f27cSSam Leffler } 777b032f27cSSam Leffler /* 778b032f27cSSam Leffler * Send directed probe requests followed by any 779b032f27cSSam Leffler * broadcast probe request. 780b032f27cSSam Leffler * XXX remove dependence on ic/vap->iv_bss 781b032f27cSSam Leffler */ 782b032f27cSSam Leffler for (i = 0; i < ss->ss_nssid; i++) 783b032f27cSSam Leffler ieee80211_send_probereq(vap->iv_bss, 784b032f27cSSam Leffler vap->iv_myaddr, ifp->if_broadcastaddr, 785b032f27cSSam Leffler ifp->if_broadcastaddr, 786b032f27cSSam Leffler ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); 787b032f27cSSam Leffler if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) 788b032f27cSSam Leffler ieee80211_send_probereq(vap->iv_bss, 789b032f27cSSam Leffler vap->iv_myaddr, ifp->if_broadcastaddr, 790b032f27cSSam Leffler ifp->if_broadcastaddr, 791b032f27cSSam Leffler "", 0); 792d81b3a55SAndrew Thompson } 793d81b3a55SAndrew Thompson 794d81b3a55SAndrew Thompson /* 79568e8e04eSSam Leffler * Scan curchan. If this is an active scan and the channel 79668e8e04eSSam Leffler * is not marked passive then send probe request frame(s). 79768e8e04eSSam Leffler * Arrange for the channel change after maxdwell ticks. 79868e8e04eSSam Leffler */ 79968e8e04eSSam Leffler static void 800b032f27cSSam Leffler scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 80168e8e04eSSam Leffler { 802b032f27cSSam Leffler struct ieee80211vap *vap = ss->ss_vap; 80368e8e04eSSam Leffler 8045efea30fSAndrew Thompson IEEE80211_LOCK(vap->iv_ic); 805b032f27cSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 806b032f27cSSam Leffler ieee80211_probe_curchan(vap, 0); 80768e8e04eSSam Leffler callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 8085efea30fSAndrew Thompson maxdwell, scan_signal, ss); 8095efea30fSAndrew Thompson IEEE80211_UNLOCK(vap->iv_ic); 8105efea30fSAndrew Thompson } 8115efea30fSAndrew Thompson 8125efea30fSAndrew Thompson static void 8135efea30fSAndrew Thompson scan_signal(void *arg) 8145efea30fSAndrew Thompson { 8155efea30fSAndrew Thompson struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; 8165efea30fSAndrew Thompson 8175efea30fSAndrew Thompson IEEE80211_LOCK_ASSERT(ss->ss_ic); 8185efea30fSAndrew Thompson 8195efea30fSAndrew Thompson cv_signal(&SCAN_PRIVATE(ss)->ss_scan_cv); 82068e8e04eSSam Leffler } 82168e8e04eSSam Leffler 82268e8e04eSSam Leffler /* 82368e8e04eSSam Leffler * Handle mindwell requirements completed; initiate a channel 82468e8e04eSSam Leffler * change to the next channel asap. 82568e8e04eSSam Leffler */ 82668e8e04eSSam Leffler static void 827b032f27cSSam Leffler scan_mindwell(struct ieee80211_scan_state *ss) 82868e8e04eSSam Leffler { 8295efea30fSAndrew Thompson struct ieee80211com *ic = ss->ss_ic; 8305efea30fSAndrew Thompson 8315efea30fSAndrew Thompson IEEE80211_LOCK(ic); 8325efea30fSAndrew Thompson scan_signal(ss); 8335efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 83468e8e04eSSam Leffler } 83568e8e04eSSam Leffler 83668e8e04eSSam Leffler static void 8375efea30fSAndrew Thompson scan_task(void *arg, int pending) 83868e8e04eSSam Leffler { 8395efea30fSAndrew Thompson #define ISCAN_REP (ISCAN_MINDWELL | ISCAN_DISCARD) 84068e8e04eSSam Leffler struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; 841b032f27cSSam Leffler struct ieee80211vap *vap = ss->ss_vap; 8425efea30fSAndrew Thompson struct ieee80211com *ic = ss->ss_ic; 84368e8e04eSSam Leffler struct ieee80211_channel *chan; 84468e8e04eSSam Leffler unsigned long maxdwell, scanend; 8455efea30fSAndrew Thompson int scandone = 0; 84668e8e04eSSam Leffler 8475efea30fSAndrew Thompson IEEE80211_LOCK(ic); 8485efea30fSAndrew Thompson if (vap == NULL || (ic->ic_flags & IEEE80211_F_SCAN) == 0 || 8495efea30fSAndrew Thompson (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT)) { 8505efea30fSAndrew Thompson /* Cancelled before we started */ 8515efea30fSAndrew Thompson goto done; 8525efea30fSAndrew Thompson } 853b032f27cSSam Leffler 8545efea30fSAndrew Thompson if (ss->ss_next == ss->ss_last) { 8555efea30fSAndrew Thompson IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 8565efea30fSAndrew Thompson "%s: no channels to scan\n", __func__); 8575efea30fSAndrew Thompson goto done; 8585efea30fSAndrew Thompson } 8595efea30fSAndrew Thompson 8605efea30fSAndrew Thompson if (vap->iv_opmode == IEEE80211_M_STA && 8615efea30fSAndrew Thompson vap->iv_state == IEEE80211_S_RUN) { 8625efea30fSAndrew Thompson if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { 8635efea30fSAndrew Thompson /* Enable station power save mode */ 8645efea30fSAndrew Thompson ieee80211_sta_pwrsave(vap, 1); 8655efea30fSAndrew Thompson /* 8665efea30fSAndrew Thompson * Use an 1ms delay so the null data frame has a chance 8675efea30fSAndrew Thompson * to go out. 8685efea30fSAndrew Thompson * XXX Should use M_TXCB mechanism to eliminate this. 8695efea30fSAndrew Thompson */ 8705efea30fSAndrew Thompson cv_timedwait(&SCAN_PRIVATE(ss)->ss_scan_cv, 8715efea30fSAndrew Thompson IEEE80211_LOCK_OBJ(ic), hz / 1000); 8725efea30fSAndrew Thompson if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) 8735efea30fSAndrew Thompson goto done; 8745efea30fSAndrew Thompson } 8755efea30fSAndrew Thompson } 8765efea30fSAndrew Thompson 8775efea30fSAndrew Thompson scanend = ticks + SCAN_PRIVATE(ss)->ss_duration; 8785efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 8795efea30fSAndrew Thompson ic->ic_scan_start(ic); /* notify driver */ 8805efea30fSAndrew Thompson IEEE80211_LOCK(ic); 8815efea30fSAndrew Thompson 8825efea30fSAndrew Thompson for (;;) { 88368e8e04eSSam Leffler scandone = (ss->ss_next >= ss->ss_last) || 88468e8e04eSSam Leffler (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; 8855efea30fSAndrew Thompson if (scandone || (ss->ss_flags & IEEE80211_SCAN_GOTPICK) || 8865efea30fSAndrew Thompson (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) || 8875efea30fSAndrew Thompson time_after(ticks + ss->ss_mindwell, scanend)) 8885efea30fSAndrew Thompson break; 8895efea30fSAndrew Thompson 89068e8e04eSSam Leffler chan = ss->ss_chans[ss->ss_next++]; 89168e8e04eSSam Leffler 89268e8e04eSSam Leffler /* 89368e8e04eSSam Leffler * Watch for truncation due to the scan end time. 89468e8e04eSSam Leffler */ 89568e8e04eSSam Leffler if (time_after(ticks + ss->ss_maxdwell, scanend)) 89668e8e04eSSam Leffler maxdwell = scanend - ticks; 89768e8e04eSSam Leffler else 89868e8e04eSSam Leffler maxdwell = ss->ss_maxdwell; 89968e8e04eSSam Leffler 900b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 901110a70e3SSam Leffler "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", 90268e8e04eSSam Leffler __func__, 90368e8e04eSSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 90468e8e04eSSam Leffler channel_type(ic->ic_curchan), 90568e8e04eSSam Leffler ieee80211_chan2ieee(ic, chan), channel_type(chan), 90668e8e04eSSam Leffler (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && 90768e8e04eSSam Leffler (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? 90868e8e04eSSam Leffler "active" : "passive", 909110a70e3SSam Leffler ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); 91068e8e04eSSam Leffler 91168e8e04eSSam Leffler /* 91268e8e04eSSam Leffler * Potentially change channel and phy mode. 91368e8e04eSSam Leffler */ 9145efea30fSAndrew Thompson ic->ic_curchan = chan; 9155efea30fSAndrew Thompson ic->ic_rt = ieee80211_get_ratetable(chan); 9165efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 9175efea30fSAndrew Thompson /* 9185efea30fSAndrew Thompson * Perform the channel change and scan unlocked so the driver 9195efea30fSAndrew Thompson * may sleep. Once set_channel returns the hardware has 9205efea30fSAndrew Thompson * completed the channel change. 9215efea30fSAndrew Thompson */ 9225efea30fSAndrew Thompson ic->ic_set_channel(ic); 92368e8e04eSSam Leffler 92468e8e04eSSam Leffler /* 92568e8e04eSSam Leffler * Scan curchan. Drivers for "intelligent hardware" 92668e8e04eSSam Leffler * override ic_scan_curchan to tell the device to do 92768e8e04eSSam Leffler * the work. Otherwise we manage the work outselves; 92868e8e04eSSam Leffler * sending a probe request (as needed), and arming the 92968e8e04eSSam Leffler * timeout to switch channels after maxdwell ticks. 9305efea30fSAndrew Thompson * 9315efea30fSAndrew Thompson * scan_curchan should only pause for the time required to 9325efea30fSAndrew Thompson * prepare/initiate the hardware for the scan (if at all), the 9335efea30fSAndrew Thompson * below condvar is used to sleep for the channels dwell time 9345efea30fSAndrew Thompson * and allows it to be signalled for abort. 93568e8e04eSSam Leffler */ 936b032f27cSSam Leffler ic->ic_scan_curchan(ss, maxdwell); 9375efea30fSAndrew Thompson IEEE80211_LOCK(ic); 93868e8e04eSSam Leffler 93968e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; 94068e8e04eSSam Leffler /* clear mindwell lock and initial channel change flush */ 94168e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; 9425efea30fSAndrew Thompson 9435efea30fSAndrew Thompson if ((SCAN_PRIVATE(ss)->ss_iflags & (ISCAN_CANCEL|ISCAN_ABORT))) 9445efea30fSAndrew Thompson continue; 9455efea30fSAndrew Thompson 9465efea30fSAndrew Thompson /* Wait to be signalled to scan the next channel */ 9475efea30fSAndrew Thompson cv_wait(&SCAN_PRIVATE(ss)->ss_scan_cv, IEEE80211_LOCK_OBJ(ic)); 9485efea30fSAndrew Thompson } 9495efea30fSAndrew Thompson if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_ABORT) 9505efea30fSAndrew Thompson goto done; 9515efea30fSAndrew Thompson 9525efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 95368e8e04eSSam Leffler ic->ic_scan_end(ic); /* notify driver */ 9545efea30fSAndrew Thompson IEEE80211_LOCK(ic); 9555efea30fSAndrew Thompson 95668e8e04eSSam Leffler /* 95768e8e04eSSam Leffler * Record scan complete time. Note that we also do 95868e8e04eSSam Leffler * this when canceled so any background scan will 95968e8e04eSSam Leffler * not be restarted for a while. 96068e8e04eSSam Leffler */ 96168e8e04eSSam Leffler if (scandone) 96268e8e04eSSam Leffler ic->ic_lastscan = ticks; 96368e8e04eSSam Leffler /* return to the bss channel */ 96468e8e04eSSam Leffler if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 9655efea30fSAndrew Thompson ic->ic_curchan != ic->ic_bsschan) { 9665efea30fSAndrew Thompson ieee80211_setupcurchan(ic, ic->ic_bsschan); 9675efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 9685efea30fSAndrew Thompson ic->ic_set_channel(ic); 9695efea30fSAndrew Thompson IEEE80211_LOCK(ic); 9705efea30fSAndrew Thompson } 97168e8e04eSSam Leffler /* clear internal flags and any indication of a pick */ 97268e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; 97368e8e04eSSam Leffler ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; 97468e8e04eSSam Leffler 97568e8e04eSSam Leffler /* 97668e8e04eSSam Leffler * If not canceled and scan completed, do post-processing. 97768e8e04eSSam Leffler * If the callback function returns 0, then it wants to 97868e8e04eSSam Leffler * continue/restart scanning. Unfortunately we needed to 97968e8e04eSSam Leffler * notify the driver to end the scan above to avoid having 98068e8e04eSSam Leffler * rx frames alter the scan candidate list. 98168e8e04eSSam Leffler */ 98268e8e04eSSam Leffler if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && 983b032f27cSSam Leffler !ss->ss_ops->scan_end(ss, vap) && 98468e8e04eSSam Leffler (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && 98568e8e04eSSam Leffler time_before(ticks + ss->ss_mindwell, scanend)) { 986b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 98768e8e04eSSam Leffler "%s: done, restart " 98868e8e04eSSam Leffler "[ticks %u, dwell min %lu scanend %lu]\n", 98968e8e04eSSam Leffler __func__, 99068e8e04eSSam Leffler ticks, ss->ss_mindwell, scanend); 99168e8e04eSSam Leffler ss->ss_next = 0; /* reset to begining */ 99268e8e04eSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 993b032f27cSSam Leffler vap->iv_stats.is_scan_active++; 99468e8e04eSSam Leffler else 995b032f27cSSam Leffler vap->iv_stats.is_scan_passive++; 99668e8e04eSSam Leffler 997b032f27cSSam Leffler ss->ss_ops->scan_restart(ss, vap); /* XXX? */ 9985efea30fSAndrew Thompson ieee80211_runtask(ic, &SCAN_PRIVATE(ss)->ss_scan_task); 9995efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 10005efea30fSAndrew Thompson return; 10015efea30fSAndrew Thompson } 10025efea30fSAndrew Thompson 100368e8e04eSSam Leffler /* past here, scandone is ``true'' if not in bg mode */ 100468e8e04eSSam Leffler if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) 100568e8e04eSSam Leffler scandone = 1; 100668e8e04eSSam Leffler 1007b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 10085efea30fSAndrew Thompson "%s: %s, [ticks %u, dwell min %lu scanend %lu]\n", 100968e8e04eSSam Leffler __func__, scandone ? "done" : "stopped", 101068e8e04eSSam Leffler ticks, ss->ss_mindwell, scanend); 101168e8e04eSSam Leffler 101268e8e04eSSam Leffler /* 101368e8e04eSSam Leffler * Clear the SCAN bit first in case frames are 101468e8e04eSSam Leffler * pending on the station power save queue. If 101568e8e04eSSam Leffler * we defer this then the dispatch of the frames 101668e8e04eSSam Leffler * may generate a request to cancel scanning. 101768e8e04eSSam Leffler */ 10185efea30fSAndrew Thompson done: 101968e8e04eSSam Leffler ic->ic_flags &= ~IEEE80211_F_SCAN; 102068e8e04eSSam Leffler /* 102168e8e04eSSam Leffler * Drop out of power save mode when a scan has 102268e8e04eSSam Leffler * completed. If this scan was prematurely terminated 102368e8e04eSSam Leffler * because it is a background scan then don't notify 102468e8e04eSSam Leffler * the ap; we'll either return to scanning after we 102568e8e04eSSam Leffler * receive the beacon frame or we'll drop out of power 102668e8e04eSSam Leffler * save mode because the beacon indicates we have frames 102768e8e04eSSam Leffler * waiting for us. 102868e8e04eSSam Leffler */ 102968e8e04eSSam Leffler if (scandone) { 1030b032f27cSSam Leffler ieee80211_sta_pwrsave(vap, 0); 103168e8e04eSSam Leffler if (ss->ss_next >= ss->ss_last) { 1032b032f27cSSam Leffler ieee80211_notify_scan_done(vap); 103368e8e04eSSam Leffler ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; 103468e8e04eSSam Leffler } 103568e8e04eSSam Leffler } 10365efea30fSAndrew Thompson SCAN_PRIVATE(ss)->ss_iflags &= ~(ISCAN_CANCEL|ISCAN_ABORT); 10375efea30fSAndrew Thompson ss->ss_flags &= ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); 10385efea30fSAndrew Thompson IEEE80211_UNLOCK(ic); 103968e8e04eSSam Leffler #undef ISCAN_REP 104068e8e04eSSam Leffler } 104168e8e04eSSam Leffler 104268e8e04eSSam Leffler #ifdef IEEE80211_DEBUG 104368e8e04eSSam Leffler static void 1044b032f27cSSam Leffler dump_country(const uint8_t *ie) 1045b032f27cSSam Leffler { 1046b032f27cSSam Leffler const struct ieee80211_country_ie *cie = 1047b032f27cSSam Leffler (const struct ieee80211_country_ie *) ie; 1048b032f27cSSam Leffler int i, nbands, schan, nchan; 1049b032f27cSSam Leffler 1050b032f27cSSam Leffler if (cie->len < 3) { 1051b032f27cSSam Leffler printf(" <bogus country ie, len %d>", cie->len); 1052b032f27cSSam Leffler return; 1053b032f27cSSam Leffler } 1054b032f27cSSam Leffler printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]); 1055b032f27cSSam Leffler nbands = (cie->len - 3) / sizeof(cie->band[0]); 1056b032f27cSSam Leffler for (i = 0; i < nbands; i++) { 1057b032f27cSSam Leffler schan = cie->band[i].schan; 1058b032f27cSSam Leffler nchan = cie->band[i].nchan; 1059b032f27cSSam Leffler if (nchan != 1) 1060b032f27cSSam Leffler printf(" %u-%u,%u", schan, schan + nchan-1, 1061b032f27cSSam Leffler cie->band[i].maxtxpwr); 1062b032f27cSSam Leffler else 1063b032f27cSSam Leffler printf(" %u,%u", schan, cie->band[i].maxtxpwr); 1064b032f27cSSam Leffler } 1065b032f27cSSam Leffler printf("]"); 1066b032f27cSSam Leffler } 1067b032f27cSSam Leffler 1068b032f27cSSam Leffler static void 106968e8e04eSSam Leffler dump_probe_beacon(uint8_t subtype, int isnew, 107068e8e04eSSam Leffler const uint8_t mac[IEEE80211_ADDR_LEN], 1071b032f27cSSam Leffler const struct ieee80211_scanparams *sp, int rssi) 107268e8e04eSSam Leffler { 107368e8e04eSSam Leffler 107468e8e04eSSam Leffler printf("[%s] %s%s on chan %u (bss chan %u) ", 107568e8e04eSSam Leffler ether_sprintf(mac), isnew ? "new " : "", 107668e8e04eSSam Leffler ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], 1077b032f27cSSam Leffler sp->chan, sp->bchan); 107868e8e04eSSam Leffler ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); 1079b032f27cSSam Leffler printf(" rssi %d\n", rssi); 108068e8e04eSSam Leffler 108168e8e04eSSam Leffler if (isnew) { 108268e8e04eSSam Leffler printf("[%s] caps 0x%x bintval %u erp 0x%x", 108368e8e04eSSam Leffler ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp); 1084b032f27cSSam Leffler if (sp->country != NULL) 1085b032f27cSSam Leffler dump_country(sp->country); 108668e8e04eSSam Leffler printf("\n"); 108768e8e04eSSam Leffler } 108868e8e04eSSam Leffler } 108968e8e04eSSam Leffler #endif /* IEEE80211_DEBUG */ 109068e8e04eSSam Leffler 109168e8e04eSSam Leffler /* 109268e8e04eSSam Leffler * Process a beacon or probe response frame. 109368e8e04eSSam Leffler */ 109468e8e04eSSam Leffler void 1095b032f27cSSam Leffler ieee80211_add_scan(struct ieee80211vap *vap, 109668e8e04eSSam Leffler const struct ieee80211_scanparams *sp, 109768e8e04eSSam Leffler const struct ieee80211_frame *wh, 109868e8e04eSSam Leffler int subtype, int rssi, int noise, int rstamp) 109968e8e04eSSam Leffler { 1100b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 110168e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 110268e8e04eSSam Leffler 1103b032f27cSSam Leffler /* XXX locking */ 110468e8e04eSSam Leffler /* 110568e8e04eSSam Leffler * Frames received during startup are discarded to avoid 110668e8e04eSSam Leffler * using scan state setup on the initial entry to the timer 110768e8e04eSSam Leffler * callback. This can occur because the device may enable 110868e8e04eSSam Leffler * rx prior to our doing the initial channel change in the 1109b032f27cSSam Leffler * timer routine. 111068e8e04eSSam Leffler */ 111168e8e04eSSam Leffler if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) 111268e8e04eSSam Leffler return; 111368e8e04eSSam Leffler #ifdef IEEE80211_DEBUG 1114b032f27cSSam Leffler if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) 1115b032f27cSSam Leffler dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); 111668e8e04eSSam Leffler #endif 111768e8e04eSSam Leffler if (ss->ss_ops != NULL && 111868e8e04eSSam Leffler ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) { 111968e8e04eSSam Leffler /* 112068e8e04eSSam Leffler * If we've reached the min dwell time terminate 112168e8e04eSSam Leffler * the timer so we'll switch to the next channel. 112268e8e04eSSam Leffler */ 112368e8e04eSSam Leffler if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && 112468e8e04eSSam Leffler time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { 1125b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 112668e8e04eSSam Leffler "%s: chan %3d%c min dwell met (%u > %lu)\n", 112768e8e04eSSam Leffler __func__, 112868e8e04eSSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 112968e8e04eSSam Leffler channel_type(ic->ic_curchan), 113068e8e04eSSam Leffler ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); 113168e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; 113268e8e04eSSam Leffler /* 113368e8e04eSSam Leffler * NB: trigger at next clock tick or wait for the 1134b032f27cSSam Leffler * hardware. 113568e8e04eSSam Leffler */ 1136b032f27cSSam Leffler ic->ic_scan_mindwell(ss); 113768e8e04eSSam Leffler } 113868e8e04eSSam Leffler } 113968e8e04eSSam Leffler } 114068e8e04eSSam Leffler 114168e8e04eSSam Leffler /* 114268e8e04eSSam Leffler * Timeout/age scan cache entries; called from sta timeout 114368e8e04eSSam Leffler * timer (XXX should be self-contained). 114468e8e04eSSam Leffler */ 114568e8e04eSSam Leffler void 114668e8e04eSSam Leffler ieee80211_scan_timeout(struct ieee80211com *ic) 114768e8e04eSSam Leffler { 114868e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 114968e8e04eSSam Leffler 115068e8e04eSSam Leffler if (ss->ss_ops != NULL) 115168e8e04eSSam Leffler ss->ss_ops->scan_age(ss); 115268e8e04eSSam Leffler } 115368e8e04eSSam Leffler 115468e8e04eSSam Leffler /* 115568e8e04eSSam Leffler * Mark a scan cache entry after a successful associate. 115668e8e04eSSam Leffler */ 115768e8e04eSSam Leffler void 1158b032f27cSSam Leffler ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[]) 115968e8e04eSSam Leffler { 1160b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 116168e8e04eSSam Leffler 116268e8e04eSSam Leffler if (ss->ss_ops != NULL) { 1163b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, 116468e8e04eSSam Leffler mac, "%s", __func__); 116568e8e04eSSam Leffler ss->ss_ops->scan_assoc_success(ss, mac); 116668e8e04eSSam Leffler } 116768e8e04eSSam Leffler } 116868e8e04eSSam Leffler 116968e8e04eSSam Leffler /* 117068e8e04eSSam Leffler * Demerit a scan cache entry after failing to associate. 117168e8e04eSSam Leffler */ 117268e8e04eSSam Leffler void 1173b032f27cSSam Leffler ieee80211_scan_assoc_fail(struct ieee80211vap *vap, 117468e8e04eSSam Leffler const uint8_t mac[], int reason) 117568e8e04eSSam Leffler { 1176b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 117768e8e04eSSam Leffler 117868e8e04eSSam Leffler if (ss->ss_ops != NULL) { 1179b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac, 118068e8e04eSSam Leffler "%s: reason %u", __func__, reason); 118168e8e04eSSam Leffler ss->ss_ops->scan_assoc_fail(ss, mac, reason); 118268e8e04eSSam Leffler } 118368e8e04eSSam Leffler } 118468e8e04eSSam Leffler 118568e8e04eSSam Leffler /* 118668e8e04eSSam Leffler * Iterate over the contents of the scan cache. 118768e8e04eSSam Leffler */ 118868e8e04eSSam Leffler void 1189b032f27cSSam Leffler ieee80211_scan_iterate(struct ieee80211vap *vap, 119068e8e04eSSam Leffler ieee80211_scan_iter_func *f, void *arg) 119168e8e04eSSam Leffler { 1192b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 119368e8e04eSSam Leffler 119468e8e04eSSam Leffler if (ss->ss_ops != NULL) 119568e8e04eSSam Leffler ss->ss_ops->scan_iterate(ss, f, arg); 119668e8e04eSSam Leffler } 119768e8e04eSSam Leffler 119868e8e04eSSam Leffler /* 119968e8e04eSSam Leffler * Flush the contents of the scan cache. 120068e8e04eSSam Leffler */ 120168e8e04eSSam Leffler void 1202b032f27cSSam Leffler ieee80211_scan_flush(struct ieee80211vap *vap) 1203b032f27cSSam Leffler { 1204b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 1205b032f27cSSam Leffler 1206b032f27cSSam Leffler if (ss->ss_ops != NULL && ss->ss_vap == vap) { 1207b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__); 1208b032f27cSSam Leffler ss->ss_ops->scan_flush(ss); 1209b032f27cSSam Leffler } 1210b032f27cSSam Leffler } 1211b032f27cSSam Leffler 1212b032f27cSSam Leffler /* 1213b032f27cSSam Leffler * Check the scan cache for an ap/channel to use; if that 1214b032f27cSSam Leffler * fails then kick off a new scan. 1215b032f27cSSam Leffler */ 1216b032f27cSSam Leffler struct ieee80211_channel * 1217b032f27cSSam Leffler ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags) 121868e8e04eSSam Leffler { 121968e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 122068e8e04eSSam Leffler 1221b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 1222b032f27cSSam Leffler 1223b032f27cSSam Leffler if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) { 1224b032f27cSSam Leffler /* XXX printf? */ 1225b032f27cSSam Leffler return NULL; 122668e8e04eSSam Leffler } 1227b032f27cSSam Leffler if (ss->ss_ops->scan_pickchan == NULL) { 1228b032f27cSSam Leffler IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, 1229b032f27cSSam Leffler "%s: scan module does not support picking a channel, " 1230b032f27cSSam Leffler "opmode %s\n", __func__, ss->ss_vap->iv_opmode); 1231b032f27cSSam Leffler return NULL; 1232b032f27cSSam Leffler } 1233b032f27cSSam Leffler return ss->ss_ops->scan_pickchan(ss, flags); 123468e8e04eSSam Leffler } 1235