ieee80211_proto.c revision 188782
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3178354Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 4116742Ssam * All rights reserved. 5116742Ssam * 6116742Ssam * Redistribution and use in source and binary forms, with or without 7116742Ssam * modification, are permitted provided that the following conditions 8116742Ssam * are met: 9116742Ssam * 1. Redistributions of source code must retain the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer. 11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12116904Ssam * notice, this list of conditions and the following disclaimer in the 13116904Ssam * documentation and/or other materials provided with the distribution. 14116742Ssam * 15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25116742Ssam */ 26116742Ssam 27116742Ssam#include <sys/cdefs.h> 28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_proto.c 188782 2009-02-19 05:21:54Z sam $"); 29116742Ssam 30116742Ssam/* 31116742Ssam * IEEE 802.11 protocol support. 32116742Ssam */ 33116742Ssam 34116742Ssam#include "opt_inet.h" 35178354Ssam#include "opt_wlan.h" 36116742Ssam 37116742Ssam#include <sys/param.h> 38138568Ssam#include <sys/kernel.h> 39170530Ssam#include <sys/systm.h> 40178354Ssam#include <sys/taskqueue.h> 41170530Ssam 42116742Ssam#include <sys/socket.h> 43178354Ssam#include <sys/sockio.h> 44116742Ssam 45116742Ssam#include <net/if.h> 46116742Ssam#include <net/if_media.h> 47138568Ssam#include <net/ethernet.h> /* XXX for ether_sprintf */ 48116742Ssam 49116742Ssam#include <net80211/ieee80211_var.h> 50178354Ssam#include <net80211/ieee80211_adhoc.h> 51178354Ssam#include <net80211/ieee80211_sta.h> 52178354Ssam#include <net80211/ieee80211_hostap.h> 53178354Ssam#include <net80211/ieee80211_wds.h> 54178354Ssam#include <net80211/ieee80211_monitor.h> 55178354Ssam#include <net80211/ieee80211_input.h> 56116742Ssam 57138568Ssam/* XXX tunables */ 58138568Ssam#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 59138568Ssam#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 60116742Ssam 61116742Ssamconst char *ieee80211_mgt_subtype_name[] = { 62116742Ssam "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 63116742Ssam "probe_req", "probe_resp", "reserved#6", "reserved#7", 64116742Ssam "beacon", "atim", "disassoc", "auth", 65172230Ssam "deauth", "action", "reserved#14", "reserved#15" 66116742Ssam}; 67138568Ssamconst char *ieee80211_ctl_subtype_name[] = { 68138568Ssam "reserved#0", "reserved#1", "reserved#2", "reserved#3", 69138568Ssam "reserved#3", "reserved#5", "reserved#6", "reserved#7", 70138568Ssam "reserved#8", "reserved#9", "ps_poll", "rts", 71138568Ssam "cts", "ack", "cf_end", "cf_end_ack" 72138568Ssam}; 73167283Ssamconst char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 74167283Ssam "IBSS", /* IEEE80211_M_IBSS */ 75167283Ssam "STA", /* IEEE80211_M_STA */ 76178354Ssam "WDS", /* IEEE80211_M_WDS */ 77167283Ssam "AHDEMO", /* IEEE80211_M_AHDEMO */ 78167283Ssam "HOSTAP", /* IEEE80211_M_HOSTAP */ 79167283Ssam "MONITOR" /* IEEE80211_M_MONITOR */ 80167283Ssam}; 81117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = { 82117811Ssam "INIT", /* IEEE80211_S_INIT */ 83117811Ssam "SCAN", /* IEEE80211_S_SCAN */ 84117811Ssam "AUTH", /* IEEE80211_S_AUTH */ 85117811Ssam "ASSOC", /* IEEE80211_S_ASSOC */ 86172058Ssam "CAC", /* IEEE80211_S_CAC */ 87172058Ssam "RUN", /* IEEE80211_S_RUN */ 88172058Ssam "CSA", /* IEEE80211_S_CSA */ 89172058Ssam "SLEEP", /* IEEE80211_S_SLEEP */ 90117811Ssam}; 91138568Ssamconst char *ieee80211_wme_acnames[] = { 92138568Ssam "WME_AC_BE", 93138568Ssam "WME_AC_BK", 94138568Ssam "WME_AC_VI", 95138568Ssam "WME_AC_VO", 96138568Ssam "WME_UPSD", 97138568Ssam}; 98116742Ssam 99178354Ssamstatic void parent_updown(void *, int); 100178354Ssamstatic int ieee80211_new_state_locked(struct ieee80211vap *, 101178354Ssam enum ieee80211_state, int); 102117811Ssam 103178354Ssamstatic int 104178354Ssamnull_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 105178354Ssam const struct ieee80211_bpf_params *params) 106172211Ssam{ 107178354Ssam struct ifnet *ifp = ni->ni_ic->ic_ifp; 108178354Ssam 109178354Ssam if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); 110178354Ssam m_freem(m); 111178354Ssam return ENETDOWN; 112172211Ssam} 113172211Ssam 114116742Ssamvoid 115138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 116116742Ssam{ 117138568Ssam struct ifnet *ifp = ic->ic_ifp; 118116742Ssam 119178354Ssam /* override the 802.3 setting */ 120178354Ssam ifp->if_hdrlen = ic->ic_headroom 121178354Ssam + sizeof(struct ieee80211_qosframe_addr4) 122178354Ssam + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN 123178354Ssam + IEEE80211_WEP_EXTIVLEN; 124178354Ssam /* XXX no way to recalculate on ifdetach */ 125178354Ssam if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { 126178354Ssam /* XXX sanity check... */ 127178354Ssam max_linkhdr = ALIGN(ifp->if_hdrlen); 128178354Ssam max_hdr = max_linkhdr + max_protohdr; 129178354Ssam max_datalen = MHLEN - max_hdr; 130178354Ssam } 131127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 132116742Ssam 133178354Ssam TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp); 134178354Ssam 135138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 136138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 137138568Ssam 138116742Ssam /* initialize management frame handlers */ 139116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 140178354Ssam ic->ic_raw_xmit = null_raw_xmit; 141178354Ssam 142178354Ssam ieee80211_adhoc_attach(ic); 143178354Ssam ieee80211_sta_attach(ic); 144178354Ssam ieee80211_wds_attach(ic); 145178354Ssam ieee80211_hostap_attach(ic); 146178354Ssam ieee80211_monitor_attach(ic); 147116742Ssam} 148116742Ssam 149116742Ssamvoid 150138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 151116742Ssam{ 152178354Ssam ieee80211_monitor_detach(ic); 153178354Ssam ieee80211_hostap_detach(ic); 154178354Ssam ieee80211_wds_detach(ic); 155178354Ssam ieee80211_adhoc_detach(ic); 156178354Ssam ieee80211_sta_detach(ic); 157178354Ssam} 158116742Ssam 159178354Ssamstatic void 160178354Ssamnull_update_beacon(struct ieee80211vap *vap, int item) 161178354Ssam{ 162178354Ssam} 163178354Ssam 164178354Ssamvoid 165178354Ssamieee80211_proto_vattach(struct ieee80211vap *vap) 166178354Ssam{ 167178354Ssam struct ieee80211com *ic = vap->iv_ic; 168178354Ssam struct ifnet *ifp = vap->iv_ifp; 169178354Ssam int i; 170178354Ssam 171178354Ssam /* override the 802.3 setting */ 172178354Ssam ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; 173178354Ssam 174178354Ssam vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; 175178354Ssam vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; 176178354Ssam vap->iv_bmiss_max = IEEE80211_BMISS_MAX; 177178354Ssam callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE); 178178354Ssam callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE); 179138568Ssam /* 180178354Ssam * Install default tx rate handling: no fixed rate, lowest 181178354Ssam * supported rate for mgmt and multicast frames. Default 182178354Ssam * max retry count. These settings can be changed by the 183178354Ssam * driver and/or user applications. 184178354Ssam */ 185188779Ssam for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { 186178354Ssam const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; 187178354Ssam 188178354Ssam vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; 189188779Ssam if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) { 190188779Ssam vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS; 191188779Ssam vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS; 192188779Ssam } else { 193188779Ssam vap->iv_txparms[i].mgmtrate = 194188779Ssam rs->rs_rates[0] & IEEE80211_RATE_VAL; 195188779Ssam vap->iv_txparms[i].mcastrate = 196188779Ssam rs->rs_rates[0] & IEEE80211_RATE_VAL; 197188779Ssam } 198178354Ssam vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; 199178354Ssam } 200178354Ssam vap->iv_roaming = IEEE80211_ROAMING_AUTO; 201178354Ssam 202178354Ssam vap->iv_update_beacon = null_update_beacon; 203178354Ssam vap->iv_deliver_data = ieee80211_deliver_data; 204178354Ssam 205178354Ssam /* attach support for operating mode */ 206178354Ssam ic->ic_vattach[vap->iv_opmode](vap); 207178354Ssam} 208178354Ssam 209178354Ssamvoid 210178354Ssamieee80211_proto_vdetach(struct ieee80211vap *vap) 211178354Ssam{ 212178354Ssam#define FREEAPPIE(ie) do { \ 213178354Ssam if (ie != NULL) \ 214186302Ssam free(ie, M_80211_NODE_IE); \ 215178354Ssam} while (0) 216178354Ssam /* 217178354Ssam * Detach operating mode module. 218178354Ssam */ 219178354Ssam if (vap->iv_opdetach != NULL) 220178354Ssam vap->iv_opdetach(vap); 221178354Ssam /* 222138568Ssam * This should not be needed as we detach when reseting 223138568Ssam * the state but be conservative here since the 224138568Ssam * authenticator may do things like spawn kernel threads. 225138568Ssam */ 226178354Ssam if (vap->iv_auth->ia_detach != NULL) 227178354Ssam vap->iv_auth->ia_detach(vap); 228138568Ssam /* 229138568Ssam * Detach any ACL'ator. 230138568Ssam */ 231178354Ssam if (vap->iv_acl != NULL) 232178354Ssam vap->iv_acl->iac_detach(vap); 233178354Ssam 234178354Ssam FREEAPPIE(vap->iv_appie_beacon); 235178354Ssam FREEAPPIE(vap->iv_appie_probereq); 236178354Ssam FREEAPPIE(vap->iv_appie_proberesp); 237178354Ssam FREEAPPIE(vap->iv_appie_assocreq); 238178354Ssam FREEAPPIE(vap->iv_appie_assocresp); 239178354Ssam FREEAPPIE(vap->iv_appie_wpa); 240178354Ssam#undef FREEAPPIE 241116742Ssam} 242116742Ssam 243138568Ssam/* 244138568Ssam * Simple-minded authenticator module support. 245138568Ssam */ 246138568Ssam 247138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 248138568Ssam/* XXX well-known names */ 249138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 250138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 251138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 252138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 253138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 254138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 255138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 256138568Ssam}; 257138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 258138568Ssam 259138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 260138568Ssam .ia_name = "wlan_internal", 261138568Ssam .ia_attach = NULL, 262138568Ssam .ia_detach = NULL, 263138568Ssam .ia_node_join = NULL, 264138568Ssam .ia_node_leave = NULL, 265138568Ssam}; 266138568Ssam 267138568Ssam/* 268138568Ssam * Setup internal authenticators once; they are never unregistered. 269138568Ssam */ 270138568Ssamstatic void 271138568Ssamieee80211_auth_setup(void) 272138568Ssam{ 273138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 274138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 275138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 276138568Ssam} 277138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 278138568Ssam 279138568Ssamconst struct ieee80211_authenticator * 280138568Ssamieee80211_authenticator_get(int auth) 281138568Ssam{ 282138568Ssam if (auth >= IEEE80211_AUTH_MAX) 283138568Ssam return NULL; 284138568Ssam if (authenticators[auth] == NULL) 285138568Ssam ieee80211_load_module(auth_modnames[auth]); 286138568Ssam return authenticators[auth]; 287138568Ssam} 288138568Ssam 289116742Ssamvoid 290138568Ssamieee80211_authenticator_register(int type, 291138568Ssam const struct ieee80211_authenticator *auth) 292116742Ssam{ 293138568Ssam if (type >= IEEE80211_AUTH_MAX) 294138568Ssam return; 295138568Ssam authenticators[type] = auth; 296138568Ssam} 297138568Ssam 298138568Ssamvoid 299138568Ssamieee80211_authenticator_unregister(int type) 300138568Ssam{ 301138568Ssam 302138568Ssam if (type >= IEEE80211_AUTH_MAX) 303138568Ssam return; 304138568Ssam authenticators[type] = NULL; 305138568Ssam} 306138568Ssam 307138568Ssam/* 308138568Ssam * Very simple-minded ACL module support. 309138568Ssam */ 310138568Ssam/* XXX just one for now */ 311138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 312138568Ssam 313138568Ssamvoid 314138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 315138568Ssam{ 316138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 317138568Ssam acl = iac; 318138568Ssam} 319138568Ssam 320138568Ssamvoid 321138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 322138568Ssam{ 323138568Ssam if (acl == iac) 324138568Ssam acl = NULL; 325138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 326138568Ssam} 327138568Ssam 328138568Ssamconst struct ieee80211_aclator * 329138568Ssamieee80211_aclator_get(const char *name) 330138568Ssam{ 331138568Ssam if (acl == NULL) 332138568Ssam ieee80211_load_module("wlan_acl"); 333138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 334138568Ssam} 335138568Ssam 336138568Ssamvoid 337170530Ssamieee80211_print_essid(const uint8_t *essid, int len) 338138568Ssam{ 339170530Ssam const uint8_t *p; 340116742Ssam int i; 341116742Ssam 342116742Ssam if (len > IEEE80211_NWID_LEN) 343116742Ssam len = IEEE80211_NWID_LEN; 344116742Ssam /* determine printable or not */ 345116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 346116742Ssam if (*p < ' ' || *p > 0x7e) 347116742Ssam break; 348116742Ssam } 349116742Ssam if (i == len) { 350116742Ssam printf("\""); 351116742Ssam for (i = 0, p = essid; i < len; i++, p++) 352116742Ssam printf("%c", *p); 353116742Ssam printf("\""); 354116742Ssam } else { 355116742Ssam printf("0x"); 356116742Ssam for (i = 0, p = essid; i < len; i++, p++) 357116742Ssam printf("%02x", *p); 358116742Ssam } 359116742Ssam} 360116742Ssam 361116742Ssamvoid 362170530Ssamieee80211_dump_pkt(struct ieee80211com *ic, 363170530Ssam const uint8_t *buf, int len, int rate, int rssi) 364116742Ssam{ 365138568Ssam const struct ieee80211_frame *wh; 366116742Ssam int i; 367116742Ssam 368138568Ssam wh = (const struct ieee80211_frame *)buf; 369116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 370116742Ssam case IEEE80211_FC1_DIR_NODS: 371116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 372116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 373116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 374116742Ssam break; 375116742Ssam case IEEE80211_FC1_DIR_TODS: 376116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 377116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 378116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 379116742Ssam break; 380116742Ssam case IEEE80211_FC1_DIR_FROMDS: 381116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 382116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 383116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 384116742Ssam break; 385116742Ssam case IEEE80211_FC1_DIR_DSTODS: 386170530Ssam printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); 387116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 388116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 389116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 390116742Ssam break; 391116742Ssam } 392116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 393116742Ssam case IEEE80211_FC0_TYPE_DATA: 394116742Ssam printf(" data"); 395116742Ssam break; 396116742Ssam case IEEE80211_FC0_TYPE_MGT: 397116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 398116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 399116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 400116742Ssam break; 401116742Ssam default: 402116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 403116742Ssam break; 404116742Ssam } 405170530Ssam if (IEEE80211_QOS_HAS_SEQ(wh)) { 406170530Ssam const struct ieee80211_qosframe *qwh = 407170530Ssam (const struct ieee80211_qosframe *)buf; 408170530Ssam printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, 409170530Ssam qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); 410170530Ssam } 411138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 412170530Ssam int off; 413170530Ssam 414170530Ssam off = ieee80211_anyhdrspace(ic, wh); 415170530Ssam printf(" WEP [IV %.02x %.02x %.02x", 416170530Ssam buf[off+0], buf[off+1], buf[off+2]); 417170530Ssam if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) 418170530Ssam printf(" %.02x %.02x %.02x", 419170530Ssam buf[off+4], buf[off+5], buf[off+6]); 420170530Ssam printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); 421138568Ssam } 422116742Ssam if (rate >= 0) 423116742Ssam printf(" %dM", rate / 2); 424116742Ssam if (rssi >= 0) 425116742Ssam printf(" +%d", rssi); 426116742Ssam printf("\n"); 427116742Ssam if (len > 0) { 428116742Ssam for (i = 0; i < len; i++) { 429116742Ssam if ((i & 1) == 0) 430116742Ssam printf(" "); 431116742Ssam printf("%02x", buf[i]); 432116742Ssam } 433116742Ssam printf("\n"); 434116742Ssam } 435116742Ssam} 436116742Ssam 437165887Ssamstatic __inline int 438165887Ssamfindrix(const struct ieee80211_rateset *rs, int r) 439165887Ssam{ 440165887Ssam int i; 441165887Ssam 442165887Ssam for (i = 0; i < rs->rs_nrates; i++) 443165887Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 444165887Ssam return i; 445165887Ssam return -1; 446165887Ssam} 447165887Ssam 448116742Ssamint 449167442Ssamieee80211_fix_rate(struct ieee80211_node *ni, 450167442Ssam struct ieee80211_rateset *nrs, int flags) 451116742Ssam{ 452116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 453178354Ssam struct ieee80211vap *vap = ni->ni_vap; 454148299Ssam struct ieee80211com *ic = ni->ni_ic; 455165887Ssam int i, j, rix, error; 456178354Ssam int okrate, badrate, fixedrate, ucastrate; 457165569Ssam const struct ieee80211_rateset *srs; 458170530Ssam uint8_t r; 459116742Ssam 460116742Ssam error = 0; 461170530Ssam okrate = badrate = 0; 462178354Ssam ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; 463178354Ssam if (ucastrate != IEEE80211_FIXED_RATE_NONE) { 464178354Ssam /* 465178354Ssam * Workaround awkwardness with fixed rate. We are called 466178354Ssam * to check both the legacy rate set and the HT rate set 467178354Ssam * but we must apply any legacy fixed rate check only to the 468178354Ssam * legacy rate set and vice versa. We cannot tell what type 469178354Ssam * of rate set we've been given (legacy or HT) but we can 470178354Ssam * distinguish the fixed rate type (MCS have 0x80 set). 471178354Ssam * So to deal with this the caller communicates whether to 472178354Ssam * check MCS or legacy rate using the flags and we use the 473178354Ssam * type of any fixed rate to avoid applying an MCS to a 474178354Ssam * legacy rate and vice versa. 475178354Ssam */ 476178354Ssam if (ucastrate & 0x80) { 477178354Ssam if (flags & IEEE80211_F_DOFRATE) 478178354Ssam flags &= ~IEEE80211_F_DOFRATE; 479178354Ssam } else if ((ucastrate & 0x80) == 0) { 480178354Ssam if (flags & IEEE80211_F_DOFMCS) 481178354Ssam flags &= ~IEEE80211_F_DOFMCS; 482178354Ssam } 483178354Ssam /* NB: required to make MCS match below work */ 484178354Ssam ucastrate &= IEEE80211_RATE_VAL; 485178354Ssam } 486170530Ssam fixedrate = IEEE80211_FIXED_RATE_NONE; 487178354Ssam /* 488178354Ssam * XXX we are called to process both MCS and legacy rates; 489178354Ssam * we must use the appropriate basic rate set or chaos will 490178354Ssam * ensue; for now callers that want MCS must supply 491178354Ssam * IEEE80211_F_DOBRS; at some point we'll need to split this 492178354Ssam * function so there are two variants, one for MCS and one 493178354Ssam * for legacy rates. 494178354Ssam */ 495178354Ssam if (flags & IEEE80211_F_DOBRS) 496178354Ssam srs = (const struct ieee80211_rateset *) 497178354Ssam ieee80211_get_suphtrates(ic, ni->ni_chan); 498178354Ssam else 499178354Ssam srs = ieee80211_get_suprates(ic, ni->ni_chan); 500120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 501116742Ssam if (flags & IEEE80211_F_DOSORT) { 502116742Ssam /* 503116742Ssam * Sort rates. 504116742Ssam */ 505116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 506116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 507116742Ssam r = nrs->rs_rates[i]; 508116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 509116742Ssam nrs->rs_rates[j] = r; 510116742Ssam } 511116742Ssam } 512116742Ssam } 513116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 514116742Ssam badrate = r; 515165887Ssam /* 516170530Ssam * Check for fixed rate. 517170530Ssam */ 518178354Ssam if (r == ucastrate) 519170530Ssam fixedrate = r; 520170530Ssam /* 521165887Ssam * Check against supported rates. 522165887Ssam */ 523165887Ssam rix = findrix(srs, r); 524116742Ssam if (flags & IEEE80211_F_DONEGO) { 525165887Ssam if (rix < 0) { 526120482Ssam /* 527120482Ssam * A rate in the node's rate set is not 528120482Ssam * supported. If this is a basic rate and we 529165887Ssam * are operating as a STA then this is an error. 530120482Ssam * Otherwise we just discard/ignore the rate. 531120482Ssam */ 532165887Ssam if ((flags & IEEE80211_F_JOIN) && 533120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 534116742Ssam error++; 535165887Ssam } else if ((flags & IEEE80211_F_JOIN) == 0) { 536165887Ssam /* 537165887Ssam * Overwrite with the supported rate 538165887Ssam * value so any basic rate bit is set. 539165887Ssam */ 540165887Ssam nrs->rs_rates[i] = srs->rs_rates[rix]; 541116742Ssam } 542116742Ssam } 543165887Ssam if ((flags & IEEE80211_F_DODEL) && rix < 0) { 544116742Ssam /* 545116742Ssam * Delete unacceptable rates. 546116742Ssam */ 547165887Ssam nrs->rs_nrates--; 548165887Ssam for (j = i; j < nrs->rs_nrates; j++) 549165887Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 550165887Ssam nrs->rs_rates[j] = 0; 551165887Ssam continue; 552116742Ssam } 553165887Ssam if (rix >= 0) 554116742Ssam okrate = nrs->rs_rates[i]; 555116742Ssam i++; 556116742Ssam } 557138568Ssam if (okrate == 0 || error != 0 || 558178354Ssam ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && 559178354Ssam fixedrate != ucastrate)) { 560178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 561178354Ssam "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " 562178354Ssam "ucastrate %x\n", __func__, fixedrate, ucastrate, flags); 563116742Ssam return badrate | IEEE80211_RATE_BASIC; 564178354Ssam } else 565116742Ssam return RV(okrate); 566116742Ssam#undef RV 567116742Ssam} 568116742Ssam 569138568Ssam/* 570138568Ssam * Reset 11g-related state. 571138568Ssam */ 572138568Ssamvoid 573138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 574138568Ssam{ 575138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 576138568Ssam ic->ic_nonerpsta = 0; 577138568Ssam ic->ic_longslotsta = 0; 578138568Ssam /* 579138568Ssam * Short slot time is enabled only when operating in 11g 580138568Ssam * and not in an IBSS. We must also honor whether or not 581138568Ssam * the driver is capable of doing it. 582138568Ssam */ 583138568Ssam ieee80211_set_shortslottime(ic, 584170530Ssam IEEE80211_IS_CHAN_A(ic->ic_curchan) || 585170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_curchan) || 586170530Ssam (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 587138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 588138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 589138568Ssam /* 590138568Ssam * Set short preamble and ERP barker-preamble flags. 591138568Ssam */ 592170530Ssam if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 593138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 594138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 595138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 596138568Ssam } else { 597138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 598138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 599138568Ssam } 600138568Ssam} 601138568Ssam 602138568Ssam/* 603138568Ssam * Set the short slot time state and notify the driver. 604138568Ssam */ 605138568Ssamvoid 606138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 607138568Ssam{ 608138568Ssam if (onoff) 609138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 610138568Ssam else 611138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 612138568Ssam /* notify driver */ 613138568Ssam if (ic->ic_updateslot != NULL) 614138568Ssam ic->ic_updateslot(ic->ic_ifp); 615138568Ssam} 616138568Ssam 617138568Ssam/* 618138568Ssam * Check if the specified rate set supports ERP. 619138568Ssam * NB: the rate set is assumed to be sorted. 620138568Ssam */ 621138568Ssamint 622178354Ssamieee80211_iserp_rateset(const struct ieee80211_rateset *rs) 623138568Ssam{ 624138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 625138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 626138568Ssam int i, j; 627138568Ssam 628138568Ssam if (rs->rs_nrates < N(rates)) 629138568Ssam return 0; 630138568Ssam for (i = 0; i < N(rates); i++) { 631138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 632138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 633138568Ssam if (rates[i] == r) 634138568Ssam goto next; 635138568Ssam if (r > rates[i]) 636138568Ssam return 0; 637138568Ssam } 638138568Ssam return 0; 639138568Ssam next: 640138568Ssam ; 641138568Ssam } 642138568Ssam return 1; 643138568Ssam#undef N 644138568Ssam} 645138568Ssam 646138568Ssam/* 647178354Ssam * Mark the basic rates for the rate table based on the 648138568Ssam * operating mode. For real 11g we mark all the 11b rates 649138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 650138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 651138568Ssam * the basic OFDM rates. 652138568Ssam */ 653178354Ssamstatic void 654178354Ssamsetbasicrates(struct ieee80211_rateset *rs, 655178354Ssam enum ieee80211_phymode mode, int add) 656138568Ssam{ 657170530Ssam static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { 658188780Ssam [IEEE80211_MODE_11A] = { 3, { 12, 24, 48 } }, 659188780Ssam [IEEE80211_MODE_11B] = { 2, { 2, 4 } }, 660188780Ssam /* NB: mixed b/g */ 661188780Ssam [IEEE80211_MODE_11G] = { 4, { 2, 4, 11, 22 } }, 662188780Ssam [IEEE80211_MODE_TURBO_A] = { 3, { 12, 24, 48 } }, 663188780Ssam [IEEE80211_MODE_TURBO_G] = { 4, { 2, 4, 11, 22 } }, 664188780Ssam [IEEE80211_MODE_STURBO_A] = { 3, { 12, 24, 48 } }, 665188782Ssam [IEEE80211_MODE_HALF] = { 3, { 6, 12, 24 } }, 666188782Ssam [IEEE80211_MODE_QUARTER] = { 3, { 3, 6, 12 } }, 667188780Ssam [IEEE80211_MODE_11NA] = { 3, { 12, 24, 48 } }, 668188780Ssam /* NB: mixed b/g */ 669188780Ssam [IEEE80211_MODE_11NG] = { 4, { 2, 4, 11, 22 } }, 670138568Ssam }; 671138568Ssam int i, j; 672138568Ssam 673138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 674178354Ssam if (!add) 675178354Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 676138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 677138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 678138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 679138568Ssam break; 680138568Ssam } 681138568Ssam } 682138568Ssam} 683138568Ssam 684138568Ssam/* 685178354Ssam * Set the basic rates in a rate set. 686138568Ssam */ 687178354Ssamvoid 688178354Ssamieee80211_setbasicrates(struct ieee80211_rateset *rs, 689178354Ssam enum ieee80211_phymode mode) 690178354Ssam{ 691178354Ssam setbasicrates(rs, mode, 0); 692178354Ssam} 693178354Ssam 694178354Ssam/* 695178354Ssam * Add basic rates to a rate set. 696178354Ssam */ 697178354Ssamvoid 698178354Ssamieee80211_addbasicrates(struct ieee80211_rateset *rs, 699178354Ssam enum ieee80211_phymode mode) 700178354Ssam{ 701178354Ssam setbasicrates(rs, mode, 1); 702178354Ssam} 703178354Ssam 704178354Ssam/* 705178354Ssam * WME protocol support. 706178354Ssam * 707178354Ssam * The default 11a/b/g/n parameters come from the WiFi Alliance WMM 708178354Ssam * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n 709178354Ssam * Draft 2.0 Test Plan (Appendix D). 710178354Ssam * 711178354Ssam * Static/Dynamic Turbo mode settings come from Atheros. 712178354Ssam */ 713138568Ssamtypedef struct phyParamType { 714178354Ssam uint8_t aifsn; 715178354Ssam uint8_t logcwmin; 716178354Ssam uint8_t logcwmax; 717178354Ssam uint16_t txopLimit; 718178354Ssam uint8_t acm; 719138568Ssam} paramType; 720138568Ssam 721138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 722188780Ssam [IEEE80211_MODE_AUTO] = { 3, 4, 6, 0, 0 }, 723188780Ssam [IEEE80211_MODE_11A] = { 3, 4, 6, 0, 0 }, 724188780Ssam [IEEE80211_MODE_11B] = { 3, 4, 6, 0, 0 }, 725188780Ssam [IEEE80211_MODE_11G] = { 3, 4, 6, 0, 0 }, 726188780Ssam [IEEE80211_MODE_FH] = { 3, 4, 6, 0, 0 }, 727188780Ssam [IEEE80211_MODE_TURBO_A]= { 2, 3, 5, 0, 0 }, 728188780Ssam [IEEE80211_MODE_TURBO_G]= { 2, 3, 5, 0, 0 }, 729188780Ssam [IEEE80211_MODE_STURBO_A]={ 2, 3, 5, 0, 0 }, 730188782Ssam [IEEE80211_MODE_HALF] = { 3, 4, 6, 0, 0 }, 731188782Ssam [IEEE80211_MODE_QUARTER]= { 3, 4, 6, 0, 0 }, 732188780Ssam [IEEE80211_MODE_11NA] = { 3, 4, 6, 0, 0 }, 733188780Ssam [IEEE80211_MODE_11NG] = { 3, 4, 6, 0, 0 }, 734138568Ssam}; 735138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 736188780Ssam [IEEE80211_MODE_AUTO] = { 7, 4, 10, 0, 0 }, 737188780Ssam [IEEE80211_MODE_11A] = { 7, 4, 10, 0, 0 }, 738188780Ssam [IEEE80211_MODE_11B] = { 7, 4, 10, 0, 0 }, 739188780Ssam [IEEE80211_MODE_11G] = { 7, 4, 10, 0, 0 }, 740188780Ssam [IEEE80211_MODE_FH] = { 7, 4, 10, 0, 0 }, 741188780Ssam [IEEE80211_MODE_TURBO_A]= { 7, 3, 10, 0, 0 }, 742188780Ssam [IEEE80211_MODE_TURBO_G]= { 7, 3, 10, 0, 0 }, 743188780Ssam [IEEE80211_MODE_STURBO_A]={ 7, 3, 10, 0, 0 }, 744188782Ssam [IEEE80211_MODE_HALF] = { 7, 4, 10, 0, 0 }, 745188782Ssam [IEEE80211_MODE_QUARTER]= { 7, 4, 10, 0, 0 }, 746188780Ssam [IEEE80211_MODE_11NA] = { 7, 4, 10, 0, 0 }, 747188780Ssam [IEEE80211_MODE_11NG] = { 7, 4, 10, 0, 0 }, 748138568Ssam}; 749138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 750188780Ssam [IEEE80211_MODE_AUTO] = { 1, 3, 4, 94, 0 }, 751188780Ssam [IEEE80211_MODE_11A] = { 1, 3, 4, 94, 0 }, 752188780Ssam [IEEE80211_MODE_11B] = { 1, 3, 4, 188, 0 }, 753188780Ssam [IEEE80211_MODE_11G] = { 1, 3, 4, 94, 0 }, 754188780Ssam [IEEE80211_MODE_FH] = { 1, 3, 4, 188, 0 }, 755188780Ssam [IEEE80211_MODE_TURBO_A]= { 1, 2, 3, 94, 0 }, 756188780Ssam [IEEE80211_MODE_TURBO_G]= { 1, 2, 3, 94, 0 }, 757188780Ssam [IEEE80211_MODE_STURBO_A]={ 1, 2, 3, 94, 0 }, 758188782Ssam [IEEE80211_MODE_HALF] = { 1, 3, 4, 94, 0 }, 759188782Ssam [IEEE80211_MODE_QUARTER]= { 1, 3, 4, 94, 0 }, 760188780Ssam [IEEE80211_MODE_11NA] = { 1, 3, 4, 94, 0 }, 761188780Ssam [IEEE80211_MODE_11NG] = { 1, 3, 4, 94, 0 }, 762138568Ssam}; 763138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 764188780Ssam [IEEE80211_MODE_AUTO] = { 1, 2, 3, 47, 0 }, 765188780Ssam [IEEE80211_MODE_11A] = { 1, 2, 3, 47, 0 }, 766188780Ssam [IEEE80211_MODE_11B] = { 1, 2, 3, 102, 0 }, 767188780Ssam [IEEE80211_MODE_11G] = { 1, 2, 3, 47, 0 }, 768188780Ssam [IEEE80211_MODE_FH] = { 1, 2, 3, 102, 0 }, 769188780Ssam [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, 770188780Ssam [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, 771188780Ssam [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, 772188782Ssam [IEEE80211_MODE_HALF] = { 1, 2, 3, 47, 0 }, 773188782Ssam [IEEE80211_MODE_QUARTER]= { 1, 2, 3, 47, 0 }, 774188780Ssam [IEEE80211_MODE_11NA] = { 1, 2, 3, 47, 0 }, 775188780Ssam [IEEE80211_MODE_11NG] = { 1, 2, 3, 47, 0 }, 776138568Ssam}; 777138568Ssam 778138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 779188780Ssam [IEEE80211_MODE_AUTO] = { 3, 4, 10, 0, 0 }, 780188780Ssam [IEEE80211_MODE_11A] = { 3, 4, 10, 0, 0 }, 781188780Ssam [IEEE80211_MODE_11B] = { 3, 4, 10, 0, 0 }, 782188780Ssam [IEEE80211_MODE_11G] = { 3, 4, 10, 0, 0 }, 783188780Ssam [IEEE80211_MODE_FH] = { 3, 4, 10, 0, 0 }, 784188780Ssam [IEEE80211_MODE_TURBO_A]= { 2, 3, 10, 0, 0 }, 785188780Ssam [IEEE80211_MODE_TURBO_G]= { 2, 3, 10, 0, 0 }, 786188780Ssam [IEEE80211_MODE_STURBO_A]={ 2, 3, 10, 0, 0 }, 787188782Ssam [IEEE80211_MODE_HALF] = { 3, 4, 10, 0, 0 }, 788188782Ssam [IEEE80211_MODE_QUARTER]= { 3, 4, 10, 0, 0 }, 789188780Ssam [IEEE80211_MODE_11NA] = { 3, 4, 10, 0, 0 }, 790188780Ssam [IEEE80211_MODE_11NG] = { 3, 4, 10, 0, 0 }, 791138568Ssam}; 792138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 793188780Ssam [IEEE80211_MODE_AUTO] = { 2, 3, 4, 94, 0 }, 794188780Ssam [IEEE80211_MODE_11A] = { 2, 3, 4, 94, 0 }, 795188780Ssam [IEEE80211_MODE_11B] = { 2, 3, 4, 188, 0 }, 796188780Ssam [IEEE80211_MODE_11G] = { 2, 3, 4, 94, 0 }, 797188780Ssam [IEEE80211_MODE_FH] = { 2, 3, 4, 188, 0 }, 798188780Ssam [IEEE80211_MODE_TURBO_A]= { 2, 2, 3, 94, 0 }, 799188780Ssam [IEEE80211_MODE_TURBO_G]= { 2, 2, 3, 94, 0 }, 800188780Ssam [IEEE80211_MODE_STURBO_A]={ 2, 2, 3, 94, 0 }, 801188782Ssam [IEEE80211_MODE_HALF] = { 2, 3, 4, 94, 0 }, 802188782Ssam [IEEE80211_MODE_QUARTER]= { 2, 3, 4, 94, 0 }, 803188780Ssam [IEEE80211_MODE_11NA] = { 2, 3, 4, 94, 0 }, 804188780Ssam [IEEE80211_MODE_11NG] = { 2, 3, 4, 94, 0 }, 805138568Ssam}; 806138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 807188780Ssam [IEEE80211_MODE_AUTO] = { 2, 2, 3, 47, 0 }, 808188780Ssam [IEEE80211_MODE_11A] = { 2, 2, 3, 47, 0 }, 809188780Ssam [IEEE80211_MODE_11B] = { 2, 2, 3, 102, 0 }, 810188780Ssam [IEEE80211_MODE_11G] = { 2, 2, 3, 47, 0 }, 811188780Ssam [IEEE80211_MODE_FH] = { 2, 2, 3, 102, 0 }, 812188780Ssam [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, 813188780Ssam [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, 814188780Ssam [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, 815188782Ssam [IEEE80211_MODE_HALF] = { 2, 2, 3, 47, 0 }, 816188782Ssam [IEEE80211_MODE_QUARTER]= { 2, 2, 3, 47, 0 }, 817188780Ssam [IEEE80211_MODE_11NA] = { 2, 2, 3, 47, 0 }, 818188780Ssam [IEEE80211_MODE_11NG] = { 2, 2, 3, 47, 0 }, 819138568Ssam}; 820138568Ssam 821178354Ssamstatic void 822178354Ssamieee80211_wme_initparams_locked(struct ieee80211vap *vap) 823138568Ssam{ 824178354Ssam struct ieee80211com *ic = vap->iv_ic; 825138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 826138568Ssam const paramType *pPhyParam, *pBssPhyParam; 827138568Ssam struct wmeParams *wmep; 828170530Ssam enum ieee80211_phymode mode; 829138568Ssam int i; 830138568Ssam 831178354Ssam IEEE80211_LOCK_ASSERT(ic); 832178354Ssam 833138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 834138568Ssam return; 835138568Ssam 836170530Ssam /* 837170530Ssam * Select mode; we can be called early in which case we 838170530Ssam * always use auto mode. We know we'll be called when 839170530Ssam * entering the RUN state with bsschan setup properly 840170530Ssam * so state will eventually get set correctly 841170530Ssam */ 842170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 843170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 844170530Ssam else 845170530Ssam mode = IEEE80211_MODE_AUTO; 846138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 847138568Ssam switch (i) { 848138568Ssam case WME_AC_BK: 849170530Ssam pPhyParam = &phyParamForAC_BK[mode]; 850170530Ssam pBssPhyParam = &phyParamForAC_BK[mode]; 851138568Ssam break; 852138568Ssam case WME_AC_VI: 853170530Ssam pPhyParam = &phyParamForAC_VI[mode]; 854170530Ssam pBssPhyParam = &bssPhyParamForAC_VI[mode]; 855138568Ssam break; 856138568Ssam case WME_AC_VO: 857170530Ssam pPhyParam = &phyParamForAC_VO[mode]; 858170530Ssam pBssPhyParam = &bssPhyParamForAC_VO[mode]; 859138568Ssam break; 860138568Ssam case WME_AC_BE: 861138568Ssam default: 862170530Ssam pPhyParam = &phyParamForAC_BE[mode]; 863170530Ssam pBssPhyParam = &bssPhyParamForAC_BE[mode]; 864138568Ssam break; 865138568Ssam } 866138568Ssam 867138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 868138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 869138568Ssam wmep->wmep_acm = pPhyParam->acm; 870138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 871138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 872138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 873138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 874138568Ssam } else { 875138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 876138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 877138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 878138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 879138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 880138568Ssam 881138568Ssam } 882178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 883138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 884138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 885138568Ssam , ieee80211_wme_acnames[i] 886138568Ssam , wmep->wmep_acm 887138568Ssam , wmep->wmep_aifsn 888138568Ssam , wmep->wmep_logcwmin 889138568Ssam , wmep->wmep_logcwmax 890138568Ssam , wmep->wmep_txopLimit 891138568Ssam ); 892138568Ssam 893138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 894138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 895138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 896138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 897138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 898138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 899178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 900138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 901138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 902138568Ssam , ieee80211_wme_acnames[i] 903138568Ssam , wmep->wmep_acm 904138568Ssam , wmep->wmep_aifsn 905138568Ssam , wmep->wmep_logcwmin 906138568Ssam , wmep->wmep_logcwmax 907138568Ssam , wmep->wmep_txopLimit 908138568Ssam ); 909138568Ssam } 910138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 911178354Ssam if (vap->iv_bss != NULL) { 912138568Ssam /* 913138568Ssam * Calculate agressive mode switching threshold based 914138568Ssam * on beacon interval. This doesn't need locking since 915138568Ssam * we're only called before entering the RUN state at 916138568Ssam * which point we start sending beacon frames. 917138568Ssam */ 918138568Ssam wme->wme_hipri_switch_thresh = 919178354Ssam (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; 920178354Ssam ieee80211_wme_updateparams(vap); 921138568Ssam } 922138568Ssam} 923138568Ssam 924178354Ssamvoid 925178354Ssamieee80211_wme_initparams(struct ieee80211vap *vap) 926178354Ssam{ 927178354Ssam struct ieee80211com *ic = vap->iv_ic; 928178354Ssam 929178354Ssam IEEE80211_LOCK(ic); 930178354Ssam ieee80211_wme_initparams_locked(vap); 931178354Ssam IEEE80211_UNLOCK(ic); 932178354Ssam} 933178354Ssam 934138568Ssam/* 935138568Ssam * Update WME parameters for ourself and the BSS. 936138568Ssam */ 937138568Ssamvoid 938178354Ssamieee80211_wme_updateparams_locked(struct ieee80211vap *vap) 939138568Ssam{ 940138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 941188780Ssam [IEEE80211_MODE_AUTO] = { 2, 4, 10, 64, 0 }, 942188780Ssam [IEEE80211_MODE_11A] = { 2, 4, 10, 64, 0 }, 943188780Ssam [IEEE80211_MODE_11B] = { 2, 5, 10, 64, 0 }, 944188780Ssam [IEEE80211_MODE_11G] = { 2, 4, 10, 64, 0 }, 945188780Ssam [IEEE80211_MODE_FH] = { 2, 5, 10, 64, 0 }, 946188780Ssam [IEEE80211_MODE_TURBO_A] = { 1, 3, 10, 64, 0 }, 947188780Ssam [IEEE80211_MODE_TURBO_G] = { 1, 3, 10, 64, 0 }, 948188780Ssam [IEEE80211_MODE_STURBO_A] = { 1, 3, 10, 64, 0 }, 949188782Ssam [IEEE80211_MODE_HALF] = { 2, 4, 10, 64, 0 }, 950188782Ssam [IEEE80211_MODE_QUARTER] = { 2, 4, 10, 64, 0 }, 951188780Ssam [IEEE80211_MODE_11NA] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/ 952188780Ssam [IEEE80211_MODE_11NG] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/ 953138568Ssam }; 954178354Ssam struct ieee80211com *ic = vap->iv_ic; 955138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 956138568Ssam const struct wmeParams *wmep; 957138568Ssam struct wmeParams *chanp, *bssp; 958170530Ssam enum ieee80211_phymode mode; 959138568Ssam int i; 960138568Ssam 961138568Ssam /* set up the channel access parameters for the physical device */ 962138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 963138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 964138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 965138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 966138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 967138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 968138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 969138568Ssam 970138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 971138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 972138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 973138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 974138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 975138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 976138568Ssam } 977138568Ssam 978138568Ssam /* 979170530Ssam * Select mode; we can be called early in which case we 980170530Ssam * always use auto mode. We know we'll be called when 981170530Ssam * entering the RUN state with bsschan setup properly 982170530Ssam * so state will eventually get set correctly 983170530Ssam */ 984170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 985170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 986170530Ssam else 987170530Ssam mode = IEEE80211_MODE_AUTO; 988170530Ssam 989170530Ssam /* 990138568Ssam * This implements agressive mode as found in certain 991138568Ssam * vendors' AP's. When there is significant high 992138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 993138568Ssam * traffic by using conservative parameters. Otherwise 994138568Ssam * BE uses agressive params to optimize performance of 995138568Ssam * legacy/non-QoS traffic. 996138568Ssam */ 997178354Ssam if ((vap->iv_opmode == IEEE80211_M_HOSTAP && 998156524Ssam (wme->wme_flags & WME_F_AGGRMODE) != 0) || 999178354Ssam (vap->iv_opmode == IEEE80211_M_STA && 1000178354Ssam (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 1001178354Ssam (vap->iv_flags & IEEE80211_F_WME) == 0) { 1002138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 1003138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 1004138568Ssam 1005170530Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; 1006138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 1007170530Ssam phyParam[mode].logcwmin; 1008138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 1009170530Ssam phyParam[mode].logcwmax; 1010138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 1011178354Ssam (vap->iv_flags & IEEE80211_F_BURST) ? 1012170530Ssam phyParam[mode].txopLimit : 0; 1013178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1014138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 1015138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 1016138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 1017138568Ssam , chanp->wmep_acm 1018138568Ssam , chanp->wmep_aifsn 1019138568Ssam , chanp->wmep_logcwmin 1020138568Ssam , chanp->wmep_logcwmax 1021138568Ssam , chanp->wmep_txopLimit 1022138568Ssam ); 1023138568Ssam } 1024138568Ssam 1025178354Ssam /* XXX multi-bss */ 1026178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 1027156524Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 1028188780Ssam static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 1029188780Ssam [IEEE80211_MODE_AUTO] = 3, 1030188780Ssam [IEEE80211_MODE_11A] = 3, 1031188780Ssam [IEEE80211_MODE_11B] = 4, 1032188780Ssam [IEEE80211_MODE_11G] = 3, 1033188780Ssam [IEEE80211_MODE_FH] = 4, 1034188780Ssam [IEEE80211_MODE_TURBO_A] = 3, 1035188780Ssam [IEEE80211_MODE_TURBO_G] = 3, 1036188780Ssam [IEEE80211_MODE_STURBO_A] = 3, 1037188782Ssam [IEEE80211_MODE_HALF] = 3, 1038188782Ssam [IEEE80211_MODE_QUARTER] = 3, 1039188780Ssam [IEEE80211_MODE_11NA] = 3, 1040188780Ssam [IEEE80211_MODE_11NG] = 3, 1041138568Ssam }; 1042138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 1043138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 1044138568Ssam 1045170530Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; 1046178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1047138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 1048138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 1049138568Ssam , chanp->wmep_logcwmin 1050138568Ssam ); 1051138568Ssam } 1052178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 1053138568Ssam /* 1054138568Ssam * Arrange for a beacon update and bump the parameter 1055138568Ssam * set number so associated stations load the new values. 1056138568Ssam */ 1057138568Ssam wme->wme_bssChanParams.cap_info = 1058138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 1059178354Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); 1060138568Ssam } 1061138568Ssam 1062138568Ssam wme->wme_update(ic); 1063138568Ssam 1064178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1065138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 1066178354Ssam vap->iv_opmode == IEEE80211_M_STA ? 1067138568Ssam wme->wme_wmeChanParams.cap_info : 1068138568Ssam wme->wme_bssChanParams.cap_info); 1069138568Ssam} 1070138568Ssam 1071138568Ssamvoid 1072178354Ssamieee80211_wme_updateparams(struct ieee80211vap *vap) 1073138568Ssam{ 1074178354Ssam struct ieee80211com *ic = vap->iv_ic; 1075138568Ssam 1076138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 1077178354Ssam IEEE80211_LOCK(ic); 1078178354Ssam ieee80211_wme_updateparams_locked(vap); 1079178354Ssam IEEE80211_UNLOCK(ic); 1080138568Ssam } 1081138568Ssam} 1082138568Ssam 1083178354Ssamstatic void 1084178354Ssamparent_updown(void *arg, int npending) 1085178354Ssam{ 1086178354Ssam struct ifnet *parent = arg; 1087178354Ssam 1088178354Ssam parent->if_ioctl(parent, SIOCSIFFLAGS, NULL); 1089178354Ssam} 1090178354Ssam 1091170530Ssam/* 1092188533Sthompsa * Block until the parent is in a known state. This is 1093188533Sthompsa * used after any operations that dispatch a task (e.g. 1094188533Sthompsa * to auto-configure the parent device up/down). 1095188533Sthompsa */ 1096188533Sthompsavoid 1097188533Sthompsaieee80211_waitfor_parent(struct ieee80211com *ic) 1098188533Sthompsa{ 1099188533Sthompsa taskqueue_drain(taskqueue_thread, &ic->ic_parent_task); 1100188533Sthompsa} 1101188533Sthompsa 1102188533Sthompsa/* 1103178354Ssam * Start a vap running. If this is the first vap to be 1104178354Ssam * set running on the underlying device then we 1105178354Ssam * automatically bring the device up. 1106170530Ssam */ 1107178354Ssamvoid 1108178354Ssamieee80211_start_locked(struct ieee80211vap *vap) 1109170530Ssam{ 1110178354Ssam struct ifnet *ifp = vap->iv_ifp; 1111178354Ssam struct ieee80211com *ic = vap->iv_ic; 1112178354Ssam struct ifnet *parent = ic->ic_ifp; 1113170530Ssam 1114178354Ssam IEEE80211_LOCK_ASSERT(ic); 1115178354Ssam 1116178354Ssam IEEE80211_DPRINTF(vap, 1117170530Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1118178354Ssam "start running, %d vaps running\n", ic->ic_nrunning); 1119170530Ssam 1120178354Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1121178354Ssam /* 1122178354Ssam * Mark us running. Note that it's ok to do this first; 1123178354Ssam * if we need to bring the parent device up we defer that 1124178354Ssam * to avoid dropping the com lock. We expect the device 1125178354Ssam * to respond to being marked up by calling back into us 1126178354Ssam * through ieee80211_start_all at which point we'll come 1127178354Ssam * back in here and complete the work. 1128178354Ssam */ 1129178354Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 1130178354Ssam /* 1131178354Ssam * We are not running; if this we are the first vap 1132178354Ssam * to be brought up auto-up the parent if necessary. 1133178354Ssam */ 1134178354Ssam if (ic->ic_nrunning++ == 0 && 1135178354Ssam (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1136178354Ssam IEEE80211_DPRINTF(vap, 1137178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1138178354Ssam "%s: up parent %s\n", __func__, parent->if_xname); 1139178354Ssam parent->if_flags |= IFF_UP; 1140178354Ssam taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); 1141178354Ssam return; 1142178354Ssam } 1143178354Ssam } 1144170530Ssam /* 1145178354Ssam * If the parent is up and running, then kick the 1146178354Ssam * 802.11 state machine as appropriate. 1147170530Ssam */ 1148178354Ssam if ((parent->if_drv_flags & IFF_DRV_RUNNING) && 1149178354Ssam vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { 1150178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) { 1151178354Ssam#if 0 1152178354Ssam /* XXX bypasses scan too easily; disable for now */ 1153178354Ssam /* 1154178354Ssam * Try to be intelligent about clocking the state 1155178354Ssam * machine. If we're currently in RUN state then 1156178354Ssam * we should be able to apply any new state/parameters 1157178354Ssam * simply by re-associating. Otherwise we need to 1158178354Ssam * re-scan to select an appropriate ap. 1159178354Ssam */ 1160178354Ssam if (vap->iv_state >= IEEE80211_S_RUN) 1161178354Ssam ieee80211_new_state_locked(vap, 1162178354Ssam IEEE80211_S_ASSOC, 1); 1163178354Ssam else 1164178354Ssam#endif 1165178354Ssam ieee80211_new_state_locked(vap, 1166178354Ssam IEEE80211_S_SCAN, 0); 1167170530Ssam } else { 1168170530Ssam /* 1169178354Ssam * For monitor+wds mode there's nothing to do but 1170178354Ssam * start running. Otherwise if this is the first 1171170530Ssam * vap to be brought up, start a scan which may be 1172170530Ssam * preempted if the station is locked to a particular 1173170530Ssam * channel. 1174170530Ssam */ 1175178354Ssam /* XXX needed? */ 1176178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0); 1177178354Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR || 1178178354Ssam vap->iv_opmode == IEEE80211_M_WDS) 1179178354Ssam ieee80211_new_state_locked(vap, 1180178354Ssam IEEE80211_S_RUN, -1); 1181178354Ssam else 1182178354Ssam ieee80211_new_state_locked(vap, 1183178354Ssam IEEE80211_S_SCAN, 0); 1184170530Ssam } 1185170530Ssam } 1186170530Ssam} 1187170530Ssam 1188170530Ssam/* 1189178354Ssam * Start a single vap. 1190178354Ssam */ 1191178354Ssamvoid 1192178354Ssamieee80211_init(void *arg) 1193178354Ssam{ 1194178354Ssam struct ieee80211vap *vap = arg; 1195178354Ssam 1196178354Ssam /* 1197178354Ssam * This routine is publicly accessible through the vap's 1198178354Ssam * if_init method so guard against calls during detach. 1199178354Ssam * ieee80211_vap_detach null's the backpointer before 1200178354Ssam * tearing down state to signal any callback should be 1201178354Ssam * rejected/ignored. 1202178354Ssam */ 1203178354Ssam if (vap != NULL) { 1204178354Ssam IEEE80211_DPRINTF(vap, 1205178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1206178354Ssam "%s\n", __func__); 1207178354Ssam 1208178354Ssam IEEE80211_LOCK(vap->iv_ic); 1209178354Ssam ieee80211_start_locked(vap); 1210178354Ssam IEEE80211_UNLOCK(vap->iv_ic); 1211178354Ssam } 1212178354Ssam} 1213178354Ssam 1214178354Ssam/* 1215178354Ssam * Start all runnable vap's on a device. 1216178354Ssam */ 1217178354Ssamvoid 1218178354Ssamieee80211_start_all(struct ieee80211com *ic) 1219178354Ssam{ 1220178354Ssam struct ieee80211vap *vap; 1221178354Ssam 1222178354Ssam IEEE80211_LOCK(ic); 1223178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1224178354Ssam struct ifnet *ifp = vap->iv_ifp; 1225178354Ssam if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ 1226178354Ssam ieee80211_start_locked(vap); 1227178354Ssam } 1228178354Ssam IEEE80211_UNLOCK(ic); 1229178354Ssam} 1230178354Ssam 1231178354Ssam/* 1232178354Ssam * Stop a vap. We force it down using the state machine 1233178354Ssam * then mark it's ifnet not running. If this is the last 1234178354Ssam * vap running on the underlying device then we close it 1235178354Ssam * too to insure it will be properly initialized when the 1236178354Ssam * next vap is brought up. 1237178354Ssam */ 1238178354Ssamvoid 1239178354Ssamieee80211_stop_locked(struct ieee80211vap *vap) 1240178354Ssam{ 1241178354Ssam struct ieee80211com *ic = vap->iv_ic; 1242178354Ssam struct ifnet *ifp = vap->iv_ifp; 1243178354Ssam struct ifnet *parent = ic->ic_ifp; 1244178354Ssam 1245178354Ssam IEEE80211_LOCK_ASSERT(ic); 1246178354Ssam 1247178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1248178354Ssam "stop running, %d vaps running\n", ic->ic_nrunning); 1249178354Ssam 1250178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); 1251178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1252178354Ssam ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */ 1253178354Ssam if (--ic->ic_nrunning == 0 && 1254178354Ssam (parent->if_drv_flags & IFF_DRV_RUNNING)) { 1255178354Ssam IEEE80211_DPRINTF(vap, 1256178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1257178354Ssam "down parent %s\n", parent->if_xname); 1258178354Ssam parent->if_flags &= ~IFF_UP; 1259178354Ssam taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); 1260178354Ssam } 1261178354Ssam } 1262178354Ssam} 1263178354Ssam 1264178354Ssamvoid 1265178354Ssamieee80211_stop(struct ieee80211vap *vap) 1266178354Ssam{ 1267178354Ssam struct ieee80211com *ic = vap->iv_ic; 1268178354Ssam 1269178354Ssam IEEE80211_LOCK(ic); 1270178354Ssam ieee80211_stop_locked(vap); 1271178354Ssam IEEE80211_UNLOCK(ic); 1272178354Ssam} 1273178354Ssam 1274178354Ssam/* 1275178354Ssam * Stop all vap's running on a device. 1276178354Ssam */ 1277178354Ssamvoid 1278178354Ssamieee80211_stop_all(struct ieee80211com *ic) 1279178354Ssam{ 1280178354Ssam struct ieee80211vap *vap; 1281178354Ssam 1282178354Ssam IEEE80211_LOCK(ic); 1283178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1284178354Ssam struct ifnet *ifp = vap->iv_ifp; 1285178354Ssam if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ 1286178354Ssam ieee80211_stop_locked(vap); 1287178354Ssam } 1288178354Ssam IEEE80211_UNLOCK(ic); 1289188533Sthompsa 1290188533Sthompsa ieee80211_waitfor_parent(ic); 1291178354Ssam} 1292178354Ssam 1293178354Ssam/* 1294179391Ssam * Stop all vap's running on a device and arrange 1295179391Ssam * for those that were running to be resumed. 1296179391Ssam */ 1297179391Ssamvoid 1298179391Ssamieee80211_suspend_all(struct ieee80211com *ic) 1299179391Ssam{ 1300179391Ssam struct ieee80211vap *vap; 1301179391Ssam 1302179391Ssam IEEE80211_LOCK(ic); 1303179391Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1304179391Ssam struct ifnet *ifp = vap->iv_ifp; 1305179391Ssam if (IFNET_IS_UP_RUNNING(ifp)) { /* NB: avoid recursion */ 1306179391Ssam vap->iv_flags_ext |= IEEE80211_FEXT_RESUME; 1307179391Ssam ieee80211_stop_locked(vap); 1308179391Ssam } 1309179391Ssam } 1310179391Ssam IEEE80211_UNLOCK(ic); 1311188533Sthompsa 1312188533Sthompsa ieee80211_waitfor_parent(ic); 1313179391Ssam} 1314179391Ssam 1315179391Ssam/* 1316179391Ssam * Start all vap's marked for resume. 1317179391Ssam */ 1318179391Ssamvoid 1319179391Ssamieee80211_resume_all(struct ieee80211com *ic) 1320179391Ssam{ 1321179391Ssam struct ieee80211vap *vap; 1322179391Ssam 1323179391Ssam IEEE80211_LOCK(ic); 1324179391Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1325179391Ssam struct ifnet *ifp = vap->iv_ifp; 1326179391Ssam if (!IFNET_IS_UP_RUNNING(ifp) && 1327179391Ssam (vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) { 1328179391Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME; 1329179391Ssam ieee80211_start_locked(vap); 1330179391Ssam } 1331179391Ssam } 1332179391Ssam IEEE80211_UNLOCK(ic); 1333179391Ssam} 1334179391Ssam 1335179391Ssam/* 1336170530Ssam * Switch between turbo and non-turbo operating modes. 1337170530Ssam * Use the specified channel flags to locate the new 1338170530Ssam * channel, update 802.11 state, and then call back into 1339170530Ssam * the driver to effect the change. 1340170530Ssam */ 1341153349Ssamvoid 1342178354Ssamieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags) 1343170530Ssam{ 1344178354Ssam struct ieee80211com *ic = vap->iv_ic; 1345170530Ssam struct ieee80211_channel *chan; 1346170530Ssam 1347170530Ssam chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); 1348170530Ssam if (chan == NULL) { /* XXX should not happen */ 1349178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 1350170530Ssam "%s: no channel with freq %u flags 0x%x\n", 1351170530Ssam __func__, ic->ic_bsschan->ic_freq, newflags); 1352170530Ssam return; 1353170530Ssam } 1354170530Ssam 1355178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 1356170530Ssam "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, 1357170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], 1358170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(chan)], 1359170530Ssam chan->ic_freq, chan->ic_flags); 1360170530Ssam 1361170530Ssam ic->ic_bsschan = chan; 1362170530Ssam ic->ic_prevchan = ic->ic_curchan; 1363170530Ssam ic->ic_curchan = chan; 1364170530Ssam ic->ic_set_channel(ic); 1365170530Ssam /* NB: do not need to reset ERP state 'cuz we're in sta mode */ 1366170530Ssam} 1367170530Ssam 1368170530Ssamvoid 1369153349Ssamieee80211_beacon_miss(struct ieee80211com *ic) 1370153349Ssam{ 1371178354Ssam struct ieee80211vap *vap; 1372153349Ssam 1373178354Ssam if (ic->ic_flags & IEEE80211_F_SCAN) 1374153349Ssam return; 1375178354Ssam /* XXX locking */ 1376178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1377153349Ssam /* 1378178354Ssam * We only pass events through for sta vap's in RUN state; 1379178354Ssam * may be too restrictive but for now this saves all the 1380178354Ssam * handlers duplicating these checks. 1381153349Ssam */ 1382178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 1383178354Ssam vap->iv_state == IEEE80211_S_RUN && 1384178354Ssam vap->iv_bmiss != NULL) 1385178354Ssam vap->iv_bmiss(vap); 1386153349Ssam } 1387153349Ssam} 1388153349Ssam 1389154736Ssam/* 1390154736Ssam * Software beacon miss handling. Check if any beacons 1391154736Ssam * were received in the last period. If not post a 1392154736Ssam * beacon miss; otherwise reset the counter. 1393154736Ssam */ 1394178354Ssamvoid 1395154736Ssamieee80211_swbmiss(void *arg) 1396154736Ssam{ 1397178354Ssam struct ieee80211vap *vap = arg; 1398179217Ssam struct ieee80211com *ic = vap->iv_ic; 1399154736Ssam 1400179217Ssam /* XXX sleep state? */ 1401179217Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 1402179217Ssam ("wrong state %d", vap->iv_state)); 1403179217Ssam 1404179217Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 1405179217Ssam /* 1406179217Ssam * If scanning just ignore and reset state. If we get a 1407179217Ssam * bmiss after coming out of scan because we haven't had 1408179217Ssam * time to receive a beacon then we should probe the AP 1409179217Ssam * before posting a real bmiss (unless iv_bmiss_max has 1410179217Ssam * been artifiically lowered). A cleaner solution might 1411179217Ssam * be to disable the timer on scan start/end but to handle 1412179217Ssam * case of multiple sta vap's we'd need to disable the 1413179217Ssam * timers of all affected vap's. 1414179217Ssam */ 1415179217Ssam vap->iv_swbmiss_count = 0; 1416179217Ssam } else if (vap->iv_swbmiss_count == 0) { 1417178354Ssam if (vap->iv_bmiss != NULL) 1418178354Ssam vap->iv_bmiss(vap); 1419178354Ssam if (vap->iv_bmiss_count == 0) /* don't re-arm timer */ 1420154736Ssam return; 1421154736Ssam } else 1422178354Ssam vap->iv_swbmiss_count = 0; 1423178354Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 1424178354Ssam ieee80211_swbmiss, vap); 1425154736Ssam} 1426154736Ssam 1427178354Ssam/* 1428178354Ssam * Start an 802.11h channel switch. We record the parameters, 1429178354Ssam * mark the operation pending, notify each vap through the 1430178354Ssam * beacon update mechanism so it can update the beacon frame 1431178354Ssam * contents, and then switch vap's to CSA state to block outbound 1432178354Ssam * traffic. Devices that handle CSA directly can use the state 1433178354Ssam * switch to do the right thing so long as they call 1434178354Ssam * ieee80211_csa_completeswitch when it's time to complete the 1435178354Ssam * channel change. Devices that depend on the net80211 layer can 1436178354Ssam * use ieee80211_beacon_update to handle the countdown and the 1437178354Ssam * channel switch. 1438178354Ssam */ 1439178354Ssamvoid 1440178354Ssamieee80211_csa_startswitch(struct ieee80211com *ic, 1441178354Ssam struct ieee80211_channel *c, int mode, int count) 1442178354Ssam{ 1443178354Ssam struct ieee80211vap *vap; 1444178354Ssam 1445178354Ssam IEEE80211_LOCK_ASSERT(ic); 1446178354Ssam 1447178354Ssam ic->ic_csa_newchan = c; 1448178354Ssam ic->ic_csa_count = count; 1449178354Ssam /* XXX record mode? */ 1450178354Ssam ic->ic_flags |= IEEE80211_F_CSAPENDING; 1451178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1452178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP || 1453178354Ssam vap->iv_opmode == IEEE80211_M_IBSS) 1454178354Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); 1455178354Ssam /* switch to CSA state to block outbound traffic */ 1456178354Ssam if (vap->iv_state == IEEE80211_S_RUN) 1457178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); 1458178354Ssam } 1459178354Ssam ieee80211_notify_csa(ic, c, mode, count); 1460178354Ssam} 1461178354Ssam 1462178354Ssam/* 1463178354Ssam * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. 1464178354Ssam * We clear state and move all vap's in CSA state to RUN state 1465178354Ssam * so they can again transmit. 1466178354Ssam */ 1467178354Ssamvoid 1468178354Ssamieee80211_csa_completeswitch(struct ieee80211com *ic) 1469178354Ssam{ 1470178354Ssam struct ieee80211vap *vap; 1471178354Ssam 1472178354Ssam IEEE80211_LOCK_ASSERT(ic); 1473178354Ssam 1474178354Ssam KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); 1475178354Ssam 1476178354Ssam ieee80211_setcurchan(ic, ic->ic_csa_newchan); 1477178354Ssam ic->ic_csa_newchan = NULL; 1478178354Ssam ic->ic_flags &= ~IEEE80211_F_CSAPENDING; 1479178354Ssam 1480178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1481178354Ssam if (vap->iv_state == IEEE80211_S_CSA) 1482178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); 1483178354Ssam} 1484178354Ssam 1485178354Ssam/* 1486178354Ssam * Complete a DFS CAC started by ieee80211_dfs_cac_start. 1487178354Ssam * We clear state and move all vap's in CAC state to RUN state. 1488178354Ssam */ 1489178354Ssamvoid 1490178354Ssamieee80211_cac_completeswitch(struct ieee80211vap *vap0) 1491178354Ssam{ 1492178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1493178354Ssam struct ieee80211vap *vap; 1494178354Ssam 1495178354Ssam IEEE80211_LOCK(ic); 1496178354Ssam /* 1497178354Ssam * Complete CAC state change for lead vap first; then 1498178354Ssam * clock all the other vap's waiting. 1499178354Ssam */ 1500178354Ssam KASSERT(vap0->iv_state == IEEE80211_S_CAC, 1501178354Ssam ("wrong state %d", vap0->iv_state)); 1502178354Ssam ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); 1503178354Ssam 1504178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1505178354Ssam if (vap->iv_state == IEEE80211_S_CAC) 1506178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); 1507178354Ssam IEEE80211_UNLOCK(ic); 1508178354Ssam} 1509178354Ssam 1510178354Ssam/* 1511178354Ssam * Force all vap's other than the specified vap to the INIT state 1512178354Ssam * and mark them as waiting for a scan to complete. These vaps 1513178354Ssam * will be brought up when the scan completes and the scanning vap 1514178354Ssam * reaches RUN state by wakeupwaiting. 1515178354Ssam * XXX if we do this in threads we can use sleep/wakeup. 1516178354Ssam */ 1517154736Ssamstatic void 1518178354Ssammarkwaiting(struct ieee80211vap *vap0) 1519147765Ssam{ 1520178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1521178354Ssam struct ieee80211vap *vap; 1522147765Ssam 1523178354Ssam IEEE80211_LOCK_ASSERT(ic); 1524178354Ssam 1525178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1526178354Ssam if (vap == vap0) 1527178354Ssam continue; 1528178354Ssam if (vap->iv_state != IEEE80211_S_INIT) { 1529178354Ssam vap->iv_newstate(vap, IEEE80211_S_INIT, 0); 1530178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1531178354Ssam } 1532147765Ssam } 1533147765Ssam} 1534147765Ssam 1535178354Ssam/* 1536178354Ssam * Wakeup all vap's waiting for a scan to complete. This is the 1537178354Ssam * companion to markwaiting (above) and is used to coordinate 1538178354Ssam * multiple vaps scanning. 1539178354Ssam */ 1540147765Ssamstatic void 1541178354Ssamwakeupwaiting(struct ieee80211vap *vap0) 1542147765Ssam{ 1543178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1544178354Ssam struct ieee80211vap *vap; 1545147765Ssam 1546178354Ssam IEEE80211_LOCK_ASSERT(ic); 1547178354Ssam 1548178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1549178354Ssam if (vap == vap0) 1550178354Ssam continue; 1551178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { 1552178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; 1553178354Ssam /* NB: sta's cannot go INIT->RUN */ 1554178354Ssam vap->iv_newstate(vap, 1555178354Ssam vap->iv_opmode == IEEE80211_M_STA ? 1556178354Ssam IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); 1557178354Ssam } 1558178354Ssam } 1559147765Ssam} 1560147765Ssam 1561170530Ssam/* 1562178354Ssam * Handle post state change work common to all operating modes. 1563170530Ssam */ 1564170530Ssamstatic void 1565178354Ssamieee80211_newstate_cb(struct ieee80211vap *vap, 1566178354Ssam enum ieee80211_state nstate, int arg) 1567170530Ssam{ 1568178354Ssam struct ieee80211com *ic = vap->iv_ic; 1569178354Ssam 1570178354Ssam IEEE80211_LOCK_ASSERT(ic); 1571178354Ssam 1572178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1573178354Ssam "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg); 1574178354Ssam 1575178354Ssam if (nstate == IEEE80211_S_RUN) { 1576178354Ssam /* 1577178354Ssam * OACTIVE may be set on the vap if the upper layer 1578178354Ssam * tried to transmit (e.g. IPv6 NDP) before we reach 1579178354Ssam * RUN state. Clear it and restart xmit. 1580178354Ssam * 1581178354Ssam * Note this can also happen as a result of SLEEP->RUN 1582178354Ssam * (i.e. coming out of power save mode). 1583178354Ssam */ 1584178354Ssam vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1585178354Ssam if_start(vap->iv_ifp); 1586178354Ssam 1587178354Ssam /* bring up any vaps waiting on us */ 1588178354Ssam wakeupwaiting(vap); 1589178354Ssam } else if (nstate == IEEE80211_S_INIT) { 1590178354Ssam /* 1591178354Ssam * Flush the scan cache if we did the last scan (XXX?) 1592178354Ssam * and flush any frames on send queues from this vap. 1593178354Ssam * Note the mgt q is used only for legacy drivers and 1594178354Ssam * will go away shortly. 1595178354Ssam */ 1596178354Ssam ieee80211_scan_flush(vap); 1597178354Ssam 1598178354Ssam /* XXX NB: cast for altq */ 1599178354Ssam ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); 1600170530Ssam } 1601178354Ssam vap->iv_newstate_cb = NULL; 1602170530Ssam} 1603170530Ssam 1604178354Ssam/* 1605178354Ssam * Public interface for initiating a state machine change. 1606178354Ssam * This routine single-threads the request and coordinates 1607178354Ssam * the scheduling of multiple vaps for the purpose of selecting 1608178354Ssam * an operating channel. Specifically the following scenarios 1609178354Ssam * are handled: 1610178354Ssam * o only one vap can be selecting a channel so on transition to 1611178354Ssam * SCAN state if another vap is already scanning then 1612178354Ssam * mark the caller for later processing and return without 1613178354Ssam * doing anything (XXX? expectations by caller of synchronous operation) 1614178354Ssam * o only one vap can be doing CAC of a channel so on transition to 1615178354Ssam * CAC state if another vap is already scanning for radar then 1616178354Ssam * mark the caller for later processing and return without 1617178354Ssam * doing anything (XXX? expectations by caller of synchronous operation) 1618178354Ssam * o if another vap is already running when a request is made 1619178354Ssam * to SCAN then an operating channel has been chosen; bypass 1620178354Ssam * the scan and just join the channel 1621178354Ssam * 1622178354Ssam * Note that the state change call is done through the iv_newstate 1623178354Ssam * method pointer so any driver routine gets invoked. The driver 1624178354Ssam * will normally call back into operating mode-specific 1625178354Ssam * ieee80211_newstate routines (below) unless it needs to completely 1626178354Ssam * bypass the state machine (e.g. because the firmware has it's 1627178354Ssam * own idea how things should work). Bypassing the net80211 layer 1628178354Ssam * is usually a mistake and indicates lack of proper integration 1629178354Ssam * with the net80211 layer. 1630178354Ssam */ 1631117811Ssamstatic int 1632178354Ssamieee80211_new_state_locked(struct ieee80211vap *vap, 1633178354Ssam enum ieee80211_state nstate, int arg) 1634116742Ssam{ 1635178354Ssam struct ieee80211com *ic = vap->iv_ic; 1636178354Ssam struct ieee80211vap *vp; 1637117811Ssam enum ieee80211_state ostate; 1638178354Ssam int nrunning, nscanning, rc; 1639116742Ssam 1640178354Ssam IEEE80211_LOCK_ASSERT(ic); 1641178354Ssam 1642178354Ssam nrunning = nscanning = 0; 1643178354Ssam /* XXX can track this state instead of calculating */ 1644178354Ssam TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { 1645178354Ssam if (vp != vap) { 1646178354Ssam if (vp->iv_state >= IEEE80211_S_RUN) 1647178354Ssam nrunning++; 1648178354Ssam /* XXX doesn't handle bg scan */ 1649178354Ssam /* NB: CAC+AUTH+ASSOC treated like SCAN */ 1650178354Ssam else if (vp->iv_state > IEEE80211_S_INIT) 1651178354Ssam nscanning++; 1652178354Ssam } 1653178354Ssam } 1654178354Ssam ostate = vap->iv_state; 1655178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1656178354Ssam "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, 1657178354Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate], 1658178354Ssam nrunning, nscanning); 1659116742Ssam switch (nstate) { 1660116742Ssam case IEEE80211_S_SCAN: 1661178354Ssam if (ostate == IEEE80211_S_INIT) { 1662178354Ssam /* 1663178354Ssam * INIT -> SCAN happens on initial bringup. 1664178354Ssam */ 1665178354Ssam KASSERT(!(nscanning && nrunning), 1666178354Ssam ("%d scanning and %d running", nscanning, nrunning)); 1667178354Ssam if (nscanning) { 1668116742Ssam /* 1669178354Ssam * Someone is scanning, defer our state 1670178354Ssam * change until the work has completed. 1671116742Ssam */ 1672178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1673178354Ssam "%s: defer %s -> %s\n", 1674178354Ssam __func__, ieee80211_state_name[ostate], 1675178354Ssam ieee80211_state_name[nstate]); 1676178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1677178354Ssam rc = 0; 1678178354Ssam goto done; 1679116742Ssam } 1680178354Ssam if (nrunning) { 1681170530Ssam /* 1682178354Ssam * Someone is operating; just join the channel 1683178354Ssam * they have chosen. 1684170530Ssam */ 1685178354Ssam /* XXX kill arg? */ 1686178354Ssam /* XXX check each opmode, adhoc? */ 1687178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 1688178354Ssam nstate = IEEE80211_S_SCAN; 1689178354Ssam else 1690178354Ssam nstate = IEEE80211_S_RUN; 1691138568Ssam#ifdef IEEE80211_DEBUG 1692178354Ssam if (nstate != IEEE80211_S_SCAN) { 1693178354Ssam IEEE80211_DPRINTF(vap, 1694178354Ssam IEEE80211_MSG_STATE, 1695178354Ssam "%s: override, now %s -> %s\n", 1696178354Ssam __func__, 1697178354Ssam ieee80211_state_name[ostate], 1698178354Ssam ieee80211_state_name[nstate]); 1699178354Ssam } 1700138568Ssam#endif 1701170530Ssam } 1702178354Ssam } else { 1703178354Ssam /* 1704178354Ssam * SCAN was forced; e.g. on beacon miss. Force 1705178354Ssam * other running vap's to INIT state and mark 1706178354Ssam * them as waiting for the scan to complete. This 1707178354Ssam * insures they don't interfere with our scanning. 1708178354Ssam * 1709178354Ssam * XXX not always right, assumes ap follows sta 1710178354Ssam */ 1711178354Ssam markwaiting(vap); 1712116742Ssam } 1713178354Ssam break; 1714178354Ssam case IEEE80211_S_RUN: 1715178354Ssam if (vap->iv_opmode == IEEE80211_M_WDS && 1716178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && 1717178354Ssam nscanning) { 1718154736Ssam /* 1719178354Ssam * Legacy WDS with someone else scanning; don't 1720178354Ssam * go online until that completes as we should 1721178354Ssam * follow the other vap to the channel they choose. 1722154736Ssam */ 1723178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1724178354Ssam "%s: defer %s -> %s (legacy WDS)\n", __func__, 1725178354Ssam ieee80211_state_name[ostate], 1726178354Ssam ieee80211_state_name[nstate]); 1727178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1728178354Ssam rc = 0; 1729178354Ssam goto done; 1730154736Ssam } 1731178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 1732178354Ssam IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && 1733178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && 1734178354Ssam !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { 1735178354Ssam /* 1736178354Ssam * This is a DFS channel, transition to CAC state 1737178354Ssam * instead of RUN. This allows us to initiate 1738178354Ssam * Channel Availability Check (CAC) as specified 1739178354Ssam * by 11h/DFS. 1740178354Ssam */ 1741178354Ssam nstate = IEEE80211_S_CAC; 1742178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1743178354Ssam "%s: override %s -> %s (DFS)\n", __func__, 1744178354Ssam ieee80211_state_name[ostate], 1745178354Ssam ieee80211_state_name[nstate]); 1746138568Ssam } 1747116742Ssam break; 1748178354Ssam case IEEE80211_S_INIT: 1749178354Ssam if (ostate == IEEE80211_S_INIT ) { 1750178354Ssam /* XXX don't believe this */ 1751178354Ssam /* INIT -> INIT. nothing to do */ 1752178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; 1753178354Ssam } 1754178354Ssam /* fall thru... */ 1755172058Ssam default: 1756172058Ssam break; 1757116742Ssam } 1758178354Ssam /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */ 1759178354Ssam if (ostate != nstate) { 1760178354Ssam /* 1761178354Ssam * Arrange for work to happen after state change completes. 1762178354Ssam * If this happens asynchronously the caller must arrange 1763178354Ssam * for the com lock to be held. 1764178354Ssam */ 1765178354Ssam vap->iv_newstate_cb = ieee80211_newstate_cb; 1766178354Ssam } 1767178354Ssam rc = vap->iv_newstate(vap, nstate, arg); 1768178354Ssam if (rc == 0 && vap->iv_newstate_cb != NULL) 1769178354Ssam vap->iv_newstate_cb(vap, nstate, arg); 1770178354Ssamdone: 1771178354Ssam return rc; 1772116742Ssam} 1773178354Ssam 1774178354Ssamint 1775178354Ssamieee80211_new_state(struct ieee80211vap *vap, 1776178354Ssam enum ieee80211_state nstate, int arg) 1777178354Ssam{ 1778178354Ssam struct ieee80211com *ic = vap->iv_ic; 1779178354Ssam int rc; 1780178354Ssam 1781178354Ssam IEEE80211_LOCK(ic); 1782178354Ssam rc = ieee80211_new_state_locked(vap, nstate, arg); 1783178354Ssam IEEE80211_UNLOCK(ic); 1784178354Ssam return rc; 1785178354Ssam} 1786