ieee80211_proto.c revision 191768
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 191768 2009-05-03 18:29:04Z thompsa $"); 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> 40170530Ssam 41116742Ssam#include <sys/socket.h> 42178354Ssam#include <sys/sockio.h> 43116742Ssam 44116742Ssam#include <net/if.h> 45116742Ssam#include <net/if_media.h> 46138568Ssam#include <net/ethernet.h> /* XXX for ether_sprintf */ 47116742Ssam 48116742Ssam#include <net80211/ieee80211_var.h> 49178354Ssam#include <net80211/ieee80211_adhoc.h> 50178354Ssam#include <net80211/ieee80211_sta.h> 51178354Ssam#include <net80211/ieee80211_hostap.h> 52178354Ssam#include <net80211/ieee80211_wds.h> 53178354Ssam#include <net80211/ieee80211_monitor.h> 54178354Ssam#include <net80211/ieee80211_input.h> 55116742Ssam 56138568Ssam/* XXX tunables */ 57138568Ssam#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 58138568Ssam#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 59116742Ssam 60116742Ssamconst char *ieee80211_mgt_subtype_name[] = { 61116742Ssam "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 62116742Ssam "probe_req", "probe_resp", "reserved#6", "reserved#7", 63116742Ssam "beacon", "atim", "disassoc", "auth", 64172230Ssam "deauth", "action", "reserved#14", "reserved#15" 65116742Ssam}; 66138568Ssamconst char *ieee80211_ctl_subtype_name[] = { 67138568Ssam "reserved#0", "reserved#1", "reserved#2", "reserved#3", 68138568Ssam "reserved#3", "reserved#5", "reserved#6", "reserved#7", 69138568Ssam "reserved#8", "reserved#9", "ps_poll", "rts", 70138568Ssam "cts", "ack", "cf_end", "cf_end_ack" 71138568Ssam}; 72167283Ssamconst char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 73167283Ssam "IBSS", /* IEEE80211_M_IBSS */ 74167283Ssam "STA", /* IEEE80211_M_STA */ 75178354Ssam "WDS", /* IEEE80211_M_WDS */ 76167283Ssam "AHDEMO", /* IEEE80211_M_AHDEMO */ 77167283Ssam "HOSTAP", /* IEEE80211_M_HOSTAP */ 78167283Ssam "MONITOR" /* IEEE80211_M_MONITOR */ 79167283Ssam}; 80117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = { 81117811Ssam "INIT", /* IEEE80211_S_INIT */ 82117811Ssam "SCAN", /* IEEE80211_S_SCAN */ 83117811Ssam "AUTH", /* IEEE80211_S_AUTH */ 84117811Ssam "ASSOC", /* IEEE80211_S_ASSOC */ 85172058Ssam "CAC", /* IEEE80211_S_CAC */ 86172058Ssam "RUN", /* IEEE80211_S_RUN */ 87172058Ssam "CSA", /* IEEE80211_S_CSA */ 88172058Ssam "SLEEP", /* IEEE80211_S_SLEEP */ 89117811Ssam}; 90138568Ssamconst char *ieee80211_wme_acnames[] = { 91138568Ssam "WME_AC_BE", 92138568Ssam "WME_AC_BK", 93138568Ssam "WME_AC_VI", 94138568Ssam "WME_AC_VO", 95138568Ssam "WME_UPSD", 96138568Ssam}; 97116742Ssam 98191746Sthompsastatic void beacon_miss(void *, int); 99191746Sthompsastatic void beacon_swmiss(void *, int); 100178354Ssamstatic void parent_updown(void *, int); 101191746Sthompsastatic void update_mcast(void *, int); 102191746Sthompsastatic void update_promisc(void *, int); 103191746Sthompsastatic void update_channel(void *, int); 104191746Sthompsastatic void ieee80211_newstate_cb(void *, int); 105178354Ssamstatic int ieee80211_new_state_locked(struct ieee80211vap *, 106178354Ssam enum ieee80211_state, int); 107117811Ssam 108178354Ssamstatic int 109178354Ssamnull_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 110178354Ssam const struct ieee80211_bpf_params *params) 111172211Ssam{ 112178354Ssam struct ifnet *ifp = ni->ni_ic->ic_ifp; 113178354Ssam 114178354Ssam if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); 115178354Ssam m_freem(m); 116178354Ssam return ENETDOWN; 117172211Ssam} 118172211Ssam 119116742Ssamvoid 120138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 121116742Ssam{ 122138568Ssam struct ifnet *ifp = ic->ic_ifp; 123116742Ssam 124178354Ssam /* override the 802.3 setting */ 125178354Ssam ifp->if_hdrlen = ic->ic_headroom 126178354Ssam + sizeof(struct ieee80211_qosframe_addr4) 127178354Ssam + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN 128178354Ssam + IEEE80211_WEP_EXTIVLEN; 129178354Ssam /* XXX no way to recalculate on ifdetach */ 130178354Ssam if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { 131178354Ssam /* XXX sanity check... */ 132178354Ssam max_linkhdr = ALIGN(ifp->if_hdrlen); 133178354Ssam max_hdr = max_linkhdr + max_protohdr; 134178354Ssam max_datalen = MHLEN - max_hdr; 135178354Ssam } 136127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 137116742Ssam 138178354Ssam TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp); 139191746Sthompsa TASK_INIT(&ic->ic_mcast_task, 0, update_mcast, ic); 140191746Sthompsa TASK_INIT(&ic->ic_promisc_task, 0, update_promisc, ic); 141191746Sthompsa TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic); 142191746Sthompsa TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic); 143178354Ssam 144138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 145138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 146138568Ssam 147116742Ssam /* initialize management frame handlers */ 148116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 149178354Ssam ic->ic_raw_xmit = null_raw_xmit; 150178354Ssam 151178354Ssam ieee80211_adhoc_attach(ic); 152178354Ssam ieee80211_sta_attach(ic); 153178354Ssam ieee80211_wds_attach(ic); 154178354Ssam ieee80211_hostap_attach(ic); 155178354Ssam ieee80211_monitor_attach(ic); 156116742Ssam} 157116742Ssam 158116742Ssamvoid 159138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 160116742Ssam{ 161178354Ssam ieee80211_monitor_detach(ic); 162178354Ssam ieee80211_hostap_detach(ic); 163178354Ssam ieee80211_wds_detach(ic); 164178354Ssam ieee80211_adhoc_detach(ic); 165178354Ssam ieee80211_sta_detach(ic); 166178354Ssam} 167116742Ssam 168178354Ssamstatic void 169178354Ssamnull_update_beacon(struct ieee80211vap *vap, int item) 170178354Ssam{ 171178354Ssam} 172178354Ssam 173178354Ssamvoid 174178354Ssamieee80211_proto_vattach(struct ieee80211vap *vap) 175178354Ssam{ 176178354Ssam struct ieee80211com *ic = vap->iv_ic; 177178354Ssam struct ifnet *ifp = vap->iv_ifp; 178178354Ssam int i; 179178354Ssam 180178354Ssam /* override the 802.3 setting */ 181178354Ssam ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; 182178354Ssam 183178354Ssam vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; 184178354Ssam vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; 185178354Ssam vap->iv_bmiss_max = IEEE80211_BMISS_MAX; 186178354Ssam callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE); 187178354Ssam callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE); 188191746Sthompsa TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap); 189191746Sthompsa TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap); 190138568Ssam /* 191178354Ssam * Install default tx rate handling: no fixed rate, lowest 192178354Ssam * supported rate for mgmt and multicast frames. Default 193178354Ssam * max retry count. These settings can be changed by the 194178354Ssam * driver and/or user applications. 195178354Ssam */ 196188779Ssam for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { 197178354Ssam const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; 198178354Ssam 199178354Ssam vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; 200188779Ssam if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) { 201188779Ssam vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS; 202188779Ssam vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS; 203188779Ssam } else { 204188779Ssam vap->iv_txparms[i].mgmtrate = 205188779Ssam rs->rs_rates[0] & IEEE80211_RATE_VAL; 206188779Ssam vap->iv_txparms[i].mcastrate = 207188779Ssam rs->rs_rates[0] & IEEE80211_RATE_VAL; 208188779Ssam } 209178354Ssam vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; 210178354Ssam } 211178354Ssam vap->iv_roaming = IEEE80211_ROAMING_AUTO; 212178354Ssam 213178354Ssam vap->iv_update_beacon = null_update_beacon; 214178354Ssam vap->iv_deliver_data = ieee80211_deliver_data; 215178354Ssam 216178354Ssam /* attach support for operating mode */ 217178354Ssam ic->ic_vattach[vap->iv_opmode](vap); 218178354Ssam} 219178354Ssam 220178354Ssamvoid 221178354Ssamieee80211_proto_vdetach(struct ieee80211vap *vap) 222178354Ssam{ 223178354Ssam#define FREEAPPIE(ie) do { \ 224178354Ssam if (ie != NULL) \ 225186302Ssam free(ie, M_80211_NODE_IE); \ 226178354Ssam} while (0) 227178354Ssam /* 228178354Ssam * Detach operating mode module. 229178354Ssam */ 230178354Ssam if (vap->iv_opdetach != NULL) 231178354Ssam vap->iv_opdetach(vap); 232178354Ssam /* 233138568Ssam * This should not be needed as we detach when reseting 234138568Ssam * the state but be conservative here since the 235138568Ssam * authenticator may do things like spawn kernel threads. 236138568Ssam */ 237178354Ssam if (vap->iv_auth->ia_detach != NULL) 238178354Ssam vap->iv_auth->ia_detach(vap); 239138568Ssam /* 240138568Ssam * Detach any ACL'ator. 241138568Ssam */ 242178354Ssam if (vap->iv_acl != NULL) 243178354Ssam vap->iv_acl->iac_detach(vap); 244178354Ssam 245178354Ssam FREEAPPIE(vap->iv_appie_beacon); 246178354Ssam FREEAPPIE(vap->iv_appie_probereq); 247178354Ssam FREEAPPIE(vap->iv_appie_proberesp); 248178354Ssam FREEAPPIE(vap->iv_appie_assocreq); 249178354Ssam FREEAPPIE(vap->iv_appie_assocresp); 250178354Ssam FREEAPPIE(vap->iv_appie_wpa); 251178354Ssam#undef FREEAPPIE 252116742Ssam} 253116742Ssam 254138568Ssam/* 255138568Ssam * Simple-minded authenticator module support. 256138568Ssam */ 257138568Ssam 258138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 259138568Ssam/* XXX well-known names */ 260138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 261138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 262138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 263138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 264138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 265138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 266138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 267138568Ssam}; 268138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 269138568Ssam 270138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 271138568Ssam .ia_name = "wlan_internal", 272138568Ssam .ia_attach = NULL, 273138568Ssam .ia_detach = NULL, 274138568Ssam .ia_node_join = NULL, 275138568Ssam .ia_node_leave = NULL, 276138568Ssam}; 277138568Ssam 278138568Ssam/* 279138568Ssam * Setup internal authenticators once; they are never unregistered. 280138568Ssam */ 281138568Ssamstatic void 282138568Ssamieee80211_auth_setup(void) 283138568Ssam{ 284138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 285138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 286138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 287138568Ssam} 288138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 289138568Ssam 290138568Ssamconst struct ieee80211_authenticator * 291138568Ssamieee80211_authenticator_get(int auth) 292138568Ssam{ 293138568Ssam if (auth >= IEEE80211_AUTH_MAX) 294138568Ssam return NULL; 295138568Ssam if (authenticators[auth] == NULL) 296138568Ssam ieee80211_load_module(auth_modnames[auth]); 297138568Ssam return authenticators[auth]; 298138568Ssam} 299138568Ssam 300116742Ssamvoid 301138568Ssamieee80211_authenticator_register(int type, 302138568Ssam const struct ieee80211_authenticator *auth) 303116742Ssam{ 304138568Ssam if (type >= IEEE80211_AUTH_MAX) 305138568Ssam return; 306138568Ssam authenticators[type] = auth; 307138568Ssam} 308138568Ssam 309138568Ssamvoid 310138568Ssamieee80211_authenticator_unregister(int type) 311138568Ssam{ 312138568Ssam 313138568Ssam if (type >= IEEE80211_AUTH_MAX) 314138568Ssam return; 315138568Ssam authenticators[type] = NULL; 316138568Ssam} 317138568Ssam 318138568Ssam/* 319138568Ssam * Very simple-minded ACL module support. 320138568Ssam */ 321138568Ssam/* XXX just one for now */ 322138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 323138568Ssam 324138568Ssamvoid 325138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 326138568Ssam{ 327138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 328138568Ssam acl = iac; 329138568Ssam} 330138568Ssam 331138568Ssamvoid 332138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 333138568Ssam{ 334138568Ssam if (acl == iac) 335138568Ssam acl = NULL; 336138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 337138568Ssam} 338138568Ssam 339138568Ssamconst struct ieee80211_aclator * 340138568Ssamieee80211_aclator_get(const char *name) 341138568Ssam{ 342138568Ssam if (acl == NULL) 343138568Ssam ieee80211_load_module("wlan_acl"); 344138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 345138568Ssam} 346138568Ssam 347138568Ssamvoid 348170530Ssamieee80211_print_essid(const uint8_t *essid, int len) 349138568Ssam{ 350170530Ssam const uint8_t *p; 351116742Ssam int i; 352116742Ssam 353116742Ssam if (len > IEEE80211_NWID_LEN) 354116742Ssam len = IEEE80211_NWID_LEN; 355116742Ssam /* determine printable or not */ 356116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 357116742Ssam if (*p < ' ' || *p > 0x7e) 358116742Ssam break; 359116742Ssam } 360116742Ssam if (i == len) { 361116742Ssam printf("\""); 362116742Ssam for (i = 0, p = essid; i < len; i++, p++) 363116742Ssam printf("%c", *p); 364116742Ssam printf("\""); 365116742Ssam } else { 366116742Ssam printf("0x"); 367116742Ssam for (i = 0, p = essid; i < len; i++, p++) 368116742Ssam printf("%02x", *p); 369116742Ssam } 370116742Ssam} 371116742Ssam 372116742Ssamvoid 373170530Ssamieee80211_dump_pkt(struct ieee80211com *ic, 374170530Ssam const uint8_t *buf, int len, int rate, int rssi) 375116742Ssam{ 376138568Ssam const struct ieee80211_frame *wh; 377116742Ssam int i; 378116742Ssam 379138568Ssam wh = (const struct ieee80211_frame *)buf; 380116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 381116742Ssam case IEEE80211_FC1_DIR_NODS: 382116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 383116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 384116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 385116742Ssam break; 386116742Ssam case IEEE80211_FC1_DIR_TODS: 387116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 388116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 389116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 390116742Ssam break; 391116742Ssam case IEEE80211_FC1_DIR_FROMDS: 392116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 393116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 394116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 395116742Ssam break; 396116742Ssam case IEEE80211_FC1_DIR_DSTODS: 397170530Ssam printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); 398116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 399116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 400116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 401116742Ssam break; 402116742Ssam } 403116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 404116742Ssam case IEEE80211_FC0_TYPE_DATA: 405116742Ssam printf(" data"); 406116742Ssam break; 407116742Ssam case IEEE80211_FC0_TYPE_MGT: 408116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 409116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 410116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 411116742Ssam break; 412116742Ssam default: 413116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 414116742Ssam break; 415116742Ssam } 416170530Ssam if (IEEE80211_QOS_HAS_SEQ(wh)) { 417170530Ssam const struct ieee80211_qosframe *qwh = 418170530Ssam (const struct ieee80211_qosframe *)buf; 419170530Ssam printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, 420170530Ssam qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); 421170530Ssam } 422138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 423170530Ssam int off; 424170530Ssam 425170530Ssam off = ieee80211_anyhdrspace(ic, wh); 426170530Ssam printf(" WEP [IV %.02x %.02x %.02x", 427170530Ssam buf[off+0], buf[off+1], buf[off+2]); 428170530Ssam if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) 429170530Ssam printf(" %.02x %.02x %.02x", 430170530Ssam buf[off+4], buf[off+5], buf[off+6]); 431170530Ssam printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); 432138568Ssam } 433116742Ssam if (rate >= 0) 434116742Ssam printf(" %dM", rate / 2); 435116742Ssam if (rssi >= 0) 436116742Ssam printf(" +%d", rssi); 437116742Ssam printf("\n"); 438116742Ssam if (len > 0) { 439116742Ssam for (i = 0; i < len; i++) { 440116742Ssam if ((i & 1) == 0) 441116742Ssam printf(" "); 442116742Ssam printf("%02x", buf[i]); 443116742Ssam } 444116742Ssam printf("\n"); 445116742Ssam } 446116742Ssam} 447116742Ssam 448165887Ssamstatic __inline int 449165887Ssamfindrix(const struct ieee80211_rateset *rs, int r) 450165887Ssam{ 451165887Ssam int i; 452165887Ssam 453165887Ssam for (i = 0; i < rs->rs_nrates; i++) 454165887Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 455165887Ssam return i; 456165887Ssam return -1; 457165887Ssam} 458165887Ssam 459116742Ssamint 460167442Ssamieee80211_fix_rate(struct ieee80211_node *ni, 461167442Ssam struct ieee80211_rateset *nrs, int flags) 462116742Ssam{ 463116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 464178354Ssam struct ieee80211vap *vap = ni->ni_vap; 465148299Ssam struct ieee80211com *ic = ni->ni_ic; 466165887Ssam int i, j, rix, error; 467178354Ssam int okrate, badrate, fixedrate, ucastrate; 468165569Ssam const struct ieee80211_rateset *srs; 469170530Ssam uint8_t r; 470116742Ssam 471116742Ssam error = 0; 472170530Ssam okrate = badrate = 0; 473178354Ssam ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; 474178354Ssam if (ucastrate != IEEE80211_FIXED_RATE_NONE) { 475178354Ssam /* 476178354Ssam * Workaround awkwardness with fixed rate. We are called 477178354Ssam * to check both the legacy rate set and the HT rate set 478178354Ssam * but we must apply any legacy fixed rate check only to the 479178354Ssam * legacy rate set and vice versa. We cannot tell what type 480178354Ssam * of rate set we've been given (legacy or HT) but we can 481178354Ssam * distinguish the fixed rate type (MCS have 0x80 set). 482178354Ssam * So to deal with this the caller communicates whether to 483178354Ssam * check MCS or legacy rate using the flags and we use the 484178354Ssam * type of any fixed rate to avoid applying an MCS to a 485178354Ssam * legacy rate and vice versa. 486178354Ssam */ 487178354Ssam if (ucastrate & 0x80) { 488178354Ssam if (flags & IEEE80211_F_DOFRATE) 489178354Ssam flags &= ~IEEE80211_F_DOFRATE; 490178354Ssam } else if ((ucastrate & 0x80) == 0) { 491178354Ssam if (flags & IEEE80211_F_DOFMCS) 492178354Ssam flags &= ~IEEE80211_F_DOFMCS; 493178354Ssam } 494178354Ssam /* NB: required to make MCS match below work */ 495178354Ssam ucastrate &= IEEE80211_RATE_VAL; 496178354Ssam } 497170530Ssam fixedrate = IEEE80211_FIXED_RATE_NONE; 498178354Ssam /* 499178354Ssam * XXX we are called to process both MCS and legacy rates; 500178354Ssam * we must use the appropriate basic rate set or chaos will 501178354Ssam * ensue; for now callers that want MCS must supply 502178354Ssam * IEEE80211_F_DOBRS; at some point we'll need to split this 503178354Ssam * function so there are two variants, one for MCS and one 504178354Ssam * for legacy rates. 505178354Ssam */ 506178354Ssam if (flags & IEEE80211_F_DOBRS) 507178354Ssam srs = (const struct ieee80211_rateset *) 508178354Ssam ieee80211_get_suphtrates(ic, ni->ni_chan); 509178354Ssam else 510178354Ssam srs = ieee80211_get_suprates(ic, ni->ni_chan); 511120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 512116742Ssam if (flags & IEEE80211_F_DOSORT) { 513116742Ssam /* 514116742Ssam * Sort rates. 515116742Ssam */ 516116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 517116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 518116742Ssam r = nrs->rs_rates[i]; 519116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 520116742Ssam nrs->rs_rates[j] = r; 521116742Ssam } 522116742Ssam } 523116742Ssam } 524116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 525116742Ssam badrate = r; 526165887Ssam /* 527170530Ssam * Check for fixed rate. 528170530Ssam */ 529178354Ssam if (r == ucastrate) 530170530Ssam fixedrate = r; 531170530Ssam /* 532165887Ssam * Check against supported rates. 533165887Ssam */ 534165887Ssam rix = findrix(srs, r); 535116742Ssam if (flags & IEEE80211_F_DONEGO) { 536165887Ssam if (rix < 0) { 537120482Ssam /* 538120482Ssam * A rate in the node's rate set is not 539120482Ssam * supported. If this is a basic rate and we 540165887Ssam * are operating as a STA then this is an error. 541120482Ssam * Otherwise we just discard/ignore the rate. 542120482Ssam */ 543165887Ssam if ((flags & IEEE80211_F_JOIN) && 544120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 545116742Ssam error++; 546165887Ssam } else if ((flags & IEEE80211_F_JOIN) == 0) { 547165887Ssam /* 548165887Ssam * Overwrite with the supported rate 549165887Ssam * value so any basic rate bit is set. 550165887Ssam */ 551165887Ssam nrs->rs_rates[i] = srs->rs_rates[rix]; 552116742Ssam } 553116742Ssam } 554165887Ssam if ((flags & IEEE80211_F_DODEL) && rix < 0) { 555116742Ssam /* 556116742Ssam * Delete unacceptable rates. 557116742Ssam */ 558165887Ssam nrs->rs_nrates--; 559165887Ssam for (j = i; j < nrs->rs_nrates; j++) 560165887Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 561165887Ssam nrs->rs_rates[j] = 0; 562165887Ssam continue; 563116742Ssam } 564165887Ssam if (rix >= 0) 565116742Ssam okrate = nrs->rs_rates[i]; 566116742Ssam i++; 567116742Ssam } 568138568Ssam if (okrate == 0 || error != 0 || 569178354Ssam ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && 570178354Ssam fixedrate != ucastrate)) { 571178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 572178354Ssam "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " 573178354Ssam "ucastrate %x\n", __func__, fixedrate, ucastrate, flags); 574116742Ssam return badrate | IEEE80211_RATE_BASIC; 575178354Ssam } else 576116742Ssam return RV(okrate); 577116742Ssam#undef RV 578116742Ssam} 579116742Ssam 580138568Ssam/* 581138568Ssam * Reset 11g-related state. 582138568Ssam */ 583138568Ssamvoid 584138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 585138568Ssam{ 586138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 587138568Ssam ic->ic_nonerpsta = 0; 588138568Ssam ic->ic_longslotsta = 0; 589138568Ssam /* 590138568Ssam * Short slot time is enabled only when operating in 11g 591138568Ssam * and not in an IBSS. We must also honor whether or not 592138568Ssam * the driver is capable of doing it. 593138568Ssam */ 594138568Ssam ieee80211_set_shortslottime(ic, 595170530Ssam IEEE80211_IS_CHAN_A(ic->ic_curchan) || 596170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_curchan) || 597170530Ssam (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 598138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 599138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 600138568Ssam /* 601138568Ssam * Set short preamble and ERP barker-preamble flags. 602138568Ssam */ 603170530Ssam if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 604138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 605138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 606138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 607138568Ssam } else { 608138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 609138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 610138568Ssam } 611138568Ssam} 612138568Ssam 613138568Ssam/* 614138568Ssam * Set the short slot time state and notify the driver. 615138568Ssam */ 616138568Ssamvoid 617138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 618138568Ssam{ 619138568Ssam if (onoff) 620138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 621138568Ssam else 622138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 623138568Ssam /* notify driver */ 624138568Ssam if (ic->ic_updateslot != NULL) 625138568Ssam ic->ic_updateslot(ic->ic_ifp); 626138568Ssam} 627138568Ssam 628138568Ssam/* 629138568Ssam * Check if the specified rate set supports ERP. 630138568Ssam * NB: the rate set is assumed to be sorted. 631138568Ssam */ 632138568Ssamint 633178354Ssamieee80211_iserp_rateset(const struct ieee80211_rateset *rs) 634138568Ssam{ 635138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 636138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 637138568Ssam int i, j; 638138568Ssam 639138568Ssam if (rs->rs_nrates < N(rates)) 640138568Ssam return 0; 641138568Ssam for (i = 0; i < N(rates); i++) { 642138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 643138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 644138568Ssam if (rates[i] == r) 645138568Ssam goto next; 646138568Ssam if (r > rates[i]) 647138568Ssam return 0; 648138568Ssam } 649138568Ssam return 0; 650138568Ssam next: 651138568Ssam ; 652138568Ssam } 653138568Ssam return 1; 654138568Ssam#undef N 655138568Ssam} 656138568Ssam 657138568Ssam/* 658178354Ssam * Mark the basic rates for the rate table based on the 659138568Ssam * operating mode. For real 11g we mark all the 11b rates 660138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 661138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 662138568Ssam * the basic OFDM rates. 663138568Ssam */ 664178354Ssamstatic void 665178354Ssamsetbasicrates(struct ieee80211_rateset *rs, 666178354Ssam enum ieee80211_phymode mode, int add) 667138568Ssam{ 668170530Ssam static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { 669188780Ssam [IEEE80211_MODE_11A] = { 3, { 12, 24, 48 } }, 670188780Ssam [IEEE80211_MODE_11B] = { 2, { 2, 4 } }, 671188780Ssam /* NB: mixed b/g */ 672188780Ssam [IEEE80211_MODE_11G] = { 4, { 2, 4, 11, 22 } }, 673188780Ssam [IEEE80211_MODE_TURBO_A] = { 3, { 12, 24, 48 } }, 674188780Ssam [IEEE80211_MODE_TURBO_G] = { 4, { 2, 4, 11, 22 } }, 675188780Ssam [IEEE80211_MODE_STURBO_A] = { 3, { 12, 24, 48 } }, 676188782Ssam [IEEE80211_MODE_HALF] = { 3, { 6, 12, 24 } }, 677188782Ssam [IEEE80211_MODE_QUARTER] = { 3, { 3, 6, 12 } }, 678188780Ssam [IEEE80211_MODE_11NA] = { 3, { 12, 24, 48 } }, 679188780Ssam /* NB: mixed b/g */ 680188780Ssam [IEEE80211_MODE_11NG] = { 4, { 2, 4, 11, 22 } }, 681138568Ssam }; 682138568Ssam int i, j; 683138568Ssam 684138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 685178354Ssam if (!add) 686178354Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 687138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 688138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 689138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 690138568Ssam break; 691138568Ssam } 692138568Ssam } 693138568Ssam} 694138568Ssam 695138568Ssam/* 696178354Ssam * Set the basic rates in a rate set. 697138568Ssam */ 698178354Ssamvoid 699178354Ssamieee80211_setbasicrates(struct ieee80211_rateset *rs, 700178354Ssam enum ieee80211_phymode mode) 701178354Ssam{ 702178354Ssam setbasicrates(rs, mode, 0); 703178354Ssam} 704178354Ssam 705178354Ssam/* 706178354Ssam * Add basic rates to a rate set. 707178354Ssam */ 708178354Ssamvoid 709178354Ssamieee80211_addbasicrates(struct ieee80211_rateset *rs, 710178354Ssam enum ieee80211_phymode mode) 711178354Ssam{ 712178354Ssam setbasicrates(rs, mode, 1); 713178354Ssam} 714178354Ssam 715178354Ssam/* 716178354Ssam * WME protocol support. 717178354Ssam * 718178354Ssam * The default 11a/b/g/n parameters come from the WiFi Alliance WMM 719178354Ssam * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n 720178354Ssam * Draft 2.0 Test Plan (Appendix D). 721178354Ssam * 722178354Ssam * Static/Dynamic Turbo mode settings come from Atheros. 723178354Ssam */ 724138568Ssamtypedef struct phyParamType { 725178354Ssam uint8_t aifsn; 726178354Ssam uint8_t logcwmin; 727178354Ssam uint8_t logcwmax; 728178354Ssam uint16_t txopLimit; 729178354Ssam uint8_t acm; 730138568Ssam} paramType; 731138568Ssam 732138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 733188780Ssam [IEEE80211_MODE_AUTO] = { 3, 4, 6, 0, 0 }, 734188780Ssam [IEEE80211_MODE_11A] = { 3, 4, 6, 0, 0 }, 735188780Ssam [IEEE80211_MODE_11B] = { 3, 4, 6, 0, 0 }, 736188780Ssam [IEEE80211_MODE_11G] = { 3, 4, 6, 0, 0 }, 737188780Ssam [IEEE80211_MODE_FH] = { 3, 4, 6, 0, 0 }, 738188780Ssam [IEEE80211_MODE_TURBO_A]= { 2, 3, 5, 0, 0 }, 739188780Ssam [IEEE80211_MODE_TURBO_G]= { 2, 3, 5, 0, 0 }, 740188780Ssam [IEEE80211_MODE_STURBO_A]={ 2, 3, 5, 0, 0 }, 741188782Ssam [IEEE80211_MODE_HALF] = { 3, 4, 6, 0, 0 }, 742188782Ssam [IEEE80211_MODE_QUARTER]= { 3, 4, 6, 0, 0 }, 743188780Ssam [IEEE80211_MODE_11NA] = { 3, 4, 6, 0, 0 }, 744188780Ssam [IEEE80211_MODE_11NG] = { 3, 4, 6, 0, 0 }, 745138568Ssam}; 746138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 747188780Ssam [IEEE80211_MODE_AUTO] = { 7, 4, 10, 0, 0 }, 748188780Ssam [IEEE80211_MODE_11A] = { 7, 4, 10, 0, 0 }, 749188780Ssam [IEEE80211_MODE_11B] = { 7, 4, 10, 0, 0 }, 750188780Ssam [IEEE80211_MODE_11G] = { 7, 4, 10, 0, 0 }, 751188780Ssam [IEEE80211_MODE_FH] = { 7, 4, 10, 0, 0 }, 752188780Ssam [IEEE80211_MODE_TURBO_A]= { 7, 3, 10, 0, 0 }, 753188780Ssam [IEEE80211_MODE_TURBO_G]= { 7, 3, 10, 0, 0 }, 754188780Ssam [IEEE80211_MODE_STURBO_A]={ 7, 3, 10, 0, 0 }, 755188782Ssam [IEEE80211_MODE_HALF] = { 7, 4, 10, 0, 0 }, 756188782Ssam [IEEE80211_MODE_QUARTER]= { 7, 4, 10, 0, 0 }, 757188780Ssam [IEEE80211_MODE_11NA] = { 7, 4, 10, 0, 0 }, 758188780Ssam [IEEE80211_MODE_11NG] = { 7, 4, 10, 0, 0 }, 759138568Ssam}; 760138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 761188780Ssam [IEEE80211_MODE_AUTO] = { 1, 3, 4, 94, 0 }, 762188780Ssam [IEEE80211_MODE_11A] = { 1, 3, 4, 94, 0 }, 763188780Ssam [IEEE80211_MODE_11B] = { 1, 3, 4, 188, 0 }, 764188780Ssam [IEEE80211_MODE_11G] = { 1, 3, 4, 94, 0 }, 765188780Ssam [IEEE80211_MODE_FH] = { 1, 3, 4, 188, 0 }, 766188780Ssam [IEEE80211_MODE_TURBO_A]= { 1, 2, 3, 94, 0 }, 767188780Ssam [IEEE80211_MODE_TURBO_G]= { 1, 2, 3, 94, 0 }, 768188780Ssam [IEEE80211_MODE_STURBO_A]={ 1, 2, 3, 94, 0 }, 769188782Ssam [IEEE80211_MODE_HALF] = { 1, 3, 4, 94, 0 }, 770188782Ssam [IEEE80211_MODE_QUARTER]= { 1, 3, 4, 94, 0 }, 771188780Ssam [IEEE80211_MODE_11NA] = { 1, 3, 4, 94, 0 }, 772188780Ssam [IEEE80211_MODE_11NG] = { 1, 3, 4, 94, 0 }, 773138568Ssam}; 774138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 775188780Ssam [IEEE80211_MODE_AUTO] = { 1, 2, 3, 47, 0 }, 776188780Ssam [IEEE80211_MODE_11A] = { 1, 2, 3, 47, 0 }, 777188780Ssam [IEEE80211_MODE_11B] = { 1, 2, 3, 102, 0 }, 778188780Ssam [IEEE80211_MODE_11G] = { 1, 2, 3, 47, 0 }, 779188780Ssam [IEEE80211_MODE_FH] = { 1, 2, 3, 102, 0 }, 780188780Ssam [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, 781188780Ssam [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, 782188780Ssam [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, 783188782Ssam [IEEE80211_MODE_HALF] = { 1, 2, 3, 47, 0 }, 784188782Ssam [IEEE80211_MODE_QUARTER]= { 1, 2, 3, 47, 0 }, 785188780Ssam [IEEE80211_MODE_11NA] = { 1, 2, 3, 47, 0 }, 786188780Ssam [IEEE80211_MODE_11NG] = { 1, 2, 3, 47, 0 }, 787138568Ssam}; 788138568Ssam 789138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 790188780Ssam [IEEE80211_MODE_AUTO] = { 3, 4, 10, 0, 0 }, 791188780Ssam [IEEE80211_MODE_11A] = { 3, 4, 10, 0, 0 }, 792188780Ssam [IEEE80211_MODE_11B] = { 3, 4, 10, 0, 0 }, 793188780Ssam [IEEE80211_MODE_11G] = { 3, 4, 10, 0, 0 }, 794188780Ssam [IEEE80211_MODE_FH] = { 3, 4, 10, 0, 0 }, 795188780Ssam [IEEE80211_MODE_TURBO_A]= { 2, 3, 10, 0, 0 }, 796188780Ssam [IEEE80211_MODE_TURBO_G]= { 2, 3, 10, 0, 0 }, 797188780Ssam [IEEE80211_MODE_STURBO_A]={ 2, 3, 10, 0, 0 }, 798188782Ssam [IEEE80211_MODE_HALF] = { 3, 4, 10, 0, 0 }, 799188782Ssam [IEEE80211_MODE_QUARTER]= { 3, 4, 10, 0, 0 }, 800188780Ssam [IEEE80211_MODE_11NA] = { 3, 4, 10, 0, 0 }, 801188780Ssam [IEEE80211_MODE_11NG] = { 3, 4, 10, 0, 0 }, 802138568Ssam}; 803138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 804188780Ssam [IEEE80211_MODE_AUTO] = { 2, 3, 4, 94, 0 }, 805188780Ssam [IEEE80211_MODE_11A] = { 2, 3, 4, 94, 0 }, 806188780Ssam [IEEE80211_MODE_11B] = { 2, 3, 4, 188, 0 }, 807188780Ssam [IEEE80211_MODE_11G] = { 2, 3, 4, 94, 0 }, 808188780Ssam [IEEE80211_MODE_FH] = { 2, 3, 4, 188, 0 }, 809188780Ssam [IEEE80211_MODE_TURBO_A]= { 2, 2, 3, 94, 0 }, 810188780Ssam [IEEE80211_MODE_TURBO_G]= { 2, 2, 3, 94, 0 }, 811188780Ssam [IEEE80211_MODE_STURBO_A]={ 2, 2, 3, 94, 0 }, 812188782Ssam [IEEE80211_MODE_HALF] = { 2, 3, 4, 94, 0 }, 813188782Ssam [IEEE80211_MODE_QUARTER]= { 2, 3, 4, 94, 0 }, 814188780Ssam [IEEE80211_MODE_11NA] = { 2, 3, 4, 94, 0 }, 815188780Ssam [IEEE80211_MODE_11NG] = { 2, 3, 4, 94, 0 }, 816138568Ssam}; 817138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 818188780Ssam [IEEE80211_MODE_AUTO] = { 2, 2, 3, 47, 0 }, 819188780Ssam [IEEE80211_MODE_11A] = { 2, 2, 3, 47, 0 }, 820188780Ssam [IEEE80211_MODE_11B] = { 2, 2, 3, 102, 0 }, 821188780Ssam [IEEE80211_MODE_11G] = { 2, 2, 3, 47, 0 }, 822188780Ssam [IEEE80211_MODE_FH] = { 2, 2, 3, 102, 0 }, 823188780Ssam [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, 824188780Ssam [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, 825188780Ssam [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, 826188782Ssam [IEEE80211_MODE_HALF] = { 2, 2, 3, 47, 0 }, 827188782Ssam [IEEE80211_MODE_QUARTER]= { 2, 2, 3, 47, 0 }, 828188780Ssam [IEEE80211_MODE_11NA] = { 2, 2, 3, 47, 0 }, 829188780Ssam [IEEE80211_MODE_11NG] = { 2, 2, 3, 47, 0 }, 830138568Ssam}; 831138568Ssam 832178354Ssamstatic void 833188863Ssam_setifsparams(struct wmeParams *wmep, const paramType *phy) 834188863Ssam{ 835188863Ssam wmep->wmep_aifsn = phy->aifsn; 836188863Ssam wmep->wmep_logcwmin = phy->logcwmin; 837188863Ssam wmep->wmep_logcwmax = phy->logcwmax; 838188863Ssam wmep->wmep_txopLimit = phy->txopLimit; 839188863Ssam} 840188863Ssam 841188863Ssamstatic void 842188863Ssamsetwmeparams(struct ieee80211vap *vap, const char *type, int ac, 843188863Ssam struct wmeParams *wmep, const paramType *phy) 844188863Ssam{ 845188863Ssam wmep->wmep_acm = phy->acm; 846188863Ssam _setifsparams(wmep, phy); 847188863Ssam 848188863Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 849188863Ssam "set %s (%s) [acm %u aifsn %u logcwmin %u logcwmax %u txop %u]\n", 850188863Ssam ieee80211_wme_acnames[ac], type, 851188863Ssam wmep->wmep_acm, wmep->wmep_aifsn, wmep->wmep_logcwmin, 852188863Ssam wmep->wmep_logcwmax, wmep->wmep_txopLimit); 853188863Ssam} 854188863Ssam 855188863Ssamstatic void 856178354Ssamieee80211_wme_initparams_locked(struct ieee80211vap *vap) 857138568Ssam{ 858178354Ssam struct ieee80211com *ic = vap->iv_ic; 859138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 860138568Ssam const paramType *pPhyParam, *pBssPhyParam; 861138568Ssam struct wmeParams *wmep; 862170530Ssam enum ieee80211_phymode mode; 863138568Ssam int i; 864138568Ssam 865178354Ssam IEEE80211_LOCK_ASSERT(ic); 866178354Ssam 867188864Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0 || ic->ic_nrunning > 1) 868138568Ssam return; 869138568Ssam 870170530Ssam /* 871170530Ssam * Select mode; we can be called early in which case we 872170530Ssam * always use auto mode. We know we'll be called when 873170530Ssam * entering the RUN state with bsschan setup properly 874170530Ssam * so state will eventually get set correctly 875170530Ssam */ 876170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 877170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 878170530Ssam else 879170530Ssam mode = IEEE80211_MODE_AUTO; 880138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 881138568Ssam switch (i) { 882138568Ssam case WME_AC_BK: 883170530Ssam pPhyParam = &phyParamForAC_BK[mode]; 884170530Ssam pBssPhyParam = &phyParamForAC_BK[mode]; 885138568Ssam break; 886138568Ssam case WME_AC_VI: 887170530Ssam pPhyParam = &phyParamForAC_VI[mode]; 888170530Ssam pBssPhyParam = &bssPhyParamForAC_VI[mode]; 889138568Ssam break; 890138568Ssam case WME_AC_VO: 891170530Ssam pPhyParam = &phyParamForAC_VO[mode]; 892170530Ssam pBssPhyParam = &bssPhyParamForAC_VO[mode]; 893138568Ssam break; 894138568Ssam case WME_AC_BE: 895138568Ssam default: 896170530Ssam pPhyParam = &phyParamForAC_BE[mode]; 897170530Ssam pBssPhyParam = &bssPhyParamForAC_BE[mode]; 898138568Ssam break; 899138568Ssam } 900138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 901138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 902188863Ssam setwmeparams(vap, "chan", i, wmep, pPhyParam); 903138568Ssam } else { 904188863Ssam setwmeparams(vap, "chan", i, wmep, pBssPhyParam); 905138568Ssam } 906138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 907188863Ssam setwmeparams(vap, "bss ", i, wmep, pBssPhyParam); 908138568Ssam } 909138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 910178354Ssam if (vap->iv_bss != NULL) { 911138568Ssam /* 912138568Ssam * Calculate agressive mode switching threshold based 913138568Ssam * on beacon interval. This doesn't need locking since 914138568Ssam * we're only called before entering the RUN state at 915138568Ssam * which point we start sending beacon frames. 916138568Ssam */ 917138568Ssam wme->wme_hipri_switch_thresh = 918178354Ssam (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; 919188864Ssam wme->wme_flags &= ~WME_F_AGGRMODE; 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{ 940188863Ssam static const paramType aggrParam[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 961188863Ssam /* 962188863Ssam * Set up the channel access parameters for the physical 963188863Ssam * device. First populate the configured settings. 964188863Ssam */ 965138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 966138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 967138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 968138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 969138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 970138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 971138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 972138568Ssam 973138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 974138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 975138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 976138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 977138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 978138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 979138568Ssam } 980138568Ssam 981138568Ssam /* 982170530Ssam * Select mode; we can be called early in which case we 983170530Ssam * always use auto mode. We know we'll be called when 984170530Ssam * entering the RUN state with bsschan setup properly 985170530Ssam * so state will eventually get set correctly 986170530Ssam */ 987170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 988170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 989170530Ssam else 990170530Ssam mode = IEEE80211_MODE_AUTO; 991170530Ssam 992170530Ssam /* 993138568Ssam * This implements agressive mode as found in certain 994138568Ssam * vendors' AP's. When there is significant high 995138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 996138568Ssam * traffic by using conservative parameters. Otherwise 997138568Ssam * BE uses agressive params to optimize performance of 998138568Ssam * legacy/non-QoS traffic. 999138568Ssam */ 1000178354Ssam if ((vap->iv_opmode == IEEE80211_M_HOSTAP && 1001156524Ssam (wme->wme_flags & WME_F_AGGRMODE) != 0) || 1002178354Ssam (vap->iv_opmode == IEEE80211_M_STA && 1003178354Ssam (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 1004178354Ssam (vap->iv_flags & IEEE80211_F_WME) == 0) { 1005138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 1006138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 1007138568Ssam 1008188863Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = aggrParam[mode].aifsn; 1009138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 1010188863Ssam aggrParam[mode].logcwmin; 1011138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 1012188863Ssam aggrParam[mode].logcwmax; 1013138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 1014188863Ssam (vap->iv_flags & IEEE80211_F_BURST) ? 1015188863Ssam aggrParam[mode].txopLimit : 0; 1016178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1017188863Ssam "update %s (chan+bss) [acm %u aifsn %u logcwmin %u " 1018188863Ssam "logcwmax %u txop %u]\n", ieee80211_wme_acnames[WME_AC_BE], 1019188863Ssam chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin, 1020188863Ssam chanp->wmep_logcwmax, chanp->wmep_txopLimit); 1021138568Ssam } 1022138568Ssam 1023178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 1024156524Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 1025188780Ssam static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 1026188780Ssam [IEEE80211_MODE_AUTO] = 3, 1027188780Ssam [IEEE80211_MODE_11A] = 3, 1028188780Ssam [IEEE80211_MODE_11B] = 4, 1029188780Ssam [IEEE80211_MODE_11G] = 3, 1030188780Ssam [IEEE80211_MODE_FH] = 4, 1031188780Ssam [IEEE80211_MODE_TURBO_A] = 3, 1032188780Ssam [IEEE80211_MODE_TURBO_G] = 3, 1033188780Ssam [IEEE80211_MODE_STURBO_A] = 3, 1034188782Ssam [IEEE80211_MODE_HALF] = 3, 1035188782Ssam [IEEE80211_MODE_QUARTER] = 3, 1036188780Ssam [IEEE80211_MODE_11NA] = 3, 1037188780Ssam [IEEE80211_MODE_11NG] = 3, 1038138568Ssam }; 1039138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 1040138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 1041138568Ssam 1042170530Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; 1043178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1044188863Ssam "update %s (chan+bss) logcwmin %u\n", 1045188863Ssam ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin); 1046138568Ssam } 1047178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 1048138568Ssam /* 1049138568Ssam * Arrange for a beacon update and bump the parameter 1050138568Ssam * set number so associated stations load the new values. 1051138568Ssam */ 1052138568Ssam wme->wme_bssChanParams.cap_info = 1053138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 1054178354Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); 1055138568Ssam } 1056138568Ssam 1057138568Ssam wme->wme_update(ic); 1058138568Ssam 1059178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1060188863Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 1061188863Ssam vap->iv_opmode == IEEE80211_M_STA ? 1062188863Ssam wme->wme_wmeChanParams.cap_info : 1063188863Ssam wme->wme_bssChanParams.cap_info); 1064138568Ssam} 1065138568Ssam 1066138568Ssamvoid 1067178354Ssamieee80211_wme_updateparams(struct ieee80211vap *vap) 1068138568Ssam{ 1069178354Ssam struct ieee80211com *ic = vap->iv_ic; 1070138568Ssam 1071138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 1072178354Ssam IEEE80211_LOCK(ic); 1073178354Ssam ieee80211_wme_updateparams_locked(vap); 1074178354Ssam IEEE80211_UNLOCK(ic); 1075138568Ssam } 1076138568Ssam} 1077138568Ssam 1078178354Ssamstatic void 1079178354Ssamparent_updown(void *arg, int npending) 1080178354Ssam{ 1081178354Ssam struct ifnet *parent = arg; 1082178354Ssam 1083178354Ssam parent->if_ioctl(parent, SIOCSIFFLAGS, NULL); 1084178354Ssam} 1085178354Ssam 1086191746Sthompsastatic void 1087191746Sthompsaupdate_mcast(void *arg, int npending) 1088191746Sthompsa{ 1089191746Sthompsa struct ieee80211com *ic = arg; 1090191746Sthompsa struct ifnet *parent = ic->ic_ifp; 1091191746Sthompsa 1092191746Sthompsa ic->ic_update_mcast(parent); 1093191746Sthompsa} 1094191746Sthompsa 1095191746Sthompsastatic void 1096191746Sthompsaupdate_promisc(void *arg, int npending) 1097191746Sthompsa{ 1098191746Sthompsa struct ieee80211com *ic = arg; 1099191746Sthompsa struct ifnet *parent = ic->ic_ifp; 1100191746Sthompsa 1101191746Sthompsa ic->ic_update_promisc(parent); 1102191746Sthompsa} 1103191746Sthompsa 1104191746Sthompsastatic void 1105191746Sthompsaupdate_channel(void *arg, int npending) 1106191746Sthompsa{ 1107191746Sthompsa struct ieee80211com *ic = arg; 1108191746Sthompsa 1109191746Sthompsa ic->ic_set_channel(ic); 1110191746Sthompsa} 1111191746Sthompsa 1112170530Ssam/* 1113188533Sthompsa * Block until the parent is in a known state. This is 1114188533Sthompsa * used after any operations that dispatch a task (e.g. 1115188533Sthompsa * to auto-configure the parent device up/down). 1116188533Sthompsa */ 1117188533Sthompsavoid 1118188533Sthompsaieee80211_waitfor_parent(struct ieee80211com *ic) 1119188533Sthompsa{ 1120191746Sthompsa taskqueue_block(ic->ic_tq); 1121191746Sthompsa ieee80211_draintask(ic, &ic->ic_parent_task); 1122191746Sthompsa ieee80211_draintask(ic, &ic->ic_mcast_task); 1123191746Sthompsa ieee80211_draintask(ic, &ic->ic_promisc_task); 1124191746Sthompsa ieee80211_draintask(ic, &ic->ic_chan_task); 1125191746Sthompsa ieee80211_draintask(ic, &ic->ic_bmiss_task); 1126191746Sthompsa taskqueue_unblock(ic->ic_tq); 1127188533Sthompsa} 1128188533Sthompsa 1129188533Sthompsa/* 1130178354Ssam * Start a vap running. If this is the first vap to be 1131178354Ssam * set running on the underlying device then we 1132178354Ssam * automatically bring the device up. 1133170530Ssam */ 1134178354Ssamvoid 1135178354Ssamieee80211_start_locked(struct ieee80211vap *vap) 1136170530Ssam{ 1137178354Ssam struct ifnet *ifp = vap->iv_ifp; 1138178354Ssam struct ieee80211com *ic = vap->iv_ic; 1139178354Ssam struct ifnet *parent = ic->ic_ifp; 1140170530Ssam 1141178354Ssam IEEE80211_LOCK_ASSERT(ic); 1142178354Ssam 1143178354Ssam IEEE80211_DPRINTF(vap, 1144170530Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1145178354Ssam "start running, %d vaps running\n", ic->ic_nrunning); 1146170530Ssam 1147178354Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1148178354Ssam /* 1149178354Ssam * Mark us running. Note that it's ok to do this first; 1150178354Ssam * if we need to bring the parent device up we defer that 1151178354Ssam * to avoid dropping the com lock. We expect the device 1152178354Ssam * to respond to being marked up by calling back into us 1153178354Ssam * through ieee80211_start_all at which point we'll come 1154178354Ssam * back in here and complete the work. 1155178354Ssam */ 1156178354Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 1157178354Ssam /* 1158178354Ssam * We are not running; if this we are the first vap 1159178354Ssam * to be brought up auto-up the parent if necessary. 1160178354Ssam */ 1161178354Ssam if (ic->ic_nrunning++ == 0 && 1162178354Ssam (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1163178354Ssam IEEE80211_DPRINTF(vap, 1164178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1165178354Ssam "%s: up parent %s\n", __func__, parent->if_xname); 1166178354Ssam parent->if_flags |= IFF_UP; 1167191746Sthompsa ieee80211_runtask(ic, &ic->ic_parent_task); 1168178354Ssam return; 1169178354Ssam } 1170178354Ssam } 1171170530Ssam /* 1172178354Ssam * If the parent is up and running, then kick the 1173178354Ssam * 802.11 state machine as appropriate. 1174170530Ssam */ 1175178354Ssam if ((parent->if_drv_flags & IFF_DRV_RUNNING) && 1176178354Ssam vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { 1177178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) { 1178178354Ssam#if 0 1179178354Ssam /* XXX bypasses scan too easily; disable for now */ 1180178354Ssam /* 1181178354Ssam * Try to be intelligent about clocking the state 1182178354Ssam * machine. If we're currently in RUN state then 1183178354Ssam * we should be able to apply any new state/parameters 1184178354Ssam * simply by re-associating. Otherwise we need to 1185178354Ssam * re-scan to select an appropriate ap. 1186178354Ssam */ 1187178354Ssam if (vap->iv_state >= IEEE80211_S_RUN) 1188178354Ssam ieee80211_new_state_locked(vap, 1189178354Ssam IEEE80211_S_ASSOC, 1); 1190178354Ssam else 1191178354Ssam#endif 1192178354Ssam ieee80211_new_state_locked(vap, 1193178354Ssam IEEE80211_S_SCAN, 0); 1194170530Ssam } else { 1195170530Ssam /* 1196178354Ssam * For monitor+wds mode there's nothing to do but 1197178354Ssam * start running. Otherwise if this is the first 1198170530Ssam * vap to be brought up, start a scan which may be 1199170530Ssam * preempted if the station is locked to a particular 1200170530Ssam * channel. 1201170530Ssam */ 1202191746Sthompsa vap->iv_flags_ext |= IEEE80211_FEXT_REINIT; 1203178354Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR || 1204178354Ssam vap->iv_opmode == IEEE80211_M_WDS) 1205178354Ssam ieee80211_new_state_locked(vap, 1206178354Ssam IEEE80211_S_RUN, -1); 1207178354Ssam else 1208178354Ssam ieee80211_new_state_locked(vap, 1209178354Ssam IEEE80211_S_SCAN, 0); 1210170530Ssam } 1211170530Ssam } 1212170530Ssam} 1213170530Ssam 1214170530Ssam/* 1215178354Ssam * Start a single vap. 1216178354Ssam */ 1217178354Ssamvoid 1218178354Ssamieee80211_init(void *arg) 1219178354Ssam{ 1220178354Ssam struct ieee80211vap *vap = arg; 1221178354Ssam 1222178354Ssam /* 1223178354Ssam * This routine is publicly accessible through the vap's 1224178354Ssam * if_init method so guard against calls during detach. 1225178354Ssam * ieee80211_vap_detach null's the backpointer before 1226178354Ssam * tearing down state to signal any callback should be 1227178354Ssam * rejected/ignored. 1228178354Ssam */ 1229178354Ssam if (vap != NULL) { 1230178354Ssam IEEE80211_DPRINTF(vap, 1231178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1232178354Ssam "%s\n", __func__); 1233178354Ssam 1234178354Ssam IEEE80211_LOCK(vap->iv_ic); 1235178354Ssam ieee80211_start_locked(vap); 1236178354Ssam IEEE80211_UNLOCK(vap->iv_ic); 1237178354Ssam } 1238178354Ssam} 1239178354Ssam 1240178354Ssam/* 1241178354Ssam * Start all runnable vap's on a device. 1242178354Ssam */ 1243178354Ssamvoid 1244178354Ssamieee80211_start_all(struct ieee80211com *ic) 1245178354Ssam{ 1246178354Ssam struct ieee80211vap *vap; 1247178354Ssam 1248178354Ssam IEEE80211_LOCK(ic); 1249178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1250178354Ssam struct ifnet *ifp = vap->iv_ifp; 1251178354Ssam if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ 1252178354Ssam ieee80211_start_locked(vap); 1253178354Ssam } 1254178354Ssam IEEE80211_UNLOCK(ic); 1255178354Ssam} 1256178354Ssam 1257178354Ssam/* 1258178354Ssam * Stop a vap. We force it down using the state machine 1259178354Ssam * then mark it's ifnet not running. If this is the last 1260178354Ssam * vap running on the underlying device then we close it 1261178354Ssam * too to insure it will be properly initialized when the 1262178354Ssam * next vap is brought up. 1263178354Ssam */ 1264178354Ssamvoid 1265178354Ssamieee80211_stop_locked(struct ieee80211vap *vap) 1266178354Ssam{ 1267178354Ssam struct ieee80211com *ic = vap->iv_ic; 1268178354Ssam struct ifnet *ifp = vap->iv_ifp; 1269178354Ssam struct ifnet *parent = ic->ic_ifp; 1270178354Ssam 1271178354Ssam IEEE80211_LOCK_ASSERT(ic); 1272178354Ssam 1273178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1274178354Ssam "stop running, %d vaps running\n", ic->ic_nrunning); 1275178354Ssam 1276178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); 1277178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1278178354Ssam ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */ 1279178354Ssam if (--ic->ic_nrunning == 0 && 1280178354Ssam (parent->if_drv_flags & IFF_DRV_RUNNING)) { 1281178354Ssam IEEE80211_DPRINTF(vap, 1282178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1283178354Ssam "down parent %s\n", parent->if_xname); 1284178354Ssam parent->if_flags &= ~IFF_UP; 1285191746Sthompsa ieee80211_runtask(ic, &ic->ic_parent_task); 1286178354Ssam } 1287178354Ssam } 1288178354Ssam} 1289178354Ssam 1290178354Ssamvoid 1291178354Ssamieee80211_stop(struct ieee80211vap *vap) 1292178354Ssam{ 1293178354Ssam struct ieee80211com *ic = vap->iv_ic; 1294178354Ssam 1295178354Ssam IEEE80211_LOCK(ic); 1296178354Ssam ieee80211_stop_locked(vap); 1297178354Ssam IEEE80211_UNLOCK(ic); 1298178354Ssam} 1299178354Ssam 1300178354Ssam/* 1301178354Ssam * Stop all vap's running on a device. 1302178354Ssam */ 1303178354Ssamvoid 1304178354Ssamieee80211_stop_all(struct ieee80211com *ic) 1305178354Ssam{ 1306178354Ssam struct ieee80211vap *vap; 1307178354Ssam 1308178354Ssam IEEE80211_LOCK(ic); 1309178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1310178354Ssam struct ifnet *ifp = vap->iv_ifp; 1311178354Ssam if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ 1312178354Ssam ieee80211_stop_locked(vap); 1313178354Ssam } 1314178354Ssam IEEE80211_UNLOCK(ic); 1315188533Sthompsa 1316188533Sthompsa ieee80211_waitfor_parent(ic); 1317178354Ssam} 1318178354Ssam 1319178354Ssam/* 1320179391Ssam * Stop all vap's running on a device and arrange 1321179391Ssam * for those that were running to be resumed. 1322179391Ssam */ 1323179391Ssamvoid 1324179391Ssamieee80211_suspend_all(struct ieee80211com *ic) 1325179391Ssam{ 1326179391Ssam struct ieee80211vap *vap; 1327179391Ssam 1328179391Ssam IEEE80211_LOCK(ic); 1329179391Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1330179391Ssam struct ifnet *ifp = vap->iv_ifp; 1331179391Ssam if (IFNET_IS_UP_RUNNING(ifp)) { /* NB: avoid recursion */ 1332179391Ssam vap->iv_flags_ext |= IEEE80211_FEXT_RESUME; 1333179391Ssam ieee80211_stop_locked(vap); 1334179391Ssam } 1335179391Ssam } 1336179391Ssam IEEE80211_UNLOCK(ic); 1337188533Sthompsa 1338188533Sthompsa ieee80211_waitfor_parent(ic); 1339179391Ssam} 1340179391Ssam 1341179391Ssam/* 1342179391Ssam * Start all vap's marked for resume. 1343179391Ssam */ 1344179391Ssamvoid 1345179391Ssamieee80211_resume_all(struct ieee80211com *ic) 1346179391Ssam{ 1347179391Ssam struct ieee80211vap *vap; 1348179391Ssam 1349179391Ssam IEEE80211_LOCK(ic); 1350179391Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1351179391Ssam struct ifnet *ifp = vap->iv_ifp; 1352179391Ssam if (!IFNET_IS_UP_RUNNING(ifp) && 1353179391Ssam (vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) { 1354179391Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME; 1355179391Ssam ieee80211_start_locked(vap); 1356179391Ssam } 1357179391Ssam } 1358179391Ssam IEEE80211_UNLOCK(ic); 1359179391Ssam} 1360179391Ssam 1361153349Ssamvoid 1362153349Ssamieee80211_beacon_miss(struct ieee80211com *ic) 1363153349Ssam{ 1364191746Sthompsa IEEE80211_LOCK(ic); 1365191746Sthompsa if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { 1366191746Sthompsa /* Process in a taskq, the handler may reenter the driver */ 1367191746Sthompsa ieee80211_runtask(ic, &ic->ic_bmiss_task); 1368191746Sthompsa } 1369191746Sthompsa IEEE80211_UNLOCK(ic); 1370191746Sthompsa} 1371191746Sthompsa 1372191746Sthompsastatic void 1373191746Sthompsabeacon_miss(void *arg, int npending) 1374191746Sthompsa{ 1375191746Sthompsa struct ieee80211com *ic = arg; 1376178354Ssam struct ieee80211vap *vap; 1377153349Ssam 1378178354Ssam /* XXX locking */ 1379178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1380153349Ssam /* 1381178354Ssam * We only pass events through for sta vap's in RUN state; 1382178354Ssam * may be too restrictive but for now this saves all the 1383178354Ssam * handlers duplicating these checks. 1384153349Ssam */ 1385178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 1386178354Ssam vap->iv_state == IEEE80211_S_RUN && 1387178354Ssam vap->iv_bmiss != NULL) 1388178354Ssam vap->iv_bmiss(vap); 1389153349Ssam } 1390153349Ssam} 1391153349Ssam 1392191746Sthompsastatic void 1393191746Sthompsabeacon_swmiss(void *arg, int npending) 1394191746Sthompsa{ 1395191746Sthompsa struct ieee80211vap *vap = arg; 1396191746Sthompsa 1397191746Sthompsa if (vap->iv_state != IEEE80211_S_RUN) 1398191746Sthompsa return; 1399191746Sthompsa 1400191746Sthompsa /* XXX Call multiple times if npending > zero? */ 1401191746Sthompsa vap->iv_bmiss(vap); 1402191746Sthompsa} 1403191746Sthompsa 1404154736Ssam/* 1405154736Ssam * Software beacon miss handling. Check if any beacons 1406154736Ssam * were received in the last period. If not post a 1407154736Ssam * beacon miss; otherwise reset the counter. 1408154736Ssam */ 1409178354Ssamvoid 1410154736Ssamieee80211_swbmiss(void *arg) 1411154736Ssam{ 1412178354Ssam struct ieee80211vap *vap = arg; 1413179217Ssam struct ieee80211com *ic = vap->iv_ic; 1414154736Ssam 1415179217Ssam /* XXX sleep state? */ 1416179217Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 1417179217Ssam ("wrong state %d", vap->iv_state)); 1418179217Ssam 1419179217Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 1420179217Ssam /* 1421179217Ssam * If scanning just ignore and reset state. If we get a 1422179217Ssam * bmiss after coming out of scan because we haven't had 1423179217Ssam * time to receive a beacon then we should probe the AP 1424179217Ssam * before posting a real bmiss (unless iv_bmiss_max has 1425179217Ssam * been artifiically lowered). A cleaner solution might 1426179217Ssam * be to disable the timer on scan start/end but to handle 1427179217Ssam * case of multiple sta vap's we'd need to disable the 1428179217Ssam * timers of all affected vap's. 1429179217Ssam */ 1430179217Ssam vap->iv_swbmiss_count = 0; 1431179217Ssam } else if (vap->iv_swbmiss_count == 0) { 1432178354Ssam if (vap->iv_bmiss != NULL) 1433191746Sthompsa ieee80211_runtask(ic, &vap->iv_swbmiss_task); 1434178354Ssam if (vap->iv_bmiss_count == 0) /* don't re-arm timer */ 1435154736Ssam return; 1436154736Ssam } else 1437178354Ssam vap->iv_swbmiss_count = 0; 1438178354Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 1439178354Ssam ieee80211_swbmiss, vap); 1440154736Ssam} 1441154736Ssam 1442178354Ssam/* 1443178354Ssam * Start an 802.11h channel switch. We record the parameters, 1444178354Ssam * mark the operation pending, notify each vap through the 1445178354Ssam * beacon update mechanism so it can update the beacon frame 1446178354Ssam * contents, and then switch vap's to CSA state to block outbound 1447178354Ssam * traffic. Devices that handle CSA directly can use the state 1448178354Ssam * switch to do the right thing so long as they call 1449178354Ssam * ieee80211_csa_completeswitch when it's time to complete the 1450178354Ssam * channel change. Devices that depend on the net80211 layer can 1451178354Ssam * use ieee80211_beacon_update to handle the countdown and the 1452178354Ssam * channel switch. 1453178354Ssam */ 1454178354Ssamvoid 1455178354Ssamieee80211_csa_startswitch(struct ieee80211com *ic, 1456178354Ssam struct ieee80211_channel *c, int mode, int count) 1457178354Ssam{ 1458178354Ssam struct ieee80211vap *vap; 1459178354Ssam 1460178354Ssam IEEE80211_LOCK_ASSERT(ic); 1461178354Ssam 1462178354Ssam ic->ic_csa_newchan = c; 1463178354Ssam ic->ic_csa_count = count; 1464178354Ssam /* XXX record mode? */ 1465178354Ssam ic->ic_flags |= IEEE80211_F_CSAPENDING; 1466178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1467178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP || 1468178354Ssam vap->iv_opmode == IEEE80211_M_IBSS) 1469178354Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); 1470178354Ssam /* switch to CSA state to block outbound traffic */ 1471178354Ssam if (vap->iv_state == IEEE80211_S_RUN) 1472178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); 1473178354Ssam } 1474178354Ssam ieee80211_notify_csa(ic, c, mode, count); 1475178354Ssam} 1476178354Ssam 1477178354Ssam/* 1478178354Ssam * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. 1479178354Ssam * We clear state and move all vap's in CSA state to RUN state 1480178354Ssam * so they can again transmit. 1481178354Ssam */ 1482178354Ssamvoid 1483178354Ssamieee80211_csa_completeswitch(struct ieee80211com *ic) 1484178354Ssam{ 1485178354Ssam struct ieee80211vap *vap; 1486178354Ssam 1487178354Ssam IEEE80211_LOCK_ASSERT(ic); 1488178354Ssam 1489178354Ssam KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); 1490178354Ssam 1491178354Ssam ieee80211_setcurchan(ic, ic->ic_csa_newchan); 1492178354Ssam ic->ic_csa_newchan = NULL; 1493178354Ssam ic->ic_flags &= ~IEEE80211_F_CSAPENDING; 1494178354Ssam 1495178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1496178354Ssam if (vap->iv_state == IEEE80211_S_CSA) 1497178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); 1498178354Ssam} 1499178354Ssam 1500178354Ssam/* 1501178354Ssam * Complete a DFS CAC started by ieee80211_dfs_cac_start. 1502178354Ssam * We clear state and move all vap's in CAC state to RUN state. 1503178354Ssam */ 1504178354Ssamvoid 1505178354Ssamieee80211_cac_completeswitch(struct ieee80211vap *vap0) 1506178354Ssam{ 1507178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1508178354Ssam struct ieee80211vap *vap; 1509178354Ssam 1510178354Ssam IEEE80211_LOCK(ic); 1511178354Ssam /* 1512178354Ssam * Complete CAC state change for lead vap first; then 1513178354Ssam * clock all the other vap's waiting. 1514178354Ssam */ 1515178354Ssam KASSERT(vap0->iv_state == IEEE80211_S_CAC, 1516178354Ssam ("wrong state %d", vap0->iv_state)); 1517178354Ssam ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); 1518178354Ssam 1519178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1520178354Ssam if (vap->iv_state == IEEE80211_S_CAC) 1521178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); 1522178354Ssam IEEE80211_UNLOCK(ic); 1523178354Ssam} 1524178354Ssam 1525178354Ssam/* 1526178354Ssam * Force all vap's other than the specified vap to the INIT state 1527178354Ssam * and mark them as waiting for a scan to complete. These vaps 1528178354Ssam * will be brought up when the scan completes and the scanning vap 1529178354Ssam * reaches RUN state by wakeupwaiting. 1530178354Ssam */ 1531154736Ssamstatic void 1532178354Ssammarkwaiting(struct ieee80211vap *vap0) 1533147765Ssam{ 1534178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1535178354Ssam struct ieee80211vap *vap; 1536147765Ssam 1537178354Ssam IEEE80211_LOCK_ASSERT(ic); 1538178354Ssam 1539191746Sthompsa /* 1540191746Sthompsa * A vap list entry can not disappear since we are running on the 1541191746Sthompsa * taskqueue and a vap destroy will queue and drain another state 1542191746Sthompsa * change task. 1543191746Sthompsa */ 1544178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1545178354Ssam if (vap == vap0) 1546178354Ssam continue; 1547178354Ssam if (vap->iv_state != IEEE80211_S_INIT) { 1548191746Sthompsa /* NB: iv_newstate may drop the lock */ 1549178354Ssam vap->iv_newstate(vap, IEEE80211_S_INIT, 0); 1550178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1551178354Ssam } 1552147765Ssam } 1553147765Ssam} 1554147765Ssam 1555178354Ssam/* 1556178354Ssam * Wakeup all vap's waiting for a scan to complete. This is the 1557178354Ssam * companion to markwaiting (above) and is used to coordinate 1558178354Ssam * multiple vaps scanning. 1559191746Sthompsa * This is called from the state taskqueue. 1560178354Ssam */ 1561147765Ssamstatic void 1562178354Ssamwakeupwaiting(struct ieee80211vap *vap0) 1563147765Ssam{ 1564178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1565178354Ssam struct ieee80211vap *vap; 1566147765Ssam 1567178354Ssam IEEE80211_LOCK_ASSERT(ic); 1568178354Ssam 1569191746Sthompsa /* 1570191746Sthompsa * A vap list entry can not disappear since we are running on the 1571191746Sthompsa * taskqueue and a vap destroy will queue and drain another state 1572191746Sthompsa * change task. 1573191746Sthompsa */ 1574178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1575178354Ssam if (vap == vap0) 1576178354Ssam continue; 1577178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { 1578178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; 1579178354Ssam /* NB: sta's cannot go INIT->RUN */ 1580191746Sthompsa /* NB: iv_newstate may drop the lock */ 1581178354Ssam vap->iv_newstate(vap, 1582178354Ssam vap->iv_opmode == IEEE80211_M_STA ? 1583178354Ssam IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); 1584178354Ssam } 1585178354Ssam } 1586147765Ssam} 1587147765Ssam 1588170530Ssam/* 1589178354Ssam * Handle post state change work common to all operating modes. 1590170530Ssam */ 1591170530Ssamstatic void 1592191746Sthompsaieee80211_newstate_cb(void *xvap, int npending) 1593170530Ssam{ 1594191746Sthompsa struct ieee80211vap *vap = xvap; 1595178354Ssam struct ieee80211com *ic = vap->iv_ic; 1596191746Sthompsa enum ieee80211_state nstate, ostate; 1597191746Sthompsa int arg, rc; 1598178354Ssam 1599191746Sthompsa IEEE80211_LOCK(ic); 1600191746Sthompsa nstate = vap->iv_nstate; 1601191746Sthompsa arg = vap->iv_nstate_arg; 1602178354Ssam 1603191746Sthompsa if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) { 1604191746Sthompsa /* 1605191746Sthompsa * We have been requested to drop back to the INIT before 1606191746Sthompsa * proceeding to the new state. 1607191746Sthompsa */ 1608191746Sthompsa IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1609191746Sthompsa "%s: %s -> %s arg %d\n", __func__, 1610191746Sthompsa ieee80211_state_name[vap->iv_state], 1611191746Sthompsa ieee80211_state_name[IEEE80211_S_INIT], arg); 1612191746Sthompsa vap->iv_newstate(vap, IEEE80211_S_INIT, arg); 1613191746Sthompsa vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT; 1614191746Sthompsa } 1615191746Sthompsa 1616191746Sthompsa ostate = vap->iv_state; 1617191746Sthompsa if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) { 1618191746Sthompsa /* 1619191746Sthompsa * SCAN was forced; e.g. on beacon miss. Force other running 1620191746Sthompsa * vap's to INIT state and mark them as waiting for the scan to 1621191746Sthompsa * complete. This insures they don't interfere with our 1622191746Sthompsa * scanning. Since we are single threaded the vaps can not 1623191746Sthompsa * transition again while we are executing. 1624191746Sthompsa * 1625191746Sthompsa * XXX not always right, assumes ap follows sta 1626191746Sthompsa */ 1627191746Sthompsa markwaiting(vap); 1628191746Sthompsa } 1629178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1630191746Sthompsa "%s: %s -> %s arg %d\n", __func__, 1631191746Sthompsa ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); 1632178354Ssam 1633191746Sthompsa rc = vap->iv_newstate(vap, nstate, arg); 1634191746Sthompsa vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT; 1635191746Sthompsa if (rc != 0) { 1636191746Sthompsa /* State transition failed */ 1637191746Sthompsa KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred")); 1638191746Sthompsa KASSERT(nstate != IEEE80211_S_INIT, 1639191746Sthompsa ("INIT state change failed")); 1640191746Sthompsa IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1641191746Sthompsa "%s: %s returned error %d\n", __func__, 1642191746Sthompsa ieee80211_state_name[nstate], rc); 1643191746Sthompsa goto done; 1644191746Sthompsa } 1645191746Sthompsa 1646191746Sthompsa /* No actual transition, skip post processing */ 1647191746Sthompsa if (ostate == nstate) 1648191746Sthompsa goto done; 1649191746Sthompsa 1650178354Ssam if (nstate == IEEE80211_S_RUN) { 1651178354Ssam /* 1652178354Ssam * OACTIVE may be set on the vap if the upper layer 1653178354Ssam * tried to transmit (e.g. IPv6 NDP) before we reach 1654178354Ssam * RUN state. Clear it and restart xmit. 1655178354Ssam * 1656178354Ssam * Note this can also happen as a result of SLEEP->RUN 1657178354Ssam * (i.e. coming out of power save mode). 1658178354Ssam */ 1659178354Ssam vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1660178354Ssam if_start(vap->iv_ifp); 1661178354Ssam 1662178354Ssam /* bring up any vaps waiting on us */ 1663178354Ssam wakeupwaiting(vap); 1664178354Ssam } else if (nstate == IEEE80211_S_INIT) { 1665178354Ssam /* 1666178354Ssam * Flush the scan cache if we did the last scan (XXX?) 1667178354Ssam * and flush any frames on send queues from this vap. 1668178354Ssam * Note the mgt q is used only for legacy drivers and 1669178354Ssam * will go away shortly. 1670178354Ssam */ 1671178354Ssam ieee80211_scan_flush(vap); 1672178354Ssam 1673178354Ssam /* XXX NB: cast for altq */ 1674178354Ssam ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); 1675170530Ssam } 1676191746Sthompsadone: 1677191746Sthompsa IEEE80211_UNLOCK(ic); 1678170530Ssam} 1679170530Ssam 1680178354Ssam/* 1681178354Ssam * Public interface for initiating a state machine change. 1682178354Ssam * This routine single-threads the request and coordinates 1683178354Ssam * the scheduling of multiple vaps for the purpose of selecting 1684178354Ssam * an operating channel. Specifically the following scenarios 1685178354Ssam * are handled: 1686178354Ssam * o only one vap can be selecting a channel so on transition to 1687178354Ssam * SCAN state if another vap is already scanning then 1688178354Ssam * mark the caller for later processing and return without 1689178354Ssam * doing anything (XXX? expectations by caller of synchronous operation) 1690178354Ssam * o only one vap can be doing CAC of a channel so on transition to 1691178354Ssam * CAC state if another vap is already scanning for radar then 1692178354Ssam * mark the caller for later processing and return without 1693178354Ssam * doing anything (XXX? expectations by caller of synchronous operation) 1694178354Ssam * o if another vap is already running when a request is made 1695178354Ssam * to SCAN then an operating channel has been chosen; bypass 1696178354Ssam * the scan and just join the channel 1697178354Ssam * 1698178354Ssam * Note that the state change call is done through the iv_newstate 1699178354Ssam * method pointer so any driver routine gets invoked. The driver 1700178354Ssam * will normally call back into operating mode-specific 1701178354Ssam * ieee80211_newstate routines (below) unless it needs to completely 1702178354Ssam * bypass the state machine (e.g. because the firmware has it's 1703178354Ssam * own idea how things should work). Bypassing the net80211 layer 1704178354Ssam * is usually a mistake and indicates lack of proper integration 1705178354Ssam * with the net80211 layer. 1706178354Ssam */ 1707117811Ssamstatic int 1708178354Ssamieee80211_new_state_locked(struct ieee80211vap *vap, 1709178354Ssam enum ieee80211_state nstate, int arg) 1710116742Ssam{ 1711178354Ssam struct ieee80211com *ic = vap->iv_ic; 1712178354Ssam struct ieee80211vap *vp; 1713117811Ssam enum ieee80211_state ostate; 1714191746Sthompsa int nrunning, nscanning; 1715116742Ssam 1716178354Ssam IEEE80211_LOCK_ASSERT(ic); 1717178354Ssam 1718191746Sthompsa if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) { 1719191746Sthompsa if (vap->iv_nstate == IEEE80211_S_INIT) { 1720191746Sthompsa /* 1721191746Sthompsa * XXX The vap is being stopped, do no allow any other 1722191746Sthompsa * state changes until this is completed. 1723191746Sthompsa */ 1724191746Sthompsa return -1; 1725191768Sthompsa } else if (vap->iv_state != vap->iv_nstate) { 1726191746Sthompsa#if 0 1727191768Sthompsa /* Warn if the previous state hasn't completed. */ 1728191768Sthompsa IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1729191768Sthompsa "%s: pending %s -> %s transition lost\n", __func__, 1730191768Sthompsa ieee80211_state_name[vap->iv_state], 1731191768Sthompsa ieee80211_state_name[vap->iv_nstate]); 1732191746Sthompsa#else 1733191768Sthompsa /* XXX temporarily enable to identify issues */ 1734191768Sthompsa if_printf(vap->iv_ifp, 1735191768Sthompsa "%s: pending %s -> %s transition lost\n", 1736191768Sthompsa __func__, ieee80211_state_name[vap->iv_state], 1737191768Sthompsa ieee80211_state_name[vap->iv_nstate]); 1738191746Sthompsa#endif 1739191768Sthompsa } 1740191746Sthompsa } 1741191746Sthompsa 1742178354Ssam nrunning = nscanning = 0; 1743178354Ssam /* XXX can track this state instead of calculating */ 1744178354Ssam TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { 1745178354Ssam if (vp != vap) { 1746178354Ssam if (vp->iv_state >= IEEE80211_S_RUN) 1747178354Ssam nrunning++; 1748178354Ssam /* XXX doesn't handle bg scan */ 1749178354Ssam /* NB: CAC+AUTH+ASSOC treated like SCAN */ 1750178354Ssam else if (vp->iv_state > IEEE80211_S_INIT) 1751178354Ssam nscanning++; 1752178354Ssam } 1753178354Ssam } 1754178354Ssam ostate = vap->iv_state; 1755178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1756178354Ssam "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, 1757178354Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate], 1758178354Ssam nrunning, nscanning); 1759116742Ssam switch (nstate) { 1760116742Ssam case IEEE80211_S_SCAN: 1761178354Ssam if (ostate == IEEE80211_S_INIT) { 1762178354Ssam /* 1763178354Ssam * INIT -> SCAN happens on initial bringup. 1764178354Ssam */ 1765178354Ssam KASSERT(!(nscanning && nrunning), 1766178354Ssam ("%d scanning and %d running", nscanning, nrunning)); 1767178354Ssam if (nscanning) { 1768116742Ssam /* 1769178354Ssam * Someone is scanning, defer our state 1770178354Ssam * change until the work has completed. 1771116742Ssam */ 1772178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1773178354Ssam "%s: defer %s -> %s\n", 1774178354Ssam __func__, ieee80211_state_name[ostate], 1775178354Ssam ieee80211_state_name[nstate]); 1776178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1777191746Sthompsa return 0; 1778116742Ssam } 1779178354Ssam if (nrunning) { 1780170530Ssam /* 1781178354Ssam * Someone is operating; just join the channel 1782178354Ssam * they have chosen. 1783170530Ssam */ 1784178354Ssam /* XXX kill arg? */ 1785178354Ssam /* XXX check each opmode, adhoc? */ 1786178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 1787178354Ssam nstate = IEEE80211_S_SCAN; 1788178354Ssam else 1789178354Ssam nstate = IEEE80211_S_RUN; 1790138568Ssam#ifdef IEEE80211_DEBUG 1791178354Ssam if (nstate != IEEE80211_S_SCAN) { 1792178354Ssam IEEE80211_DPRINTF(vap, 1793178354Ssam IEEE80211_MSG_STATE, 1794178354Ssam "%s: override, now %s -> %s\n", 1795178354Ssam __func__, 1796178354Ssam ieee80211_state_name[ostate], 1797178354Ssam ieee80211_state_name[nstate]); 1798178354Ssam } 1799138568Ssam#endif 1800170530Ssam } 1801116742Ssam } 1802178354Ssam break; 1803178354Ssam case IEEE80211_S_RUN: 1804178354Ssam if (vap->iv_opmode == IEEE80211_M_WDS && 1805178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && 1806178354Ssam nscanning) { 1807154736Ssam /* 1808178354Ssam * Legacy WDS with someone else scanning; don't 1809178354Ssam * go online until that completes as we should 1810178354Ssam * follow the other vap to the channel they choose. 1811154736Ssam */ 1812178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1813178354Ssam "%s: defer %s -> %s (legacy WDS)\n", __func__, 1814178354Ssam ieee80211_state_name[ostate], 1815178354Ssam ieee80211_state_name[nstate]); 1816178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1817191746Sthompsa return 0; 1818154736Ssam } 1819178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 1820178354Ssam IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && 1821178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && 1822178354Ssam !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { 1823178354Ssam /* 1824178354Ssam * This is a DFS channel, transition to CAC state 1825178354Ssam * instead of RUN. This allows us to initiate 1826178354Ssam * Channel Availability Check (CAC) as specified 1827178354Ssam * by 11h/DFS. 1828178354Ssam */ 1829178354Ssam nstate = IEEE80211_S_CAC; 1830178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1831178354Ssam "%s: override %s -> %s (DFS)\n", __func__, 1832178354Ssam ieee80211_state_name[ostate], 1833178354Ssam ieee80211_state_name[nstate]); 1834138568Ssam } 1835116742Ssam break; 1836178354Ssam case IEEE80211_S_INIT: 1837178354Ssam if (ostate == IEEE80211_S_INIT ) { 1838178354Ssam /* XXX don't believe this */ 1839178354Ssam /* INIT -> INIT. nothing to do */ 1840178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; 1841178354Ssam } 1842178354Ssam /* fall thru... */ 1843172058Ssam default: 1844172058Ssam break; 1845116742Ssam } 1846191746Sthompsa /* defer the state change to a thread */ 1847191746Sthompsa vap->iv_nstate = nstate; 1848191746Sthompsa vap->iv_nstate_arg = arg; 1849191746Sthompsa vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT; 1850191746Sthompsa ieee80211_runtask(ic, &vap->iv_nstate_task); 1851191746Sthompsa return EINPROGRESS; 1852116742Ssam} 1853178354Ssam 1854178354Ssamint 1855178354Ssamieee80211_new_state(struct ieee80211vap *vap, 1856178354Ssam enum ieee80211_state nstate, int arg) 1857178354Ssam{ 1858178354Ssam struct ieee80211com *ic = vap->iv_ic; 1859178354Ssam int rc; 1860178354Ssam 1861178354Ssam IEEE80211_LOCK(ic); 1862178354Ssam rc = ieee80211_new_state_locked(vap, nstate, arg); 1863178354Ssam IEEE80211_UNLOCK(ic); 1864178354Ssam return rc; 1865178354Ssam} 1866