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> 3668e8e04eSSam Leffler #include <sys/kernel.h> 3768e8e04eSSam Leffler 3868e8e04eSSam Leffler #include <sys/socket.h> 3968e8e04eSSam Leffler 4068e8e04eSSam Leffler #include <net/if.h> 4168e8e04eSSam Leffler #include <net/if_media.h> 4268e8e04eSSam Leffler #include <net/ethernet.h> 4368e8e04eSSam Leffler 4468e8e04eSSam Leffler #include <net80211/ieee80211_var.h> 4568e8e04eSSam Leffler 4668e8e04eSSam Leffler #include <net/bpf.h> 4768e8e04eSSam Leffler 4868e8e04eSSam Leffler struct scan_state { 4968e8e04eSSam Leffler struct ieee80211_scan_state base; /* public state */ 5068e8e04eSSam Leffler 5168e8e04eSSam Leffler u_int ss_iflags; /* flags used internally */ 5268e8e04eSSam Leffler #define ISCAN_MINDWELL 0x0001 /* min dwell time reached */ 5368e8e04eSSam Leffler #define ISCAN_DISCARD 0x0002 /* discard rx'd frames */ 5468e8e04eSSam Leffler #define ISCAN_CANCEL 0x0004 /* cancel current scan */ 5568e8e04eSSam Leffler #define ISCAN_START 0x0008 /* 1st time through next_scan */ 5668e8e04eSSam Leffler unsigned long ss_chanmindwell; /* min dwell on curchan */ 5768e8e04eSSam Leffler unsigned long ss_scanend; /* time scan must stop */ 5868e8e04eSSam Leffler u_int ss_duration; /* duration for next scan */ 5968e8e04eSSam Leffler struct callout ss_scan_timer; /* scan timer */ 6068e8e04eSSam Leffler }; 6168e8e04eSSam Leffler #define SCAN_PRIVATE(ss) ((struct scan_state *) ss) 6268e8e04eSSam Leffler 6368e8e04eSSam Leffler /* 6468e8e04eSSam Leffler * Amount of time to go off-channel during a background 6568e8e04eSSam Leffler * scan. This value should be large enough to catch most 6668e8e04eSSam Leffler * ap's but short enough that we can return on-channel 6768e8e04eSSam Leffler * before our listen interval expires. 6868e8e04eSSam Leffler * 6968e8e04eSSam Leffler * XXX tunable 7068e8e04eSSam Leffler * XXX check against configured listen interval 7168e8e04eSSam Leffler */ 7268e8e04eSSam Leffler #define IEEE80211_SCAN_OFFCHANNEL msecs_to_ticks(150) 7368e8e04eSSam Leffler 7468e8e04eSSam Leffler /* 7568e8e04eSSam Leffler * Roaming-related defaults. RSSI thresholds are as returned by the 7682c990a4SSam Leffler * driver (.5dBm). Transmit rate thresholds are IEEE rate codes (i.e 77b032f27cSSam Leffler * .5M units) or MCS. 7868e8e04eSSam Leffler */ 7982c990a4SSam Leffler /* rssi thresholds */ 8082c990a4SSam Leffler #define ROAM_RSSI_11A_DEFAULT 14 /* 11a bss */ 8182c990a4SSam Leffler #define ROAM_RSSI_11B_DEFAULT 14 /* 11b bss */ 8282c990a4SSam Leffler #define ROAM_RSSI_11BONLY_DEFAULT 14 /* 11b-only bss */ 8382c990a4SSam Leffler /* transmit rate thresholds */ 8482c990a4SSam Leffler #define ROAM_RATE_11A_DEFAULT 2*12 /* 11a bss */ 8582c990a4SSam Leffler #define ROAM_RATE_11B_DEFAULT 2*5 /* 11b bss */ 8682c990a4SSam Leffler #define ROAM_RATE_11BONLY_DEFAULT 2*1 /* 11b-only bss */ 876a76ae21SSam Leffler #define ROAM_RATE_HALF_DEFAULT 2*6 /* half-width 11a/g bss */ 886a76ae21SSam Leffler #define ROAM_RATE_QUARTER_DEFAULT 2*3 /* quarter-width 11a/g bss */ 8982c990a4SSam Leffler #define ROAM_MCS_11N_DEFAULT (1 | IEEE80211_RATE_MCS) /* 11n bss */ 9068e8e04eSSam Leffler 9168e8e04eSSam Leffler static void scan_restart_pwrsav(void *); 92b032f27cSSam Leffler static void scan_curchan(struct ieee80211_scan_state *, unsigned long); 93b032f27cSSam Leffler static void scan_mindwell(struct ieee80211_scan_state *); 9468e8e04eSSam Leffler static void scan_next(void *); 9568e8e04eSSam Leffler 9668e8e04eSSam Leffler MALLOC_DEFINE(M_80211_SCAN, "80211scan", "802.11 scan state"); 9768e8e04eSSam Leffler 9868e8e04eSSam Leffler void 9968e8e04eSSam Leffler ieee80211_scan_attach(struct ieee80211com *ic) 10068e8e04eSSam Leffler { 10168e8e04eSSam Leffler struct scan_state *ss; 10268e8e04eSSam Leffler 103e2126decSSam Leffler ss = (struct scan_state *) malloc(sizeof(struct scan_state), 10468e8e04eSSam Leffler M_80211_SCAN, M_NOWAIT | M_ZERO); 10568e8e04eSSam Leffler if (ss == NULL) { 10668e8e04eSSam Leffler ic->ic_scan = NULL; 10768e8e04eSSam Leffler return; 10868e8e04eSSam Leffler } 109978359b3SSam Leffler callout_init_mtx(&ss->ss_scan_timer, IEEE80211_LOCK_OBJ(ic), 0); 11068e8e04eSSam Leffler ic->ic_scan = &ss->base; 11168e8e04eSSam Leffler 11268e8e04eSSam Leffler ic->ic_scan_curchan = scan_curchan; 11368e8e04eSSam Leffler ic->ic_scan_mindwell = scan_mindwell; 11468e8e04eSSam Leffler } 11568e8e04eSSam Leffler 11668e8e04eSSam Leffler void 11768e8e04eSSam Leffler ieee80211_scan_detach(struct ieee80211com *ic) 11868e8e04eSSam Leffler { 11968e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 12068e8e04eSSam Leffler 12168e8e04eSSam Leffler if (ss != NULL) { 12268e8e04eSSam Leffler callout_drain(&SCAN_PRIVATE(ss)->ss_scan_timer); 12368e8e04eSSam Leffler if (ss->ss_ops != NULL) { 12468e8e04eSSam Leffler ss->ss_ops->scan_detach(ss); 12568e8e04eSSam Leffler ss->ss_ops = NULL; 12668e8e04eSSam Leffler } 12768e8e04eSSam Leffler ic->ic_flags &= ~IEEE80211_F_SCAN; 12868e8e04eSSam Leffler ic->ic_scan = NULL; 129e2126decSSam Leffler free(SCAN_PRIVATE(ss), M_80211_SCAN); 13068e8e04eSSam Leffler } 13168e8e04eSSam Leffler } 13268e8e04eSSam Leffler 13382c990a4SSam Leffler static const struct ieee80211_roamparam defroam[IEEE80211_MODE_MAX] = { 13482c990a4SSam Leffler [IEEE80211_MODE_11A] = { .rssi = ROAM_RSSI_11A_DEFAULT, 13582c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 13682c990a4SSam Leffler [IEEE80211_MODE_11G] = { .rssi = ROAM_RSSI_11B_DEFAULT, 13782c990a4SSam Leffler .rate = ROAM_RATE_11B_DEFAULT }, 13882c990a4SSam Leffler [IEEE80211_MODE_11B] = { .rssi = ROAM_RSSI_11BONLY_DEFAULT, 13982c990a4SSam Leffler .rate = ROAM_RATE_11BONLY_DEFAULT }, 14082c990a4SSam Leffler [IEEE80211_MODE_TURBO_A]= { .rssi = ROAM_RSSI_11A_DEFAULT, 14182c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 14282c990a4SSam Leffler [IEEE80211_MODE_TURBO_G]= { .rssi = ROAM_RSSI_11A_DEFAULT, 14382c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 14482c990a4SSam Leffler [IEEE80211_MODE_STURBO_A]={ .rssi = ROAM_RSSI_11A_DEFAULT, 14582c990a4SSam Leffler .rate = ROAM_RATE_11A_DEFAULT }, 1466a76ae21SSam Leffler [IEEE80211_MODE_HALF] = { .rssi = ROAM_RSSI_11A_DEFAULT, 1476a76ae21SSam Leffler .rate = ROAM_RATE_HALF_DEFAULT }, 1486a76ae21SSam Leffler [IEEE80211_MODE_QUARTER]= { .rssi = ROAM_RSSI_11A_DEFAULT, 1496a76ae21SSam Leffler .rate = ROAM_RATE_QUARTER_DEFAULT }, 15082c990a4SSam Leffler [IEEE80211_MODE_11NA] = { .rssi = ROAM_RSSI_11A_DEFAULT, 15182c990a4SSam Leffler .rate = ROAM_MCS_11N_DEFAULT }, 15282c990a4SSam Leffler [IEEE80211_MODE_11NG] = { .rssi = ROAM_RSSI_11B_DEFAULT, 15382c990a4SSam Leffler .rate = ROAM_MCS_11N_DEFAULT }, 15482c990a4SSam Leffler }; 155b032f27cSSam Leffler 156b032f27cSSam Leffler void 157b032f27cSSam Leffler ieee80211_scan_vattach(struct ieee80211vap *vap) 158b032f27cSSam Leffler { 159b032f27cSSam Leffler vap->iv_bgscanidle = (IEEE80211_BGSCAN_IDLE_DEFAULT*1000)/hz; 160b032f27cSSam Leffler vap->iv_bgscanintvl = IEEE80211_BGSCAN_INTVAL_DEFAULT*hz; 161b032f27cSSam Leffler vap->iv_scanvalid = IEEE80211_SCAN_VALID_DEFAULT*hz; 162b032f27cSSam Leffler 163b032f27cSSam Leffler vap->iv_roaming = IEEE80211_ROAMING_AUTO; 16482c990a4SSam Leffler memcpy(vap->iv_roamparms, defroam, sizeof(defroam)); 165b032f27cSSam Leffler } 166b032f27cSSam Leffler 167b032f27cSSam Leffler void 168b032f27cSSam Leffler ieee80211_scan_vdetach(struct ieee80211vap *vap) 169b032f27cSSam Leffler { 170b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 171b032f27cSSam Leffler struct ieee80211_scan_state *ss; 172b032f27cSSam Leffler 173b032f27cSSam Leffler IEEE80211_LOCK(ic); 174b032f27cSSam Leffler ss = ic->ic_scan; 175b032f27cSSam Leffler if (ss != NULL && ss->ss_vap == vap) { 176b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 177b032f27cSSam Leffler /* XXX callout_drain */ 178b032f27cSSam Leffler callout_stop(&SCAN_PRIVATE(ss)->ss_scan_timer); 179b032f27cSSam Leffler ic->ic_flags &= ~IEEE80211_F_SCAN; 180b032f27cSSam Leffler } 181b032f27cSSam Leffler if (ss->ss_ops != NULL) { 182b032f27cSSam Leffler ss->ss_ops->scan_detach(ss); 183b032f27cSSam Leffler ss->ss_ops = NULL; 184b032f27cSSam Leffler } 185b032f27cSSam Leffler ss->ss_vap = NULL; 186b032f27cSSam Leffler } 187b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 188b032f27cSSam Leffler } 189b032f27cSSam Leffler 19068e8e04eSSam Leffler /* 19168e8e04eSSam Leffler * Simple-minded scanner module support. 19268e8e04eSSam Leffler */ 193b032f27cSSam Leffler static const char *scan_modnames[IEEE80211_OPMODE_MAX] = { 19468e8e04eSSam Leffler "wlan_scan_sta", /* IEEE80211_M_IBSS */ 19568e8e04eSSam Leffler "wlan_scan_sta", /* IEEE80211_M_STA */ 19668e8e04eSSam Leffler "wlan_scan_wds", /* IEEE80211_M_WDS */ 19768e8e04eSSam Leffler "wlan_scan_sta", /* IEEE80211_M_AHDEMO */ 19868e8e04eSSam Leffler "wlan_scan_ap", /* IEEE80211_M_HOSTAP */ 19968e8e04eSSam Leffler "wlan_scan_monitor", /* IEEE80211_M_MONITOR */ 20068e8e04eSSam Leffler }; 201b032f27cSSam Leffler static const struct ieee80211_scanner *scanners[IEEE80211_OPMODE_MAX]; 20268e8e04eSSam Leffler 20368e8e04eSSam Leffler const struct ieee80211_scanner * 20468e8e04eSSam Leffler ieee80211_scanner_get(enum ieee80211_opmode mode) 20568e8e04eSSam Leffler { 206b032f27cSSam Leffler if (mode >= IEEE80211_OPMODE_MAX) 20768e8e04eSSam Leffler return NULL; 208b032f27cSSam Leffler if (scanners[mode] == NULL) 20968e8e04eSSam Leffler ieee80211_load_module(scan_modnames[mode]); 21068e8e04eSSam Leffler return scanners[mode]; 21168e8e04eSSam Leffler } 21268e8e04eSSam Leffler 21368e8e04eSSam Leffler void 21468e8e04eSSam Leffler ieee80211_scanner_register(enum ieee80211_opmode mode, 21568e8e04eSSam Leffler const struct ieee80211_scanner *scan) 21668e8e04eSSam Leffler { 217b032f27cSSam Leffler if (mode >= IEEE80211_OPMODE_MAX) 21868e8e04eSSam Leffler return; 21968e8e04eSSam Leffler scanners[mode] = scan; 22068e8e04eSSam Leffler } 22168e8e04eSSam Leffler 22268e8e04eSSam Leffler void 22368e8e04eSSam Leffler ieee80211_scanner_unregister(enum ieee80211_opmode mode, 22468e8e04eSSam Leffler const struct ieee80211_scanner *scan) 22568e8e04eSSam Leffler { 226b032f27cSSam Leffler if (mode >= IEEE80211_OPMODE_MAX) 22768e8e04eSSam Leffler return; 22868e8e04eSSam Leffler if (scanners[mode] == scan) 22968e8e04eSSam Leffler scanners[mode] = NULL; 23068e8e04eSSam Leffler } 23168e8e04eSSam Leffler 23268e8e04eSSam Leffler void 23368e8e04eSSam Leffler ieee80211_scanner_unregister_all(const struct ieee80211_scanner *scan) 23468e8e04eSSam Leffler { 23568e8e04eSSam Leffler int m; 23668e8e04eSSam Leffler 237b032f27cSSam Leffler for (m = 0; m < IEEE80211_OPMODE_MAX; m++) 23868e8e04eSSam Leffler if (scanners[m] == scan) 23968e8e04eSSam Leffler scanners[m] = NULL; 24068e8e04eSSam Leffler } 24168e8e04eSSam Leffler 24268e8e04eSSam Leffler /* 24368e8e04eSSam Leffler * Update common scanner state to reflect the current 24468e8e04eSSam Leffler * operating mode. This is called when the state machine 24568e8e04eSSam Leffler * is transitioned to RUN state w/o scanning--e.g. when 24668e8e04eSSam Leffler * operating in monitor mode. The purpose of this is to 24768e8e04eSSam Leffler * ensure later callbacks find ss_ops set to properly 24868e8e04eSSam Leffler * reflect current operating mode. 24968e8e04eSSam Leffler */ 250b032f27cSSam Leffler static void 251b032f27cSSam Leffler scan_update_locked(struct ieee80211vap *vap, 252b032f27cSSam Leffler const struct ieee80211_scanner *scan) 25368e8e04eSSam Leffler { 254b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 25568e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 25668e8e04eSSam Leffler 257b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 258b032f27cSSam Leffler 259b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 260b032f27cSSam Leffler if (ss->ss_vap != vap || ss->ss_ops != scan) { 261b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 262b032f27cSSam Leffler "%s: current scanner is <%s:%s>, switch to <%s:%s>\n", 263b032f27cSSam Leffler __func__, 264b032f27cSSam Leffler ss->ss_vap != NULL ? 265b032f27cSSam Leffler ss->ss_vap->iv_ifp->if_xname : "none", 266b032f27cSSam Leffler ss->ss_vap != NULL ? 267b032f27cSSam Leffler ieee80211_opmode_name[ss->ss_vap->iv_opmode] : "none", 268b032f27cSSam Leffler vap->iv_ifp->if_xname, 269b032f27cSSam Leffler ieee80211_opmode_name[vap->iv_opmode]); 27068e8e04eSSam Leffler } 271b032f27cSSam Leffler #endif 272b032f27cSSam Leffler ss->ss_vap = vap; 27368e8e04eSSam Leffler if (ss->ss_ops != scan) { 274b032f27cSSam Leffler /* 275b032f27cSSam Leffler * Switch scanners; detach old, attach new. Special 276b032f27cSSam Leffler * case where a single scan module implements multiple 277b032f27cSSam Leffler * policies by using different scan ops but a common 278b032f27cSSam Leffler * core. We assume if the old and new attach methods 279b032f27cSSam Leffler * are identical then it's ok to just change ss_ops 280b032f27cSSam Leffler * and not flush the internal state of the module. 281b032f27cSSam Leffler */ 282b032f27cSSam Leffler if (scan == NULL || ss->ss_ops == NULL || 283b032f27cSSam Leffler ss->ss_ops->scan_attach != scan->scan_attach) { 28468e8e04eSSam Leffler if (ss->ss_ops != NULL) 28568e8e04eSSam Leffler ss->ss_ops->scan_detach(ss); 28668e8e04eSSam Leffler if (scan != NULL && !scan->scan_attach(ss)) { 28768e8e04eSSam Leffler /* XXX attach failure */ 28868e8e04eSSam Leffler /* XXX stat+msg */ 289b032f27cSSam Leffler scan = NULL; 290b032f27cSSam Leffler } 291b032f27cSSam Leffler } 29268e8e04eSSam Leffler ss->ss_ops = scan; 29368e8e04eSSam Leffler } 29468e8e04eSSam Leffler } 29568e8e04eSSam Leffler 29668e8e04eSSam Leffler static void 29768e8e04eSSam Leffler change_channel(struct ieee80211com *ic, 29868e8e04eSSam Leffler struct ieee80211_channel *chan) 29968e8e04eSSam Leffler { 30068e8e04eSSam Leffler ic->ic_curchan = chan; 30168e8e04eSSam Leffler ic->ic_set_channel(ic); 30268e8e04eSSam Leffler } 30368e8e04eSSam Leffler 30468e8e04eSSam Leffler static char 30568e8e04eSSam Leffler channel_type(const struct ieee80211_channel *c) 30668e8e04eSSam Leffler { 30768e8e04eSSam Leffler if (IEEE80211_IS_CHAN_ST(c)) 30868e8e04eSSam Leffler return 'S'; 30968e8e04eSSam Leffler if (IEEE80211_IS_CHAN_108A(c)) 31068e8e04eSSam Leffler return 'T'; 31168e8e04eSSam Leffler if (IEEE80211_IS_CHAN_108G(c)) 31268e8e04eSSam Leffler return 'G'; 31368e8e04eSSam Leffler if (IEEE80211_IS_CHAN_HT(c)) 31468e8e04eSSam Leffler return 'n'; 31568e8e04eSSam Leffler if (IEEE80211_IS_CHAN_A(c)) 31668e8e04eSSam Leffler return 'a'; 31768e8e04eSSam Leffler if (IEEE80211_IS_CHAN_ANYG(c)) 31868e8e04eSSam Leffler return 'g'; 31968e8e04eSSam Leffler if (IEEE80211_IS_CHAN_B(c)) 32068e8e04eSSam Leffler return 'b'; 32168e8e04eSSam Leffler return 'f'; 32268e8e04eSSam Leffler } 32368e8e04eSSam Leffler 32468e8e04eSSam Leffler void 32568e8e04eSSam Leffler ieee80211_scan_dump_channels(const struct ieee80211_scan_state *ss) 32668e8e04eSSam Leffler { 327b032f27cSSam Leffler struct ieee80211com *ic = ss->ss_vap->iv_ic; 32868e8e04eSSam Leffler const char *sep; 32968e8e04eSSam Leffler int i; 33068e8e04eSSam Leffler 33168e8e04eSSam Leffler sep = ""; 33268e8e04eSSam Leffler for (i = ss->ss_next; i < ss->ss_last; i++) { 33368e8e04eSSam Leffler const struct ieee80211_channel *c = ss->ss_chans[i]; 33468e8e04eSSam Leffler 33568e8e04eSSam Leffler printf("%s%u%c", sep, ieee80211_chan2ieee(ic, c), 33668e8e04eSSam Leffler channel_type(c)); 33768e8e04eSSam Leffler sep = ", "; 33868e8e04eSSam Leffler } 33968e8e04eSSam Leffler } 34068e8e04eSSam Leffler 341b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 342b032f27cSSam Leffler static void 343b032f27cSSam Leffler scan_dump(struct ieee80211_scan_state *ss) 344b032f27cSSam Leffler { 345b032f27cSSam Leffler struct ieee80211vap *vap = ss->ss_vap; 346b032f27cSSam Leffler 347b032f27cSSam Leffler if_printf(vap->iv_ifp, "scan set "); 348b032f27cSSam Leffler ieee80211_scan_dump_channels(ss); 349110a70e3SSam Leffler printf(" dwell min %lums max %lums\n", 350110a70e3SSam Leffler ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(ss->ss_maxdwell)); 351b032f27cSSam Leffler } 352b032f27cSSam Leffler #endif /* IEEE80211_DEBUG */ 353b032f27cSSam Leffler 35468e8e04eSSam Leffler /* 35568e8e04eSSam Leffler * Enable station power save mode and start/restart the scanning thread. 35668e8e04eSSam Leffler */ 35768e8e04eSSam Leffler static void 35868e8e04eSSam Leffler scan_restart_pwrsav(void *arg) 35968e8e04eSSam Leffler { 36068e8e04eSSam Leffler struct scan_state *ss = (struct scan_state *) arg; 361b032f27cSSam Leffler struct ieee80211vap *vap = ss->base.ss_vap; 362b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 363b032f27cSSam Leffler int ticksdelay; 36468e8e04eSSam Leffler 365b032f27cSSam Leffler ieee80211_sta_pwrsave(vap, 1); 36668e8e04eSSam Leffler /* 367b032f27cSSam Leffler * Use an initial 1ms delay so the null 36868e8e04eSSam Leffler * data frame has a chance to go out. 36968e8e04eSSam Leffler * XXX 1ms is a lot, better to trigger scan 37068e8e04eSSam Leffler * on tx complete. 37168e8e04eSSam Leffler */ 372b032f27cSSam Leffler ticksdelay = msecs_to_ticks(1); 373b032f27cSSam Leffler if (ticksdelay < 1) 374b032f27cSSam Leffler ticksdelay = 1; 37568e8e04eSSam Leffler ic->ic_scan_start(ic); /* notify driver */ 376b032f27cSSam Leffler ss->ss_scanend = ticks + ticksdelay + ss->ss_duration; 37768e8e04eSSam Leffler ss->ss_iflags |= ISCAN_START; 378b032f27cSSam Leffler callout_reset(&ss->ss_scan_timer, ticksdelay, scan_next, ss); 37968e8e04eSSam Leffler } 38068e8e04eSSam Leffler 38168e8e04eSSam Leffler /* 38268e8e04eSSam Leffler * Start/restart scanning. If we're operating in station mode 38368e8e04eSSam Leffler * and associated notify the ap we're going into power save mode 38468e8e04eSSam Leffler * and schedule a callback to initiate the work (where there's a 38568e8e04eSSam Leffler * better context for doing the work). Otherwise, start the scan 38668e8e04eSSam Leffler * directly. 38768e8e04eSSam Leffler */ 38868e8e04eSSam Leffler static int 38968e8e04eSSam Leffler scan_restart(struct scan_state *ss, u_int duration) 39068e8e04eSSam Leffler { 391b032f27cSSam Leffler struct ieee80211vap *vap = ss->base.ss_vap; 392b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 39368e8e04eSSam Leffler int defer = 0; 39468e8e04eSSam Leffler 39568e8e04eSSam Leffler if (ss->base.ss_next == ss->base.ss_last) { 396b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 39768e8e04eSSam Leffler "%s: no channels to scan\n", __func__); 39868e8e04eSSam Leffler return 0; 39968e8e04eSSam Leffler } 400b032f27cSSam Leffler if (vap->iv_opmode == IEEE80211_M_STA && 401b032f27cSSam Leffler vap->iv_state == IEEE80211_S_RUN) { 402b032f27cSSam Leffler if ((vap->iv_bss->ni_flags & IEEE80211_NODE_PWR_MGT) == 0) { 40368e8e04eSSam Leffler /* 40468e8e04eSSam Leffler * Initiate power save before going off-channel. 40568e8e04eSSam Leffler * Note that we cannot do this directly because 40668e8e04eSSam Leffler * of locking issues; instead we defer it to a 40768e8e04eSSam Leffler * tasklet. 40868e8e04eSSam Leffler */ 40968e8e04eSSam Leffler ss->ss_duration = duration; 41068e8e04eSSam Leffler defer = 1; 41168e8e04eSSam Leffler } 41268e8e04eSSam Leffler } 41368e8e04eSSam Leffler 41468e8e04eSSam Leffler if (!defer) { 41568e8e04eSSam Leffler ic->ic_scan_start(ic); /* notify driver */ 41668e8e04eSSam Leffler ss->ss_scanend = ticks + duration; 41768e8e04eSSam Leffler ss->ss_iflags |= ISCAN_START; 41868e8e04eSSam Leffler callout_reset(&ss->ss_scan_timer, 0, scan_next, ss); 41968e8e04eSSam Leffler } else 42068e8e04eSSam Leffler scan_restart_pwrsav(ss); 42168e8e04eSSam Leffler return 1; 42268e8e04eSSam Leffler } 42368e8e04eSSam Leffler 42468e8e04eSSam Leffler static void 425b032f27cSSam Leffler copy_ssid(struct ieee80211vap *vap, struct ieee80211_scan_state *ss, 42668e8e04eSSam Leffler int nssid, const struct ieee80211_scan_ssid ssids[]) 42768e8e04eSSam Leffler { 42868e8e04eSSam Leffler if (nssid > IEEE80211_SCAN_MAX_SSID) { 42968e8e04eSSam Leffler /* XXX printf */ 430b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 43168e8e04eSSam Leffler "%s: too many ssid %d, ignoring all of them\n", 43268e8e04eSSam Leffler __func__, nssid); 43368e8e04eSSam Leffler return; 43468e8e04eSSam Leffler } 43568e8e04eSSam Leffler memcpy(ss->ss_ssid, ssids, nssid * sizeof(ssids[0])); 43668e8e04eSSam Leffler ss->ss_nssid = nssid; 43768e8e04eSSam Leffler } 43868e8e04eSSam Leffler 43968e8e04eSSam Leffler /* 44068e8e04eSSam Leffler * Start a scan unless one is already going. 44168e8e04eSSam Leffler */ 442b032f27cSSam Leffler static int 443b032f27cSSam Leffler start_scan_locked(const struct ieee80211_scanner *scan, 444b032f27cSSam Leffler struct ieee80211vap *vap, int flags, u_int duration, 445b032f27cSSam Leffler u_int mindwell, u_int maxdwell, 44668e8e04eSSam Leffler u_int nssid, const struct ieee80211_scan_ssid ssids[]) 44768e8e04eSSam Leffler { 448b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 44968e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 45068e8e04eSSam Leffler 451b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 45268e8e04eSSam Leffler 453b032f27cSSam Leffler if (ic->ic_flags & IEEE80211_F_CSAPENDING) { 454b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 455b032f27cSSam Leffler "%s: scan inhibited by pending channel change\n", __func__); 456b032f27cSSam Leffler } else if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 457b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 458b032f27cSSam Leffler "%s: %s scan, duration %u mindwell %u maxdwell %u, desired mode %s, %s%s%s%s%s%s\n" 45968e8e04eSSam Leffler , __func__ 46068e8e04eSSam Leffler , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" 461b032f27cSSam Leffler , duration, mindwell, maxdwell 462b032f27cSSam Leffler , ieee80211_phymode_name[vap->iv_des_mode] 46368e8e04eSSam Leffler , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" 46468e8e04eSSam Leffler , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" 465b032f27cSSam Leffler , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" 466b032f27cSSam Leffler , flags & IEEE80211_SCAN_NOBCAST ? ", nobcast" : "" 46768e8e04eSSam Leffler , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" 46868e8e04eSSam Leffler , flags & IEEE80211_SCAN_ONCE ? ", once" : "" 46968e8e04eSSam Leffler ); 47068e8e04eSSam Leffler 471b032f27cSSam Leffler scan_update_locked(vap, scan); 47268e8e04eSSam Leffler if (ss->ss_ops != NULL) { 47368e8e04eSSam Leffler if ((flags & IEEE80211_SCAN_NOSSID) == 0) 474b032f27cSSam Leffler copy_ssid(vap, ss, nssid, ssids); 47568e8e04eSSam Leffler 47668e8e04eSSam Leffler /* NB: top 4 bits for internal use */ 47768e8e04eSSam Leffler ss->ss_flags = flags & 0xfff; 47868e8e04eSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 479b032f27cSSam Leffler vap->iv_stats.is_scan_active++; 48068e8e04eSSam Leffler else 481b032f27cSSam Leffler vap->iv_stats.is_scan_passive++; 48268e8e04eSSam Leffler if (flags & IEEE80211_SCAN_FLUSH) 48368e8e04eSSam Leffler ss->ss_ops->scan_flush(ss); 48468e8e04eSSam Leffler 48568e8e04eSSam Leffler /* NB: flush frames rx'd before 1st channel change */ 48668e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 487b032f27cSSam Leffler ss->ss_next = 0; 488b032f27cSSam Leffler ss->ss_mindwell = mindwell; 489b032f27cSSam Leffler ss->ss_maxdwell = maxdwell; 490b032f27cSSam Leffler ss->ss_ops->scan_start(ss, vap); 491b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 492b032f27cSSam Leffler if (ieee80211_msg_scan(vap)) 493b032f27cSSam Leffler scan_dump(ss); 494b032f27cSSam Leffler #endif /* IEEE80211_DEBUG */ 49568e8e04eSSam Leffler if (scan_restart(SCAN_PRIVATE(ss), duration)) 49668e8e04eSSam Leffler ic->ic_flags |= IEEE80211_F_SCAN; 49768e8e04eSSam Leffler } 49868e8e04eSSam Leffler } else { 499b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 50068e8e04eSSam Leffler "%s: %s scan already in progress\n", __func__, 50168e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); 50268e8e04eSSam Leffler } 503b032f27cSSam Leffler return (ic->ic_flags & IEEE80211_F_SCAN); 504b032f27cSSam Leffler } 505b032f27cSSam Leffler 506b032f27cSSam Leffler /* 507b032f27cSSam Leffler * Start a scan unless one is already going. 508b032f27cSSam Leffler */ 509b032f27cSSam Leffler int 510b032f27cSSam Leffler ieee80211_start_scan(struct ieee80211vap *vap, int flags, 511b032f27cSSam Leffler u_int duration, u_int mindwell, u_int maxdwell, 512b032f27cSSam Leffler u_int nssid, const struct ieee80211_scan_ssid ssids[]) 513b032f27cSSam Leffler { 514b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 515b032f27cSSam Leffler const struct ieee80211_scanner *scan; 516b032f27cSSam Leffler int result; 517b032f27cSSam Leffler 518b032f27cSSam Leffler scan = ieee80211_scanner_get(vap->iv_opmode); 519b032f27cSSam Leffler if (scan == NULL) { 520b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 521b032f27cSSam Leffler "%s: no scanner support for %s mode\n", 522b032f27cSSam Leffler __func__, ieee80211_opmode_name[vap->iv_opmode]); 523b032f27cSSam Leffler /* XXX stat */ 524b032f27cSSam Leffler return 0; 525b032f27cSSam Leffler } 526b032f27cSSam Leffler 527b032f27cSSam Leffler IEEE80211_LOCK(ic); 528b032f27cSSam Leffler result = start_scan_locked(scan, vap, flags, duration, 529b032f27cSSam Leffler mindwell, maxdwell, nssid, ssids); 53068e8e04eSSam Leffler IEEE80211_UNLOCK(ic); 53168e8e04eSSam Leffler 532b032f27cSSam Leffler return result; 53368e8e04eSSam Leffler } 53468e8e04eSSam Leffler 53568e8e04eSSam Leffler /* 53668e8e04eSSam Leffler * Check the scan cache for an ap/channel to use; if that 53768e8e04eSSam Leffler * fails then kick off a new scan. 53868e8e04eSSam Leffler */ 53968e8e04eSSam Leffler int 540b032f27cSSam Leffler ieee80211_check_scan(struct ieee80211vap *vap, int flags, 541b032f27cSSam Leffler u_int duration, u_int mindwell, u_int maxdwell, 54268e8e04eSSam Leffler u_int nssid, const struct ieee80211_scan_ssid ssids[]) 54368e8e04eSSam Leffler { 544b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 54568e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 546b032f27cSSam Leffler const struct ieee80211_scanner *scan; 547aa0fbc73SSam Leffler int result; 548b032f27cSSam Leffler 549b032f27cSSam Leffler scan = ieee80211_scanner_get(vap->iv_opmode); 550b032f27cSSam Leffler if (scan == NULL) { 551b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 552b032f27cSSam Leffler "%s: no scanner support for %s mode\n", 553b032f27cSSam Leffler __func__, vap->iv_opmode); 554b032f27cSSam Leffler /* XXX stat */ 555b032f27cSSam Leffler return 0; 556b032f27cSSam Leffler } 55768e8e04eSSam Leffler 55868e8e04eSSam Leffler /* 55968e8e04eSSam Leffler * Check if there's a list of scan candidates already. 56068e8e04eSSam Leffler * XXX want more than the ap we're currently associated with 56168e8e04eSSam Leffler */ 56268e8e04eSSam Leffler 56368e8e04eSSam Leffler IEEE80211_LOCK(ic); 564b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 565b032f27cSSam Leffler "%s: %s scan, %s%s%s%s%s\n" 56668e8e04eSSam Leffler , __func__ 56768e8e04eSSam Leffler , flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive" 56868e8e04eSSam Leffler , flags & IEEE80211_SCAN_FLUSH ? "flush" : "append" 56968e8e04eSSam Leffler , flags & IEEE80211_SCAN_NOPICK ? ", nopick" : "" 570b032f27cSSam Leffler , flags & IEEE80211_SCAN_NOJOIN ? ", nojoin" : "" 57168e8e04eSSam Leffler , flags & IEEE80211_SCAN_PICK1ST ? ", pick1st" : "" 57268e8e04eSSam Leffler , flags & IEEE80211_SCAN_ONCE ? ", once" : "" 57368e8e04eSSam Leffler ); 57468e8e04eSSam Leffler 575b032f27cSSam Leffler if (ss->ss_ops != scan) { 576b032f27cSSam Leffler /* XXX re-use cache contents? e.g. adhoc<->sta */ 577b032f27cSSam Leffler flags |= IEEE80211_SCAN_FLUSH; 578b032f27cSSam Leffler } 579b032f27cSSam Leffler scan_update_locked(vap, scan); 58068e8e04eSSam Leffler if (ss->ss_ops != NULL) { 581b032f27cSSam Leffler /* XXX verify ss_ops matches vap->iv_opmode */ 58268e8e04eSSam Leffler if ((flags & IEEE80211_SCAN_NOSSID) == 0) { 58368e8e04eSSam Leffler /* 58468e8e04eSSam Leffler * Update the ssid list and mark flags so if 58568e8e04eSSam Leffler * we call start_scan it doesn't duplicate work. 58668e8e04eSSam Leffler */ 587b032f27cSSam Leffler copy_ssid(vap, ss, nssid, ssids); 58868e8e04eSSam Leffler flags |= IEEE80211_SCAN_NOSSID; 58968e8e04eSSam Leffler } 59068e8e04eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0 && 591b032f27cSSam Leffler (flags & IEEE80211_SCAN_FLUSH) == 0 && 592b032f27cSSam Leffler time_before(ticks, ic->ic_lastscan + vap->iv_scanvalid)) { 59368e8e04eSSam Leffler /* 59468e8e04eSSam Leffler * We're not currently scanning and the cache is 59568e8e04eSSam Leffler * deemed hot enough to consult. Lock out others 59668e8e04eSSam Leffler * by marking IEEE80211_F_SCAN while we decide if 59768e8e04eSSam Leffler * something is already in the scan cache we can 59868e8e04eSSam Leffler * use. Also discard any frames that might come 59968e8e04eSSam Leffler * in while temporarily marked as scanning. 60068e8e04eSSam Leffler */ 60168e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 60268e8e04eSSam Leffler ic->ic_flags |= IEEE80211_F_SCAN; 603aa0fbc73SSam Leffler 604aa0fbc73SSam Leffler /* NB: need to use supplied flags in check */ 605b032f27cSSam Leffler ss->ss_flags = flags & 0xff; 606aa0fbc73SSam Leffler result = ss->ss_ops->scan_end(ss, vap); 607aa0fbc73SSam Leffler 60868e8e04eSSam Leffler ic->ic_flags &= ~IEEE80211_F_SCAN; 609aa0fbc73SSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_DISCARD; 610aa0fbc73SSam Leffler if (result) { 611b032f27cSSam Leffler ieee80211_notify_scan_done(vap); 612b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 61368e8e04eSSam Leffler return 1; 61468e8e04eSSam Leffler } 615aa0fbc73SSam Leffler } 61668e8e04eSSam Leffler } 617b032f27cSSam Leffler result = start_scan_locked(scan, vap, flags, duration, 618b032f27cSSam Leffler mindwell, maxdwell, nssid, ssids); 619b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 620b032f27cSSam Leffler 621b032f27cSSam Leffler return result; 622b032f27cSSam Leffler } 623b032f27cSSam Leffler 624b032f27cSSam Leffler /* 625b032f27cSSam Leffler * Check the scan cache for an ap/channel to use; if that fails 626b032f27cSSam Leffler * then kick off a scan using the current settings. 627b032f27cSSam Leffler */ 628b032f27cSSam Leffler int 629b032f27cSSam Leffler ieee80211_check_scan_current(struct ieee80211vap *vap) 630b032f27cSSam Leffler { 631b032f27cSSam Leffler return ieee80211_check_scan(vap, 632b032f27cSSam Leffler IEEE80211_SCAN_ACTIVE, 633b032f27cSSam Leffler IEEE80211_SCAN_FOREVER, 0, 0, 634b032f27cSSam Leffler vap->iv_des_nssid, vap->iv_des_ssid); 63568e8e04eSSam Leffler } 63668e8e04eSSam Leffler 63768e8e04eSSam Leffler /* 63868e8e04eSSam Leffler * Restart a previous scan. If the previous scan completed 63968e8e04eSSam Leffler * then we start again using the existing channel list. 64068e8e04eSSam Leffler */ 64168e8e04eSSam Leffler int 642b032f27cSSam Leffler ieee80211_bg_scan(struct ieee80211vap *vap, int flags) 64368e8e04eSSam Leffler { 644b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 64568e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 646b032f27cSSam Leffler const struct ieee80211_scanner *scan; 647b032f27cSSam Leffler 648b032f27cSSam Leffler scan = ieee80211_scanner_get(vap->iv_opmode); 649b032f27cSSam Leffler if (scan == NULL) { 650b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 651b032f27cSSam Leffler "%s: no scanner support for %s mode\n", 652b032f27cSSam Leffler __func__, vap->iv_opmode); 653b032f27cSSam Leffler /* XXX stat */ 654b032f27cSSam Leffler return 0; 655b032f27cSSam Leffler } 65668e8e04eSSam Leffler 65768e8e04eSSam Leffler IEEE80211_LOCK(ic); 65868e8e04eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 65968e8e04eSSam Leffler u_int duration; 66068e8e04eSSam Leffler /* 66168e8e04eSSam Leffler * Go off-channel for a fixed interval that is large 66268e8e04eSSam Leffler * enough to catch most ap's but short enough that 66368e8e04eSSam Leffler * we can return on-channel before our listen interval 66468e8e04eSSam Leffler * expires. 66568e8e04eSSam Leffler */ 66668e8e04eSSam Leffler duration = IEEE80211_SCAN_OFFCHANNEL; 66768e8e04eSSam Leffler 668b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 66968e8e04eSSam Leffler "%s: %s scan, ticks %u duration %lu\n", __func__, 67068e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive", 67168e8e04eSSam Leffler ticks, duration); 67268e8e04eSSam Leffler 673b032f27cSSam Leffler scan_update_locked(vap, scan); 67468e8e04eSSam Leffler if (ss->ss_ops != NULL) { 675b032f27cSSam Leffler ss->ss_vap = vap; 67668e8e04eSSam Leffler /* 67768e8e04eSSam Leffler * A background scan does not select a new sta; it 67868e8e04eSSam Leffler * just refreshes the scan cache. Also, indicate 67968e8e04eSSam Leffler * the scan logic should follow the beacon schedule: 68068e8e04eSSam Leffler * we go off-channel and scan for a while, then 68168e8e04eSSam Leffler * return to the bss channel to receive a beacon, 68268e8e04eSSam Leffler * then go off-channel again. All during this time 68368e8e04eSSam Leffler * we notify the ap we're in power save mode. When 68468e8e04eSSam Leffler * the scan is complete we leave power save mode. 68568e8e04eSSam Leffler * If any beacon indicates there are frames pending 68668e8e04eSSam Leffler * for us then we drop out of power save mode 68768e8e04eSSam Leffler * (and background scan) automatically by way of the 68868e8e04eSSam Leffler * usual sta power save logic. 68968e8e04eSSam Leffler */ 69068e8e04eSSam Leffler ss->ss_flags |= IEEE80211_SCAN_NOPICK 691b032f27cSSam Leffler | IEEE80211_SCAN_BGSCAN 692b032f27cSSam Leffler | flags 693b032f27cSSam Leffler ; 69468e8e04eSSam Leffler /* if previous scan completed, restart */ 69568e8e04eSSam Leffler if (ss->ss_next >= ss->ss_last) { 69668e8e04eSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 697b032f27cSSam Leffler vap->iv_stats.is_scan_active++; 69868e8e04eSSam Leffler else 699b032f27cSSam Leffler vap->iv_stats.is_scan_passive++; 700b032f27cSSam Leffler /* 701b032f27cSSam Leffler * NB: beware of the scan cache being flushed; 702b032f27cSSam Leffler * if the channel list is empty use the 703b032f27cSSam Leffler * scan_start method to populate it. 704b032f27cSSam Leffler */ 705b032f27cSSam Leffler ss->ss_next = 0; 706b032f27cSSam Leffler if (ss->ss_last != 0) 707b032f27cSSam Leffler ss->ss_ops->scan_restart(ss, vap); 708b032f27cSSam Leffler else { 709b032f27cSSam Leffler ss->ss_ops->scan_start(ss, vap); 710b032f27cSSam Leffler #ifdef IEEE80211_DEBUG 711b032f27cSSam Leffler if (ieee80211_msg_scan(vap)) 712b032f27cSSam Leffler scan_dump(ss); 713b032f27cSSam Leffler #endif /* IEEE80211_DEBUG */ 714b032f27cSSam Leffler } 71568e8e04eSSam Leffler } 71668e8e04eSSam Leffler /* NB: flush frames rx'd before 1st channel change */ 71768e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_DISCARD; 71868e8e04eSSam Leffler ss->ss_maxdwell = duration; 71968e8e04eSSam Leffler if (scan_restart(SCAN_PRIVATE(ss), duration)) { 72068e8e04eSSam Leffler ic->ic_flags |= IEEE80211_F_SCAN; 72168e8e04eSSam Leffler ic->ic_flags_ext |= IEEE80211_FEXT_BGSCAN; 72268e8e04eSSam Leffler } 72368e8e04eSSam Leffler } else { 72468e8e04eSSam Leffler /* XXX msg+stat */ 72568e8e04eSSam Leffler } 72668e8e04eSSam Leffler } else { 727b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 72868e8e04eSSam Leffler "%s: %s scan already in progress\n", __func__, 72968e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? "active" : "passive"); 73068e8e04eSSam Leffler } 73168e8e04eSSam Leffler IEEE80211_UNLOCK(ic); 73268e8e04eSSam Leffler 73368e8e04eSSam Leffler /* NB: racey, does it matter? */ 73468e8e04eSSam Leffler return (ic->ic_flags & IEEE80211_F_SCAN); 73568e8e04eSSam Leffler } 73668e8e04eSSam Leffler 73768e8e04eSSam Leffler /* 738b032f27cSSam Leffler * Cancel any scan currently going on for the specified vap. 739b032f27cSSam Leffler */ 740b032f27cSSam Leffler void 741b032f27cSSam Leffler ieee80211_cancel_scan(struct ieee80211vap *vap) 742b032f27cSSam Leffler { 743b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 744b032f27cSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 745b032f27cSSam Leffler 746b032f27cSSam Leffler IEEE80211_LOCK(ic); 747b032f27cSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) && 748b032f27cSSam Leffler ss->ss_vap == vap && 749b032f27cSSam Leffler (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { 750b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 751b032f27cSSam Leffler "%s: cancel %s scan\n", __func__, 752b032f27cSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? 753b032f27cSSam Leffler "active" : "passive"); 754b032f27cSSam Leffler 755b032f27cSSam Leffler /* clear bg scan NOPICK and mark cancel request */ 756b032f27cSSam Leffler ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; 757b032f27cSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; 758b032f27cSSam Leffler /* force it to fire asap */ 759b032f27cSSam Leffler callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 760b032f27cSSam Leffler 0, scan_next, ss); 761b032f27cSSam Leffler } 762b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 763b032f27cSSam Leffler } 764b032f27cSSam Leffler 765b032f27cSSam Leffler /* 76668e8e04eSSam Leffler * Cancel any scan currently going on. 76768e8e04eSSam Leffler */ 76868e8e04eSSam Leffler void 769b032f27cSSam Leffler ieee80211_cancel_anyscan(struct ieee80211vap *vap) 77068e8e04eSSam Leffler { 771b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 77268e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 77368e8e04eSSam Leffler 77468e8e04eSSam Leffler IEEE80211_LOCK(ic); 77568e8e04eSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) && 77668e8e04eSSam Leffler (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0) { 777b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 77868e8e04eSSam Leffler "%s: cancel %s scan\n", __func__, 77968e8e04eSSam Leffler ss->ss_flags & IEEE80211_SCAN_ACTIVE ? 78068e8e04eSSam Leffler "active" : "passive"); 78168e8e04eSSam Leffler 78268e8e04eSSam Leffler /* clear bg scan NOPICK and mark cancel request */ 78368e8e04eSSam Leffler ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; 78468e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_CANCEL; 78568e8e04eSSam Leffler /* force it to fire asap */ 78668e8e04eSSam Leffler callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 78768e8e04eSSam Leffler 0, scan_next, ss); 78868e8e04eSSam Leffler } 78968e8e04eSSam Leffler IEEE80211_UNLOCK(ic); 79068e8e04eSSam Leffler } 79168e8e04eSSam Leffler 79268e8e04eSSam Leffler /* 79368e8e04eSSam Leffler * Public access to scan_next for drivers that manage 79468e8e04eSSam Leffler * scanning themselves (e.g. for firmware-based devices). 79568e8e04eSSam Leffler */ 79668e8e04eSSam Leffler void 797b032f27cSSam Leffler ieee80211_scan_next(struct ieee80211vap *vap) 79868e8e04eSSam Leffler { 799b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 800b032f27cSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 801b032f27cSSam Leffler 802b032f27cSSam Leffler callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); 80368e8e04eSSam Leffler } 80468e8e04eSSam Leffler 80568e8e04eSSam Leffler /* 806d81b3a55SAndrew Thompson * Public access to scan_next for drivers that are not able to scan single 807d81b3a55SAndrew Thompson * channels (e.g. for firmware-based devices). 808d81b3a55SAndrew Thompson */ 809d81b3a55SAndrew Thompson void 810b032f27cSSam Leffler ieee80211_scan_done(struct ieee80211vap *vap) 811d81b3a55SAndrew Thompson { 812b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 813b032f27cSSam Leffler struct ieee80211_scan_state *ss; 814d81b3a55SAndrew Thompson 815b032f27cSSam Leffler IEEE80211_LOCK(ic); 816b032f27cSSam Leffler ss = ic->ic_scan; 817d81b3a55SAndrew Thompson ss->ss_next = ss->ss_last; /* all channels are complete */ 818d81b3a55SAndrew Thompson scan_next(ss); 819b032f27cSSam Leffler IEEE80211_UNLOCK(ic); 820b032f27cSSam Leffler } 821b032f27cSSam Leffler 822b032f27cSSam Leffler /* 823b032f27cSSam Leffler * Probe the curent channel, if allowed, while scanning. 824b032f27cSSam Leffler * If the channel is not marked passive-only then send 825b032f27cSSam Leffler * a probe request immediately. Otherwise mark state and 826b032f27cSSam Leffler * listen for beacons on the channel; if we receive something 827b032f27cSSam Leffler * then we'll transmit a probe request. 828b032f27cSSam Leffler */ 829b032f27cSSam Leffler void 830b032f27cSSam Leffler ieee80211_probe_curchan(struct ieee80211vap *vap, int force) 831b032f27cSSam Leffler { 832b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 833b032f27cSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 834b032f27cSSam Leffler struct ifnet *ifp = vap->iv_ifp; 835b032f27cSSam Leffler int i; 836b032f27cSSam Leffler 837b032f27cSSam Leffler if ((ic->ic_curchan->ic_flags & IEEE80211_CHAN_PASSIVE) && !force) { 838b032f27cSSam Leffler ic->ic_flags_ext |= IEEE80211_FEXT_PROBECHAN; 839b032f27cSSam Leffler return; 840b032f27cSSam Leffler } 841b032f27cSSam Leffler /* 842b032f27cSSam Leffler * Send directed probe requests followed by any 843b032f27cSSam Leffler * broadcast probe request. 844b032f27cSSam Leffler * XXX remove dependence on ic/vap->iv_bss 845b032f27cSSam Leffler */ 846b032f27cSSam Leffler for (i = 0; i < ss->ss_nssid; i++) 847b032f27cSSam Leffler ieee80211_send_probereq(vap->iv_bss, 848b032f27cSSam Leffler vap->iv_myaddr, ifp->if_broadcastaddr, 849b032f27cSSam Leffler ifp->if_broadcastaddr, 850b032f27cSSam Leffler ss->ss_ssid[i].ssid, ss->ss_ssid[i].len); 851b032f27cSSam Leffler if ((ss->ss_flags & IEEE80211_SCAN_NOBCAST) == 0) 852b032f27cSSam Leffler ieee80211_send_probereq(vap->iv_bss, 853b032f27cSSam Leffler vap->iv_myaddr, ifp->if_broadcastaddr, 854b032f27cSSam Leffler ifp->if_broadcastaddr, 855b032f27cSSam Leffler "", 0); 856d81b3a55SAndrew Thompson } 857d81b3a55SAndrew Thompson 858d81b3a55SAndrew Thompson /* 85968e8e04eSSam Leffler * Scan curchan. If this is an active scan and the channel 86068e8e04eSSam Leffler * is not marked passive then send probe request frame(s). 86168e8e04eSSam Leffler * Arrange for the channel change after maxdwell ticks. 86268e8e04eSSam Leffler */ 86368e8e04eSSam Leffler static void 864b032f27cSSam Leffler scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 86568e8e04eSSam Leffler { 866b032f27cSSam Leffler struct ieee80211vap *vap = ss->ss_vap; 86768e8e04eSSam Leffler 868b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(vap->iv_ic); 86968e8e04eSSam Leffler 870b032f27cSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 871b032f27cSSam Leffler ieee80211_probe_curchan(vap, 0); 87268e8e04eSSam Leffler callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 87368e8e04eSSam Leffler maxdwell, scan_next, ss); 87468e8e04eSSam Leffler } 87568e8e04eSSam Leffler 87668e8e04eSSam Leffler /* 87768e8e04eSSam Leffler * Handle mindwell requirements completed; initiate a channel 87868e8e04eSSam Leffler * change to the next channel asap. 87968e8e04eSSam Leffler */ 88068e8e04eSSam Leffler static void 881b032f27cSSam Leffler scan_mindwell(struct ieee80211_scan_state *ss) 88268e8e04eSSam Leffler { 88368e8e04eSSam Leffler callout_reset(&SCAN_PRIVATE(ss)->ss_scan_timer, 0, scan_next, ss); 88468e8e04eSSam Leffler } 88568e8e04eSSam Leffler 88668e8e04eSSam Leffler /* 88768e8e04eSSam Leffler * Switch to the next channel marked for scanning. 88868e8e04eSSam Leffler */ 88968e8e04eSSam Leffler static void 89068e8e04eSSam Leffler scan_next(void *arg) 89168e8e04eSSam Leffler { 89268e8e04eSSam Leffler #define ISCAN_REP (ISCAN_MINDWELL | ISCAN_START | ISCAN_DISCARD) 89368e8e04eSSam Leffler struct ieee80211_scan_state *ss = (struct ieee80211_scan_state *) arg; 894b032f27cSSam Leffler struct ieee80211vap *vap = ss->ss_vap; 895b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 89668e8e04eSSam Leffler struct ieee80211_channel *chan; 89768e8e04eSSam Leffler unsigned long maxdwell, scanend; 898b032f27cSSam Leffler int scandone; 89968e8e04eSSam Leffler 900b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 901b032f27cSSam Leffler 902b032f27cSSam Leffler if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) 90368e8e04eSSam Leffler return; 90468e8e04eSSam Leffler again: 90568e8e04eSSam Leffler scandone = (ss->ss_next >= ss->ss_last) || 90668e8e04eSSam Leffler (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) != 0; 90768e8e04eSSam Leffler scanend = SCAN_PRIVATE(ss)->ss_scanend; 90868e8e04eSSam Leffler if (!scandone && 90968e8e04eSSam Leffler (ss->ss_flags & IEEE80211_SCAN_GOTPICK) == 0 && 91068e8e04eSSam Leffler ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_START) || 91168e8e04eSSam Leffler time_before(ticks + ss->ss_mindwell, scanend))) { 91268e8e04eSSam Leffler chan = ss->ss_chans[ss->ss_next++]; 91368e8e04eSSam Leffler 91468e8e04eSSam Leffler /* 91568e8e04eSSam Leffler * Watch for truncation due to the scan end time. 91668e8e04eSSam Leffler */ 91768e8e04eSSam Leffler if (time_after(ticks + ss->ss_maxdwell, scanend)) 91868e8e04eSSam Leffler maxdwell = scanend - ticks; 91968e8e04eSSam Leffler else 92068e8e04eSSam Leffler maxdwell = ss->ss_maxdwell; 92168e8e04eSSam Leffler 922b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 923110a70e3SSam Leffler "%s: chan %3d%c -> %3d%c [%s, dwell min %lums max %lums]\n", 92468e8e04eSSam Leffler __func__, 92568e8e04eSSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 92668e8e04eSSam Leffler channel_type(ic->ic_curchan), 92768e8e04eSSam Leffler ieee80211_chan2ieee(ic, chan), channel_type(chan), 92868e8e04eSSam Leffler (ss->ss_flags & IEEE80211_SCAN_ACTIVE) && 92968e8e04eSSam Leffler (chan->ic_flags & IEEE80211_CHAN_PASSIVE) == 0 ? 93068e8e04eSSam Leffler "active" : "passive", 931110a70e3SSam Leffler ticks_to_msecs(ss->ss_mindwell), ticks_to_msecs(maxdwell)); 93268e8e04eSSam Leffler 93368e8e04eSSam Leffler /* 93468e8e04eSSam Leffler * Potentially change channel and phy mode. 93568e8e04eSSam Leffler */ 93668e8e04eSSam Leffler change_channel(ic, chan); 93768e8e04eSSam Leffler 93868e8e04eSSam Leffler /* 93968e8e04eSSam Leffler * Scan curchan. Drivers for "intelligent hardware" 94068e8e04eSSam Leffler * override ic_scan_curchan to tell the device to do 94168e8e04eSSam Leffler * the work. Otherwise we manage the work outselves; 94268e8e04eSSam Leffler * sending a probe request (as needed), and arming the 94368e8e04eSSam Leffler * timeout to switch channels after maxdwell ticks. 94468e8e04eSSam Leffler */ 945b032f27cSSam Leffler ic->ic_scan_curchan(ss, maxdwell); 94668e8e04eSSam Leffler 94768e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_chanmindwell = ticks + ss->ss_mindwell; 94868e8e04eSSam Leffler /* clear mindwell lock and initial channel change flush */ 94968e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; 95068e8e04eSSam Leffler } else { 95168e8e04eSSam Leffler ic->ic_scan_end(ic); /* notify driver */ 95268e8e04eSSam Leffler /* 95368e8e04eSSam Leffler * Record scan complete time. Note that we also do 95468e8e04eSSam Leffler * this when canceled so any background scan will 95568e8e04eSSam Leffler * not be restarted for a while. 95668e8e04eSSam Leffler */ 95768e8e04eSSam Leffler if (scandone) 95868e8e04eSSam Leffler ic->ic_lastscan = ticks; 95968e8e04eSSam Leffler /* return to the bss channel */ 96068e8e04eSSam Leffler if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && 96168e8e04eSSam Leffler ic->ic_curchan != ic->ic_bsschan) 962b032f27cSSam Leffler ieee80211_setcurchan(ic, ic->ic_bsschan); 96368e8e04eSSam Leffler /* clear internal flags and any indication of a pick */ 96468e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_REP; 96568e8e04eSSam Leffler ss->ss_flags &= ~IEEE80211_SCAN_GOTPICK; 96668e8e04eSSam Leffler 96768e8e04eSSam Leffler /* 96868e8e04eSSam Leffler * If not canceled and scan completed, do post-processing. 96968e8e04eSSam Leffler * If the callback function returns 0, then it wants to 97068e8e04eSSam Leffler * continue/restart scanning. Unfortunately we needed to 97168e8e04eSSam Leffler * notify the driver to end the scan above to avoid having 97268e8e04eSSam Leffler * rx frames alter the scan candidate list. 97368e8e04eSSam Leffler */ 97468e8e04eSSam Leffler if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_CANCEL) == 0 && 975b032f27cSSam Leffler !ss->ss_ops->scan_end(ss, vap) && 97668e8e04eSSam Leffler (ss->ss_flags & IEEE80211_SCAN_ONCE) == 0 && 97768e8e04eSSam Leffler time_before(ticks + ss->ss_mindwell, scanend)) { 978b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 97968e8e04eSSam Leffler "%s: done, restart " 98068e8e04eSSam Leffler "[ticks %u, dwell min %lu scanend %lu]\n", 98168e8e04eSSam Leffler __func__, 98268e8e04eSSam Leffler ticks, ss->ss_mindwell, scanend); 98368e8e04eSSam Leffler ss->ss_next = 0; /* reset to begining */ 98468e8e04eSSam Leffler if (ss->ss_flags & IEEE80211_SCAN_ACTIVE) 985b032f27cSSam Leffler vap->iv_stats.is_scan_active++; 98668e8e04eSSam Leffler else 987b032f27cSSam Leffler vap->iv_stats.is_scan_passive++; 98868e8e04eSSam Leffler 989b032f27cSSam Leffler ss->ss_ops->scan_restart(ss, vap); /* XXX? */ 99068e8e04eSSam Leffler ic->ic_scan_start(ic); /* notify driver */ 99168e8e04eSSam Leffler goto again; 99268e8e04eSSam Leffler } else { 99368e8e04eSSam Leffler /* past here, scandone is ``true'' if not in bg mode */ 99468e8e04eSSam Leffler if ((ss->ss_flags & IEEE80211_SCAN_BGSCAN) == 0) 99568e8e04eSSam Leffler scandone = 1; 99668e8e04eSSam Leffler 997b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 99868e8e04eSSam Leffler "%s: %s, " 99968e8e04eSSam Leffler "[ticks %u, dwell min %lu scanend %lu]\n", 100068e8e04eSSam Leffler __func__, scandone ? "done" : "stopped", 100168e8e04eSSam Leffler ticks, ss->ss_mindwell, scanend); 100268e8e04eSSam Leffler 100368e8e04eSSam Leffler /* 100468e8e04eSSam Leffler * Clear the SCAN bit first in case frames are 100568e8e04eSSam Leffler * pending on the station power save queue. If 100668e8e04eSSam Leffler * we defer this then the dispatch of the frames 100768e8e04eSSam Leffler * may generate a request to cancel scanning. 100868e8e04eSSam Leffler */ 100968e8e04eSSam Leffler ic->ic_flags &= ~IEEE80211_F_SCAN; 101068e8e04eSSam Leffler /* 101168e8e04eSSam Leffler * Drop out of power save mode when a scan has 101268e8e04eSSam Leffler * completed. If this scan was prematurely terminated 101368e8e04eSSam Leffler * because it is a background scan then don't notify 101468e8e04eSSam Leffler * the ap; we'll either return to scanning after we 101568e8e04eSSam Leffler * receive the beacon frame or we'll drop out of power 101668e8e04eSSam Leffler * save mode because the beacon indicates we have frames 101768e8e04eSSam Leffler * waiting for us. 101868e8e04eSSam Leffler */ 101968e8e04eSSam Leffler if (scandone) { 1020b032f27cSSam Leffler ieee80211_sta_pwrsave(vap, 0); 102168e8e04eSSam Leffler if (ss->ss_next >= ss->ss_last) { 1022b032f27cSSam Leffler ieee80211_notify_scan_done(vap); 102368e8e04eSSam Leffler ic->ic_flags_ext &= ~IEEE80211_FEXT_BGSCAN; 102468e8e04eSSam Leffler } 102568e8e04eSSam Leffler } 102668e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags &= ~ISCAN_CANCEL; 102768e8e04eSSam Leffler ss->ss_flags &= 102868e8e04eSSam Leffler ~(IEEE80211_SCAN_ONCE | IEEE80211_SCAN_PICK1ST); 102968e8e04eSSam Leffler } 103068e8e04eSSam Leffler } 103168e8e04eSSam Leffler #undef ISCAN_REP 103268e8e04eSSam Leffler } 103368e8e04eSSam Leffler 103468e8e04eSSam Leffler #ifdef IEEE80211_DEBUG 103568e8e04eSSam Leffler static void 1036b032f27cSSam Leffler dump_country(const uint8_t *ie) 1037b032f27cSSam Leffler { 1038b032f27cSSam Leffler const struct ieee80211_country_ie *cie = 1039b032f27cSSam Leffler (const struct ieee80211_country_ie *) ie; 1040b032f27cSSam Leffler int i, nbands, schan, nchan; 1041b032f27cSSam Leffler 1042b032f27cSSam Leffler if (cie->len < 3) { 1043b032f27cSSam Leffler printf(" <bogus country ie, len %d>", cie->len); 1044b032f27cSSam Leffler return; 1045b032f27cSSam Leffler } 1046b032f27cSSam Leffler printf(" country [%c%c%c", cie->cc[0], cie->cc[1], cie->cc[2]); 1047b032f27cSSam Leffler nbands = (cie->len - 3) / sizeof(cie->band[0]); 1048b032f27cSSam Leffler for (i = 0; i < nbands; i++) { 1049b032f27cSSam Leffler schan = cie->band[i].schan; 1050b032f27cSSam Leffler nchan = cie->band[i].nchan; 1051b032f27cSSam Leffler if (nchan != 1) 1052b032f27cSSam Leffler printf(" %u-%u,%u", schan, schan + nchan-1, 1053b032f27cSSam Leffler cie->band[i].maxtxpwr); 1054b032f27cSSam Leffler else 1055b032f27cSSam Leffler printf(" %u,%u", schan, cie->band[i].maxtxpwr); 1056b032f27cSSam Leffler } 1057b032f27cSSam Leffler printf("]"); 1058b032f27cSSam Leffler } 1059b032f27cSSam Leffler 1060b032f27cSSam Leffler static void 106168e8e04eSSam Leffler dump_probe_beacon(uint8_t subtype, int isnew, 106268e8e04eSSam Leffler const uint8_t mac[IEEE80211_ADDR_LEN], 1063b032f27cSSam Leffler const struct ieee80211_scanparams *sp, int rssi) 106468e8e04eSSam Leffler { 106568e8e04eSSam Leffler 106668e8e04eSSam Leffler printf("[%s] %s%s on chan %u (bss chan %u) ", 106768e8e04eSSam Leffler ether_sprintf(mac), isnew ? "new " : "", 106868e8e04eSSam Leffler ieee80211_mgt_subtype_name[subtype >> IEEE80211_FC0_SUBTYPE_SHIFT], 1069b032f27cSSam Leffler sp->chan, sp->bchan); 107068e8e04eSSam Leffler ieee80211_print_essid(sp->ssid + 2, sp->ssid[1]); 1071b032f27cSSam Leffler printf(" rssi %d\n", rssi); 107268e8e04eSSam Leffler 107368e8e04eSSam Leffler if (isnew) { 107468e8e04eSSam Leffler printf("[%s] caps 0x%x bintval %u erp 0x%x", 107568e8e04eSSam Leffler ether_sprintf(mac), sp->capinfo, sp->bintval, sp->erp); 1076b032f27cSSam Leffler if (sp->country != NULL) 1077b032f27cSSam Leffler dump_country(sp->country); 107868e8e04eSSam Leffler printf("\n"); 107968e8e04eSSam Leffler } 108068e8e04eSSam Leffler } 108168e8e04eSSam Leffler #endif /* IEEE80211_DEBUG */ 108268e8e04eSSam Leffler 108368e8e04eSSam Leffler /* 108468e8e04eSSam Leffler * Process a beacon or probe response frame. 108568e8e04eSSam Leffler */ 108668e8e04eSSam Leffler void 1087b032f27cSSam Leffler ieee80211_add_scan(struct ieee80211vap *vap, 108868e8e04eSSam Leffler const struct ieee80211_scanparams *sp, 108968e8e04eSSam Leffler const struct ieee80211_frame *wh, 109068e8e04eSSam Leffler int subtype, int rssi, int noise, int rstamp) 109168e8e04eSSam Leffler { 1092b032f27cSSam Leffler struct ieee80211com *ic = vap->iv_ic; 109368e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 109468e8e04eSSam Leffler 1095b032f27cSSam Leffler /* XXX locking */ 109668e8e04eSSam Leffler /* 109768e8e04eSSam Leffler * Frames received during startup are discarded to avoid 109868e8e04eSSam Leffler * using scan state setup on the initial entry to the timer 109968e8e04eSSam Leffler * callback. This can occur because the device may enable 110068e8e04eSSam Leffler * rx prior to our doing the initial channel change in the 1101b032f27cSSam Leffler * timer routine. 110268e8e04eSSam Leffler */ 110368e8e04eSSam Leffler if (SCAN_PRIVATE(ss)->ss_iflags & ISCAN_DISCARD) 110468e8e04eSSam Leffler return; 110568e8e04eSSam Leffler #ifdef IEEE80211_DEBUG 1106b032f27cSSam Leffler if (ieee80211_msg_scan(vap) && (ic->ic_flags & IEEE80211_F_SCAN)) 1107b032f27cSSam Leffler dump_probe_beacon(subtype, 1, wh->i_addr2, sp, rssi); 110868e8e04eSSam Leffler #endif 110968e8e04eSSam Leffler if (ss->ss_ops != NULL && 111068e8e04eSSam Leffler ss->ss_ops->scan_add(ss, sp, wh, subtype, rssi, noise, rstamp)) { 111168e8e04eSSam Leffler /* 111268e8e04eSSam Leffler * If we've reached the min dwell time terminate 111368e8e04eSSam Leffler * the timer so we'll switch to the next channel. 111468e8e04eSSam Leffler */ 111568e8e04eSSam Leffler if ((SCAN_PRIVATE(ss)->ss_iflags & ISCAN_MINDWELL) == 0 && 111668e8e04eSSam Leffler time_after_eq(ticks, SCAN_PRIVATE(ss)->ss_chanmindwell)) { 1117b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, 111868e8e04eSSam Leffler "%s: chan %3d%c min dwell met (%u > %lu)\n", 111968e8e04eSSam Leffler __func__, 112068e8e04eSSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 112168e8e04eSSam Leffler channel_type(ic->ic_curchan), 112268e8e04eSSam Leffler ticks, SCAN_PRIVATE(ss)->ss_chanmindwell); 112368e8e04eSSam Leffler SCAN_PRIVATE(ss)->ss_iflags |= ISCAN_MINDWELL; 112468e8e04eSSam Leffler /* 112568e8e04eSSam Leffler * NB: trigger at next clock tick or wait for the 1126b032f27cSSam Leffler * hardware. 112768e8e04eSSam Leffler */ 1128b032f27cSSam Leffler ic->ic_scan_mindwell(ss); 112968e8e04eSSam Leffler } 113068e8e04eSSam Leffler } 113168e8e04eSSam Leffler } 113268e8e04eSSam Leffler 113368e8e04eSSam Leffler /* 113468e8e04eSSam Leffler * Timeout/age scan cache entries; called from sta timeout 113568e8e04eSSam Leffler * timer (XXX should be self-contained). 113668e8e04eSSam Leffler */ 113768e8e04eSSam Leffler void 113868e8e04eSSam Leffler ieee80211_scan_timeout(struct ieee80211com *ic) 113968e8e04eSSam Leffler { 114068e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 114168e8e04eSSam Leffler 114268e8e04eSSam Leffler if (ss->ss_ops != NULL) 114368e8e04eSSam Leffler ss->ss_ops->scan_age(ss); 114468e8e04eSSam Leffler } 114568e8e04eSSam Leffler 114668e8e04eSSam Leffler /* 114768e8e04eSSam Leffler * Mark a scan cache entry after a successful associate. 114868e8e04eSSam Leffler */ 114968e8e04eSSam Leffler void 1150b032f27cSSam Leffler ieee80211_scan_assoc_success(struct ieee80211vap *vap, const uint8_t mac[]) 115168e8e04eSSam Leffler { 1152b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 115368e8e04eSSam Leffler 115468e8e04eSSam Leffler if (ss->ss_ops != NULL) { 1155b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, 115668e8e04eSSam Leffler mac, "%s", __func__); 115768e8e04eSSam Leffler ss->ss_ops->scan_assoc_success(ss, mac); 115868e8e04eSSam Leffler } 115968e8e04eSSam Leffler } 116068e8e04eSSam Leffler 116168e8e04eSSam Leffler /* 116268e8e04eSSam Leffler * Demerit a scan cache entry after failing to associate. 116368e8e04eSSam Leffler */ 116468e8e04eSSam Leffler void 1165b032f27cSSam Leffler ieee80211_scan_assoc_fail(struct ieee80211vap *vap, 116668e8e04eSSam Leffler const uint8_t mac[], int reason) 116768e8e04eSSam Leffler { 1168b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 116968e8e04eSSam Leffler 117068e8e04eSSam Leffler if (ss->ss_ops != NULL) { 1171b032f27cSSam Leffler IEEE80211_NOTE_MAC(vap, IEEE80211_MSG_SCAN, mac, 117268e8e04eSSam Leffler "%s: reason %u", __func__, reason); 117368e8e04eSSam Leffler ss->ss_ops->scan_assoc_fail(ss, mac, reason); 117468e8e04eSSam Leffler } 117568e8e04eSSam Leffler } 117668e8e04eSSam Leffler 117768e8e04eSSam Leffler /* 117868e8e04eSSam Leffler * Iterate over the contents of the scan cache. 117968e8e04eSSam Leffler */ 118068e8e04eSSam Leffler void 1181b032f27cSSam Leffler ieee80211_scan_iterate(struct ieee80211vap *vap, 118268e8e04eSSam Leffler ieee80211_scan_iter_func *f, void *arg) 118368e8e04eSSam Leffler { 1184b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 118568e8e04eSSam Leffler 118668e8e04eSSam Leffler if (ss->ss_ops != NULL) 118768e8e04eSSam Leffler ss->ss_ops->scan_iterate(ss, f, arg); 118868e8e04eSSam Leffler } 118968e8e04eSSam Leffler 119068e8e04eSSam Leffler /* 119168e8e04eSSam Leffler * Flush the contents of the scan cache. 119268e8e04eSSam Leffler */ 119368e8e04eSSam Leffler void 1194b032f27cSSam Leffler ieee80211_scan_flush(struct ieee80211vap *vap) 1195b032f27cSSam Leffler { 1196b032f27cSSam Leffler struct ieee80211_scan_state *ss = vap->iv_ic->ic_scan; 1197b032f27cSSam Leffler 1198b032f27cSSam Leffler if (ss->ss_ops != NULL && ss->ss_vap == vap) { 1199b032f27cSSam Leffler IEEE80211_DPRINTF(vap, IEEE80211_MSG_SCAN, "%s\n", __func__); 1200b032f27cSSam Leffler ss->ss_ops->scan_flush(ss); 1201b032f27cSSam Leffler } 1202b032f27cSSam Leffler } 1203b032f27cSSam Leffler 1204b032f27cSSam Leffler /* 1205b032f27cSSam Leffler * Check the scan cache for an ap/channel to use; if that 1206b032f27cSSam Leffler * fails then kick off a new scan. 1207b032f27cSSam Leffler */ 1208b032f27cSSam Leffler struct ieee80211_channel * 1209b032f27cSSam Leffler ieee80211_scan_pickchannel(struct ieee80211com *ic, int flags) 121068e8e04eSSam Leffler { 121168e8e04eSSam Leffler struct ieee80211_scan_state *ss = ic->ic_scan; 121268e8e04eSSam Leffler 1213b032f27cSSam Leffler IEEE80211_LOCK_ASSERT(ic); 1214b032f27cSSam Leffler 1215b032f27cSSam Leffler if (ss == NULL || ss->ss_ops == NULL || ss->ss_vap == NULL) { 1216b032f27cSSam Leffler /* XXX printf? */ 1217b032f27cSSam Leffler return NULL; 121868e8e04eSSam Leffler } 1219b032f27cSSam Leffler if (ss->ss_ops->scan_pickchan == NULL) { 1220b032f27cSSam Leffler IEEE80211_DPRINTF(ss->ss_vap, IEEE80211_MSG_SCAN, 1221b032f27cSSam Leffler "%s: scan module does not support picking a channel, " 1222b032f27cSSam Leffler "opmode %s\n", __func__, ss->ss_vap->iv_opmode); 1223b032f27cSSam Leffler return NULL; 1224b032f27cSSam Leffler } 1225b032f27cSSam Leffler return ss->ss_ops->scan_pickchan(ss, flags); 122668e8e04eSSam Leffler } 1227