ieee80211_proto.c revision 172058
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3170360Ssam * Copyright (c) 2002-2007 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 172058 2007-09-05 21:31:32Z sam $"); 29116742Ssam 30116742Ssam/* 31116742Ssam * IEEE 802.11 protocol support. 32116742Ssam */ 33116742Ssam 34116742Ssam#include "opt_inet.h" 35116742Ssam 36116742Ssam#include <sys/param.h> 37138568Ssam#include <sys/kernel.h> 38170530Ssam#include <sys/systm.h> 39170530Ssam 40116742Ssam#include <sys/socket.h> 41116742Ssam 42116742Ssam#include <net/if.h> 43116742Ssam#include <net/if_media.h> 44138568Ssam#include <net/ethernet.h> /* XXX for ether_sprintf */ 45116742Ssam 46116742Ssam#include <net80211/ieee80211_var.h> 47116742Ssam 48138568Ssam/* XXX tunables */ 49138568Ssam#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 50138568Ssam#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 51116742Ssam 52116742Ssam#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 53116742Ssam 54116742Ssamconst char *ieee80211_mgt_subtype_name[] = { 55116742Ssam "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 56116742Ssam "probe_req", "probe_resp", "reserved#6", "reserved#7", 57116742Ssam "beacon", "atim", "disassoc", "auth", 58116742Ssam "deauth", "reserved#13", "reserved#14", "reserved#15" 59116742Ssam}; 60138568Ssamconst char *ieee80211_ctl_subtype_name[] = { 61138568Ssam "reserved#0", "reserved#1", "reserved#2", "reserved#3", 62138568Ssam "reserved#3", "reserved#5", "reserved#6", "reserved#7", 63138568Ssam "reserved#8", "reserved#9", "ps_poll", "rts", 64138568Ssam "cts", "ack", "cf_end", "cf_end_ack" 65138568Ssam}; 66167283Ssamconst char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 67167283Ssam "IBSS", /* IEEE80211_M_IBSS */ 68167283Ssam "STA", /* IEEE80211_M_STA */ 69167283Ssam "#2", 70167283Ssam "AHDEMO", /* IEEE80211_M_AHDEMO */ 71167283Ssam "#4", "#5", 72167283Ssam "HOSTAP", /* IEEE80211_M_HOSTAP */ 73167283Ssam "#7", 74167283Ssam "MONITOR" /* IEEE80211_M_MONITOR */ 75167283Ssam}; 76117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = { 77117811Ssam "INIT", /* IEEE80211_S_INIT */ 78117811Ssam "SCAN", /* IEEE80211_S_SCAN */ 79117811Ssam "AUTH", /* IEEE80211_S_AUTH */ 80117811Ssam "ASSOC", /* IEEE80211_S_ASSOC */ 81172058Ssam "CAC", /* IEEE80211_S_CAC */ 82172058Ssam "RUN", /* IEEE80211_S_RUN */ 83172058Ssam "CSA", /* IEEE80211_S_CSA */ 84172058Ssam "SLEEP", /* IEEE80211_S_SLEEP */ 85117811Ssam}; 86138568Ssamconst char *ieee80211_wme_acnames[] = { 87138568Ssam "WME_AC_BE", 88138568Ssam "WME_AC_BK", 89138568Ssam "WME_AC_VI", 90138568Ssam "WME_AC_VO", 91138568Ssam "WME_UPSD", 92138568Ssam}; 93116742Ssam 94117811Ssamstatic int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 95117811Ssam 96116742Ssamvoid 97138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 98116742Ssam{ 99138568Ssam struct ifnet *ifp = ic->ic_ifp; 100116742Ssam 101138568Ssam /* XXX room for crypto */ 102138568Ssam ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 103116742Ssam 104116742Ssam ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 105148291Ssam ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 106148290Ssam ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 107153349Ssam ic->ic_bmiss_max = IEEE80211_BMISS_MAX; 108154736Ssam callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); 109170530Ssam callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE); 110153346Ssam ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; 111127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 112138568Ssam ic->ic_roaming = IEEE80211_ROAMING_AUTO; 113116742Ssam 114138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 115138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 116138568Ssam 117121816Sbrooks mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 118116742Ssam 119117811Ssam /* protocol state change handler */ 120117811Ssam ic->ic_newstate = ieee80211_newstate; 121117811Ssam 122116742Ssam /* initialize management frame handlers */ 123116742Ssam ic->ic_recv_mgmt = ieee80211_recv_mgmt; 124116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 125160690Ssam ic->ic_raw_xmit = ieee80211_raw_xmit; 126116742Ssam} 127116742Ssam 128116742Ssamvoid 129138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 130116742Ssam{ 131116742Ssam 132138568Ssam /* 133138568Ssam * This should not be needed as we detach when reseting 134138568Ssam * the state but be conservative here since the 135138568Ssam * authenticator may do things like spawn kernel threads. 136138568Ssam */ 137138568Ssam if (ic->ic_auth->ia_detach) 138138568Ssam ic->ic_auth->ia_detach(ic); 139138568Ssam 140165894Ssam ieee80211_drain_ifq(&ic->ic_mgtq); 141116742Ssam mtx_destroy(&ic->ic_mgtq.ifq_mtx); 142138568Ssam 143138568Ssam /* 144138568Ssam * Detach any ACL'ator. 145138568Ssam */ 146138568Ssam if (ic->ic_acl != NULL) 147138568Ssam ic->ic_acl->iac_detach(ic); 148116742Ssam} 149116742Ssam 150138568Ssam/* 151138568Ssam * Simple-minded authenticator module support. 152138568Ssam */ 153138568Ssam 154138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 155138568Ssam/* XXX well-known names */ 156138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 157138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 158138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 159138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 160138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 161138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 162138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 163138568Ssam}; 164138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 165138568Ssam 166138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 167138568Ssam .ia_name = "wlan_internal", 168138568Ssam .ia_attach = NULL, 169138568Ssam .ia_detach = NULL, 170138568Ssam .ia_node_join = NULL, 171138568Ssam .ia_node_leave = NULL, 172138568Ssam}; 173138568Ssam 174138568Ssam/* 175138568Ssam * Setup internal authenticators once; they are never unregistered. 176138568Ssam */ 177138568Ssamstatic void 178138568Ssamieee80211_auth_setup(void) 179138568Ssam{ 180138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 181138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 182138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 183138568Ssam} 184138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 185138568Ssam 186138568Ssamconst struct ieee80211_authenticator * 187138568Ssamieee80211_authenticator_get(int auth) 188138568Ssam{ 189138568Ssam if (auth >= IEEE80211_AUTH_MAX) 190138568Ssam return NULL; 191138568Ssam if (authenticators[auth] == NULL) 192138568Ssam ieee80211_load_module(auth_modnames[auth]); 193138568Ssam return authenticators[auth]; 194138568Ssam} 195138568Ssam 196116742Ssamvoid 197138568Ssamieee80211_authenticator_register(int type, 198138568Ssam const struct ieee80211_authenticator *auth) 199116742Ssam{ 200138568Ssam if (type >= IEEE80211_AUTH_MAX) 201138568Ssam return; 202138568Ssam authenticators[type] = auth; 203138568Ssam} 204138568Ssam 205138568Ssamvoid 206138568Ssamieee80211_authenticator_unregister(int type) 207138568Ssam{ 208138568Ssam 209138568Ssam if (type >= IEEE80211_AUTH_MAX) 210138568Ssam return; 211138568Ssam authenticators[type] = NULL; 212138568Ssam} 213138568Ssam 214138568Ssam/* 215138568Ssam * Very simple-minded ACL module support. 216138568Ssam */ 217138568Ssam/* XXX just one for now */ 218138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 219138568Ssam 220138568Ssamvoid 221138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 222138568Ssam{ 223138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 224138568Ssam acl = iac; 225138568Ssam} 226138568Ssam 227138568Ssamvoid 228138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 229138568Ssam{ 230138568Ssam if (acl == iac) 231138568Ssam acl = NULL; 232138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 233138568Ssam} 234138568Ssam 235138568Ssamconst struct ieee80211_aclator * 236138568Ssamieee80211_aclator_get(const char *name) 237138568Ssam{ 238138568Ssam if (acl == NULL) 239138568Ssam ieee80211_load_module("wlan_acl"); 240138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 241138568Ssam} 242138568Ssam 243138568Ssamvoid 244170530Ssamieee80211_print_essid(const uint8_t *essid, int len) 245138568Ssam{ 246170530Ssam const uint8_t *p; 247116742Ssam int i; 248116742Ssam 249116742Ssam if (len > IEEE80211_NWID_LEN) 250116742Ssam len = IEEE80211_NWID_LEN; 251116742Ssam /* determine printable or not */ 252116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 253116742Ssam if (*p < ' ' || *p > 0x7e) 254116742Ssam break; 255116742Ssam } 256116742Ssam if (i == len) { 257116742Ssam printf("\""); 258116742Ssam for (i = 0, p = essid; i < len; i++, p++) 259116742Ssam printf("%c", *p); 260116742Ssam printf("\""); 261116742Ssam } else { 262116742Ssam printf("0x"); 263116742Ssam for (i = 0, p = essid; i < len; i++, p++) 264116742Ssam printf("%02x", *p); 265116742Ssam } 266116742Ssam} 267116742Ssam 268116742Ssamvoid 269170530Ssamieee80211_dump_pkt(struct ieee80211com *ic, 270170530Ssam const uint8_t *buf, int len, int rate, int rssi) 271116742Ssam{ 272138568Ssam const struct ieee80211_frame *wh; 273116742Ssam int i; 274116742Ssam 275138568Ssam wh = (const struct ieee80211_frame *)buf; 276116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 277116742Ssam case IEEE80211_FC1_DIR_NODS: 278116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 279116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 280116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 281116742Ssam break; 282116742Ssam case IEEE80211_FC1_DIR_TODS: 283116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 284116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 285116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 286116742Ssam break; 287116742Ssam case IEEE80211_FC1_DIR_FROMDS: 288116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 289116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 290116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 291116742Ssam break; 292116742Ssam case IEEE80211_FC1_DIR_DSTODS: 293170530Ssam printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); 294116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 295116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 296116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 297116742Ssam break; 298116742Ssam } 299116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 300116742Ssam case IEEE80211_FC0_TYPE_DATA: 301116742Ssam printf(" data"); 302116742Ssam break; 303116742Ssam case IEEE80211_FC0_TYPE_MGT: 304116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 305116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 306116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 307116742Ssam break; 308116742Ssam default: 309116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 310116742Ssam break; 311116742Ssam } 312170530Ssam if (IEEE80211_QOS_HAS_SEQ(wh)) { 313170530Ssam const struct ieee80211_qosframe *qwh = 314170530Ssam (const struct ieee80211_qosframe *)buf; 315170530Ssam printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, 316170530Ssam qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); 317170530Ssam } 318138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 319170530Ssam int off; 320170530Ssam 321170530Ssam off = ieee80211_anyhdrspace(ic, wh); 322170530Ssam printf(" WEP [IV %.02x %.02x %.02x", 323170530Ssam buf[off+0], buf[off+1], buf[off+2]); 324170530Ssam if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) 325170530Ssam printf(" %.02x %.02x %.02x", 326170530Ssam buf[off+4], buf[off+5], buf[off+6]); 327170530Ssam printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); 328138568Ssam } 329116742Ssam if (rate >= 0) 330116742Ssam printf(" %dM", rate / 2); 331116742Ssam if (rssi >= 0) 332116742Ssam printf(" +%d", rssi); 333116742Ssam printf("\n"); 334116742Ssam if (len > 0) { 335116742Ssam for (i = 0; i < len; i++) { 336116742Ssam if ((i & 1) == 0) 337116742Ssam printf(" "); 338116742Ssam printf("%02x", buf[i]); 339116742Ssam } 340116742Ssam printf("\n"); 341116742Ssam } 342116742Ssam} 343116742Ssam 344165887Ssamstatic __inline int 345165887Ssamfindrix(const struct ieee80211_rateset *rs, int r) 346165887Ssam{ 347165887Ssam int i; 348165887Ssam 349165887Ssam for (i = 0; i < rs->rs_nrates; i++) 350165887Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 351165887Ssam return i; 352165887Ssam return -1; 353165887Ssam} 354165887Ssam 355116742Ssamint 356167442Ssamieee80211_fix_rate(struct ieee80211_node *ni, 357167442Ssam struct ieee80211_rateset *nrs, int flags) 358116742Ssam{ 359116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 360148299Ssam struct ieee80211com *ic = ni->ni_ic; 361165887Ssam int i, j, rix, error; 362138568Ssam int okrate, badrate, fixedrate; 363165569Ssam const struct ieee80211_rateset *srs; 364170530Ssam uint8_t r; 365116742Ssam 366116742Ssam error = 0; 367170530Ssam okrate = badrate = 0; 368170530Ssam fixedrate = IEEE80211_FIXED_RATE_NONE; 369165569Ssam srs = ieee80211_get_suprates(ic, ni->ni_chan); 370120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 371116742Ssam if (flags & IEEE80211_F_DOSORT) { 372116742Ssam /* 373116742Ssam * Sort rates. 374116742Ssam */ 375116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 376116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 377116742Ssam r = nrs->rs_rates[i]; 378116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 379116742Ssam nrs->rs_rates[j] = r; 380116742Ssam } 381116742Ssam } 382116742Ssam } 383116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 384116742Ssam badrate = r; 385165887Ssam /* 386170530Ssam * Check for fixed rate. 387170530Ssam */ 388170530Ssam if (r == ic->ic_fixed_rate) 389170530Ssam fixedrate = r; 390170530Ssam /* 391165887Ssam * Check against supported rates. 392165887Ssam */ 393165887Ssam rix = findrix(srs, r); 394116742Ssam if (flags & IEEE80211_F_DONEGO) { 395165887Ssam if (rix < 0) { 396120482Ssam /* 397120482Ssam * A rate in the node's rate set is not 398120482Ssam * supported. If this is a basic rate and we 399165887Ssam * are operating as a STA then this is an error. 400120482Ssam * Otherwise we just discard/ignore the rate. 401120482Ssam */ 402165887Ssam if ((flags & IEEE80211_F_JOIN) && 403120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 404116742Ssam error++; 405165887Ssam } else if ((flags & IEEE80211_F_JOIN) == 0) { 406165887Ssam /* 407165887Ssam * Overwrite with the supported rate 408165887Ssam * value so any basic rate bit is set. 409165887Ssam */ 410165887Ssam nrs->rs_rates[i] = srs->rs_rates[rix]; 411116742Ssam } 412116742Ssam } 413165887Ssam if ((flags & IEEE80211_F_DODEL) && rix < 0) { 414116742Ssam /* 415116742Ssam * Delete unacceptable rates. 416116742Ssam */ 417165887Ssam nrs->rs_nrates--; 418165887Ssam for (j = i; j < nrs->rs_nrates; j++) 419165887Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 420165887Ssam nrs->rs_rates[j] = 0; 421165887Ssam continue; 422116742Ssam } 423165887Ssam if (rix >= 0) 424116742Ssam okrate = nrs->rs_rates[i]; 425116742Ssam i++; 426116742Ssam } 427138568Ssam if (okrate == 0 || error != 0 || 428170530Ssam ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate)) 429116742Ssam return badrate | IEEE80211_RATE_BASIC; 430116742Ssam else 431116742Ssam return RV(okrate); 432116742Ssam#undef RV 433116742Ssam} 434116742Ssam 435138568Ssam/* 436138568Ssam * Reset 11g-related state. 437138568Ssam */ 438138568Ssamvoid 439138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 440138568Ssam{ 441138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 442138568Ssam ic->ic_nonerpsta = 0; 443138568Ssam ic->ic_longslotsta = 0; 444138568Ssam /* 445138568Ssam * Short slot time is enabled only when operating in 11g 446138568Ssam * and not in an IBSS. We must also honor whether or not 447138568Ssam * the driver is capable of doing it. 448138568Ssam */ 449138568Ssam ieee80211_set_shortslottime(ic, 450170530Ssam IEEE80211_IS_CHAN_A(ic->ic_curchan) || 451170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_curchan) || 452170530Ssam (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 453138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 454138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 455138568Ssam /* 456138568Ssam * Set short preamble and ERP barker-preamble flags. 457138568Ssam */ 458170530Ssam if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 459138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 460138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 461138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 462138568Ssam } else { 463138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 464138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 465138568Ssam } 466138568Ssam} 467138568Ssam 468138568Ssam/* 469138568Ssam * Set the short slot time state and notify the driver. 470138568Ssam */ 471138568Ssamvoid 472138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 473138568Ssam{ 474138568Ssam if (onoff) 475138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 476138568Ssam else 477138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 478138568Ssam /* notify driver */ 479138568Ssam if (ic->ic_updateslot != NULL) 480138568Ssam ic->ic_updateslot(ic->ic_ifp); 481138568Ssam} 482138568Ssam 483138568Ssam/* 484138568Ssam * Check if the specified rate set supports ERP. 485138568Ssam * NB: the rate set is assumed to be sorted. 486138568Ssam */ 487138568Ssamint 488138568Ssamieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 489138568Ssam{ 490138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 491138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 492138568Ssam int i, j; 493138568Ssam 494138568Ssam if (rs->rs_nrates < N(rates)) 495138568Ssam return 0; 496138568Ssam for (i = 0; i < N(rates); i++) { 497138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 498138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 499138568Ssam if (rates[i] == r) 500138568Ssam goto next; 501138568Ssam if (r > rates[i]) 502138568Ssam return 0; 503138568Ssam } 504138568Ssam return 0; 505138568Ssam next: 506138568Ssam ; 507138568Ssam } 508138568Ssam return 1; 509138568Ssam#undef N 510138568Ssam} 511138568Ssam 512138568Ssam/* 513138568Ssam * Mark the basic rates for the 11g rate table based on the 514138568Ssam * operating mode. For real 11g we mark all the 11b rates 515138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 516138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 517138568Ssam * the basic OFDM rates. 518138568Ssam */ 519138568Ssamvoid 520138568Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 521138568Ssam{ 522170530Ssam static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { 523170530Ssam { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ 524138568Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 525138568Ssam { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 526138568Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 527170530Ssam { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ 528138568Ssam /* IEEE80211_MODE_PUREG (not yet) */ 529138568Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 530170530Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ 531170530Ssam /* IEEE80211_MODE_11NG (mixed b/g) */ 532170530Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 533138568Ssam }; 534138568Ssam int i, j; 535138568Ssam 536138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 537138568Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 538138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 539138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 540138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 541138568Ssam break; 542138568Ssam } 543138568Ssam } 544138568Ssam} 545138568Ssam 546138568Ssam/* 547138568Ssam * WME protocol support. The following parameters come from the spec. 548138568Ssam */ 549138568Ssamtypedef struct phyParamType { 550170530Ssam uint8_t aifsn; 551170530Ssam uint8_t logcwmin; 552170530Ssam uint8_t logcwmax; 553170530Ssam uint16_t txopLimit; 554170530Ssam uint8_t acm; 555138568Ssam} paramType; 556138568Ssam 557138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 558170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */ 559170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */ 560170530Ssam { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_11B */ 561170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */ 562170530Ssam { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_FH */ 563170530Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 564170530Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 565170530Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 566170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */ /* XXXcheck*/ 567170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */ /* XXXcheck*/ 568138568Ssam}; 569138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 570170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 571170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 572170530Ssam { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 573170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 574170530Ssam { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 575170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 576170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 577170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 578170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 579170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 580138568Ssam}; 581138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 582170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 583170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 584170530Ssam { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ 585170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 586170530Ssam { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ 587170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 588170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 589170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 590170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 591170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 592138568Ssam}; 593138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 594170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 595170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 596170530Ssam { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ 597170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 598170530Ssam { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ 599170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 600170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 601170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 602170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 603170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 604138568Ssam}; 605138568Ssam 606138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 607170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 608170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 609170530Ssam { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 610170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 611170530Ssam { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 612170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 613170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 614170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 615170530Ssam { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 616170530Ssam { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 617138568Ssam}; 618138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 619170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 620170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 621170530Ssam { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ 622170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 623170530Ssam { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ 624170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 625170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 626170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 627170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 628170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 629138568Ssam}; 630138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 631170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 632170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 633170530Ssam { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ 634170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 635170530Ssam { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ 636170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 637170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 638170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 639170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 640170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 641138568Ssam}; 642138568Ssam 643138568Ssamvoid 644138568Ssamieee80211_wme_initparams(struct ieee80211com *ic) 645138568Ssam{ 646138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 647138568Ssam const paramType *pPhyParam, *pBssPhyParam; 648138568Ssam struct wmeParams *wmep; 649170530Ssam enum ieee80211_phymode mode; 650138568Ssam int i; 651138568Ssam 652138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 653138568Ssam return; 654138568Ssam 655170530Ssam /* 656170530Ssam * Select mode; we can be called early in which case we 657170530Ssam * always use auto mode. We know we'll be called when 658170530Ssam * entering the RUN state with bsschan setup properly 659170530Ssam * so state will eventually get set correctly 660170530Ssam */ 661170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 662170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 663170530Ssam else 664170530Ssam mode = IEEE80211_MODE_AUTO; 665138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 666138568Ssam switch (i) { 667138568Ssam case WME_AC_BK: 668170530Ssam pPhyParam = &phyParamForAC_BK[mode]; 669170530Ssam pBssPhyParam = &phyParamForAC_BK[mode]; 670138568Ssam break; 671138568Ssam case WME_AC_VI: 672170530Ssam pPhyParam = &phyParamForAC_VI[mode]; 673170530Ssam pBssPhyParam = &bssPhyParamForAC_VI[mode]; 674138568Ssam break; 675138568Ssam case WME_AC_VO: 676170530Ssam pPhyParam = &phyParamForAC_VO[mode]; 677170530Ssam pBssPhyParam = &bssPhyParamForAC_VO[mode]; 678138568Ssam break; 679138568Ssam case WME_AC_BE: 680138568Ssam default: 681170530Ssam pPhyParam = &phyParamForAC_BE[mode]; 682170530Ssam pBssPhyParam = &bssPhyParamForAC_BE[mode]; 683138568Ssam break; 684138568Ssam } 685138568Ssam 686138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 687138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 688138568Ssam wmep->wmep_acm = pPhyParam->acm; 689138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 690138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 691138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 692138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 693138568Ssam } else { 694138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 695138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 696138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 697138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 698138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 699138568Ssam 700138568Ssam } 701138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 702138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 703138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 704138568Ssam , ieee80211_wme_acnames[i] 705138568Ssam , wmep->wmep_acm 706138568Ssam , wmep->wmep_aifsn 707138568Ssam , wmep->wmep_logcwmin 708138568Ssam , wmep->wmep_logcwmax 709138568Ssam , wmep->wmep_txopLimit 710138568Ssam ); 711138568Ssam 712138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 713138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 714138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 715138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 716138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 717138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 718138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 719138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 720138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 721138568Ssam , ieee80211_wme_acnames[i] 722138568Ssam , wmep->wmep_acm 723138568Ssam , wmep->wmep_aifsn 724138568Ssam , wmep->wmep_logcwmin 725138568Ssam , wmep->wmep_logcwmax 726138568Ssam , wmep->wmep_txopLimit 727138568Ssam ); 728138568Ssam } 729138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 730138568Ssam if (ic->ic_bss != NULL) { 731138568Ssam /* 732138568Ssam * Calculate agressive mode switching threshold based 733138568Ssam * on beacon interval. This doesn't need locking since 734138568Ssam * we're only called before entering the RUN state at 735138568Ssam * which point we start sending beacon frames. 736138568Ssam */ 737138568Ssam wme->wme_hipri_switch_thresh = 738138568Ssam (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 739138568Ssam ieee80211_wme_updateparams(ic); 740138568Ssam } 741138568Ssam} 742138568Ssam 743138568Ssam/* 744138568Ssam * Update WME parameters for ourself and the BSS. 745138568Ssam */ 746138568Ssamvoid 747138568Ssamieee80211_wme_updateparams_locked(struct ieee80211com *ic) 748138568Ssam{ 749138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 750170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ 751170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */ 752170530Ssam { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */ 753170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */ 754170530Ssam { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */ 755170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */ 756170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */ 757170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */ 758170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ 759170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ 760138568Ssam }; 761138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 762138568Ssam const struct wmeParams *wmep; 763138568Ssam struct wmeParams *chanp, *bssp; 764170530Ssam enum ieee80211_phymode mode; 765138568Ssam int i; 766138568Ssam 767138568Ssam /* set up the channel access parameters for the physical device */ 768138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 769138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 770138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 771138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 772138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 773138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 774138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 775138568Ssam 776138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 777138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 778138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 779138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 780138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 781138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 782138568Ssam } 783138568Ssam 784138568Ssam /* 785170530Ssam * Select mode; we can be called early in which case we 786170530Ssam * always use auto mode. We know we'll be called when 787170530Ssam * entering the RUN state with bsschan setup properly 788170530Ssam * so state will eventually get set correctly 789170530Ssam */ 790170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 791170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 792170530Ssam else 793170530Ssam mode = IEEE80211_MODE_AUTO; 794170530Ssam 795170530Ssam /* 796138568Ssam * This implements agressive mode as found in certain 797138568Ssam * vendors' AP's. When there is significant high 798138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 799138568Ssam * traffic by using conservative parameters. Otherwise 800138568Ssam * BE uses agressive params to optimize performance of 801138568Ssam * legacy/non-QoS traffic. 802138568Ssam */ 803138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 804156524Ssam (wme->wme_flags & WME_F_AGGRMODE) != 0) || 805153974Ssam (ic->ic_opmode == IEEE80211_M_STA && 806138568Ssam (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 807138568Ssam (ic->ic_flags & IEEE80211_F_WME) == 0) { 808138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 809138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 810138568Ssam 811170530Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; 812138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 813170530Ssam phyParam[mode].logcwmin; 814138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 815170530Ssam phyParam[mode].logcwmax; 816138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 817153421Ssam (ic->ic_flags & IEEE80211_F_BURST) ? 818170530Ssam phyParam[mode].txopLimit : 0; 819138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 820138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 821138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 822138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 823138568Ssam , chanp->wmep_acm 824138568Ssam , chanp->wmep_aifsn 825138568Ssam , chanp->wmep_logcwmin 826138568Ssam , chanp->wmep_logcwmax 827138568Ssam , chanp->wmep_txopLimit 828138568Ssam ); 829138568Ssam } 830138568Ssam 831138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 832156524Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 833170530Ssam static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 834138568Ssam 3, /* IEEE80211_MODE_AUTO */ 835138568Ssam 3, /* IEEE80211_MODE_11A */ 836138568Ssam 4, /* IEEE80211_MODE_11B */ 837138568Ssam 3, /* IEEE80211_MODE_11G */ 838138568Ssam 4, /* IEEE80211_MODE_FH */ 839138568Ssam 3, /* IEEE80211_MODE_TURBO_A */ 840138568Ssam 3, /* IEEE80211_MODE_TURBO_G */ 841170530Ssam 3, /* IEEE80211_MODE_STURBO_A */ 842170530Ssam 3, /* IEEE80211_MODE_11NA */ 843170530Ssam 3, /* IEEE80211_MODE_11NG */ 844138568Ssam }; 845138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 846138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 847138568Ssam 848170530Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; 849138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 850138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 851138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 852138568Ssam , chanp->wmep_logcwmin 853138568Ssam ); 854138568Ssam } 855138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 856138568Ssam /* 857138568Ssam * Arrange for a beacon update and bump the parameter 858138568Ssam * set number so associated stations load the new values. 859138568Ssam */ 860138568Ssam wme->wme_bssChanParams.cap_info = 861138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 862138568Ssam ic->ic_flags |= IEEE80211_F_WMEUPDATE; 863138568Ssam } 864138568Ssam 865138568Ssam wme->wme_update(ic); 866138568Ssam 867138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 868138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 869138568Ssam ic->ic_opmode == IEEE80211_M_STA ? 870138568Ssam wme->wme_wmeChanParams.cap_info : 871138568Ssam wme->wme_bssChanParams.cap_info); 872138568Ssam} 873138568Ssam 874138568Ssamvoid 875138568Ssamieee80211_wme_updateparams(struct ieee80211com *ic) 876138568Ssam{ 877138568Ssam 878138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 879138568Ssam IEEE80211_BEACON_LOCK(ic); 880138568Ssam ieee80211_wme_updateparams_locked(ic); 881138568Ssam IEEE80211_BEACON_UNLOCK(ic); 882138568Ssam } 883138568Ssam} 884138568Ssam 885170530Ssam/* 886170530Ssam * Start a device. If this is the first vap running on the 887170530Ssam * underlying device then we first bring it up. 888170530Ssam */ 889170530Ssamint 890170530Ssamieee80211_init(struct ieee80211com *ic, int forcescan) 891170530Ssam{ 892170530Ssam 893170530Ssam IEEE80211_DPRINTF(ic, 894170530Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 895170530Ssam "%s\n", "start running"); 896170530Ssam 897170530Ssam /* 898170530Ssam * Kick the 802.11 state machine as appropriate. 899170530Ssam */ 900170530Ssam if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { 901170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 902170530Ssam /* 903170530Ssam * Try to be intelligent about clocking the state 904170530Ssam * machine. If we're currently in RUN state then 905170530Ssam * we should be able to apply any new state/parameters 906170530Ssam * simply by re-associating. Otherwise we need to 907170530Ssam * re-scan to select an appropriate ap. 908170530Ssam */ 909170530Ssam if (ic->ic_state != IEEE80211_S_RUN || forcescan) 910170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 911170530Ssam else 912170530Ssam ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); 913170530Ssam } else { 914170530Ssam /* 915170530Ssam * For monitor+wds modes there's nothing to do but 916170530Ssam * start running. Otherwise, if this is the first 917170530Ssam * vap to be brought up, start a scan which may be 918170530Ssam * preempted if the station is locked to a particular 919170530Ssam * channel. 920170530Ssam */ 921170530Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR || 922170530Ssam ic->ic_opmode == IEEE80211_M_WDS) { 923170530Ssam ic->ic_state = IEEE80211_S_INIT; /* XXX*/ 924170530Ssam ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 925170530Ssam } else 926170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 927170530Ssam } 928170530Ssam } 929170530Ssam return 0; 930170530Ssam} 931170530Ssam 932170530Ssam/* 933170530Ssam * Switch between turbo and non-turbo operating modes. 934170530Ssam * Use the specified channel flags to locate the new 935170530Ssam * channel, update 802.11 state, and then call back into 936170530Ssam * the driver to effect the change. 937170530Ssam */ 938153349Ssamvoid 939170530Ssamieee80211_dturbo_switch(struct ieee80211com *ic, int newflags) 940170530Ssam{ 941170530Ssam struct ieee80211_channel *chan; 942170530Ssam 943170530Ssam chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); 944170530Ssam if (chan == NULL) { /* XXX should not happen */ 945170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 946170530Ssam "%s: no channel with freq %u flags 0x%x\n", 947170530Ssam __func__, ic->ic_bsschan->ic_freq, newflags); 948170530Ssam return; 949170530Ssam } 950170530Ssam 951170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 952170530Ssam "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, 953170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], 954170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(chan)], 955170530Ssam chan->ic_freq, chan->ic_flags); 956170530Ssam 957170530Ssam ic->ic_bsschan = chan; 958170530Ssam ic->ic_prevchan = ic->ic_curchan; 959170530Ssam ic->ic_curchan = chan; 960170530Ssam ic->ic_set_channel(ic); 961170530Ssam /* NB: do not need to reset ERP state 'cuz we're in sta mode */ 962170530Ssam} 963170530Ssam 964170530Ssamvoid 965153349Ssamieee80211_beacon_miss(struct ieee80211com *ic) 966153349Ssam{ 967153349Ssam 968153349Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 969153349Ssam /* XXX check ic_curchan != ic_bsschan? */ 970153349Ssam return; 971153349Ssam } 972170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 973153349Ssam "%s\n", "beacon miss"); 974153349Ssam 975153349Ssam /* 976153349Ssam * Our handling is only meaningful for stations that are 977153349Ssam * associated; any other conditions else will be handled 978153349Ssam * through different means (e.g. the tx timeout on mgt frames). 979153349Ssam */ 980153349Ssam if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) 981153349Ssam return; 982153349Ssam 983153349Ssam if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { 984153349Ssam /* 985153349Ssam * Send a directed probe req before falling back to a scan; 986153349Ssam * if we receive a response ic_bmiss_count will be reset. 987153349Ssam * Some cards mistakenly report beacon miss so this avoids 988153349Ssam * the expensive scan if the ap is still there. 989153349Ssam */ 990153349Ssam ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, 991153349Ssam ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, 992153349Ssam ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, 993153349Ssam ic->ic_opt_ie, ic->ic_opt_ie_len); 994153349Ssam return; 995153349Ssam } 996153349Ssam ic->ic_bmiss_count = 0; 997170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 998170530Ssam /* 999170530Ssam * If we receive a beacon miss interrupt when using 1000170530Ssam * dynamic turbo, attempt to switch modes before 1001170530Ssam * reassociating. 1002170530Ssam */ 1003170530Ssam if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP)) 1004170530Ssam ieee80211_dturbo_switch(ic, 1005170530Ssam ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); 1006170530Ssam /* 1007170530Ssam * Try to reassociate before scanning for a new ap. 1008170530Ssam */ 1009170530Ssam ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); 1010170530Ssam } else { 1011170530Ssam /* 1012170530Ssam * Somebody else is controlling state changes (e.g. 1013170530Ssam * a user-mode app) don't do anything that would 1014170530Ssam * confuse them; just drop into scan mode so they'll 1015170530Ssam * notified of the state change and given control. 1016170530Ssam */ 1017170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 1018170530Ssam } 1019153349Ssam} 1020153349Ssam 1021154736Ssam/* 1022154736Ssam * Software beacon miss handling. Check if any beacons 1023154736Ssam * were received in the last period. If not post a 1024154736Ssam * beacon miss; otherwise reset the counter. 1025154736Ssam */ 1026147765Ssamstatic void 1027154736Ssamieee80211_swbmiss(void *arg) 1028154736Ssam{ 1029154736Ssam struct ieee80211com *ic = arg; 1030154736Ssam 1031154736Ssam if (ic->ic_swbmiss_count == 0) { 1032154736Ssam ieee80211_beacon_miss(ic); 1033154736Ssam if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ 1034154736Ssam return; 1035154736Ssam } else 1036154736Ssam ic->ic_swbmiss_count = 0; 1037154736Ssam callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1038154736Ssam ieee80211_swbmiss, ic); 1039154736Ssam} 1040154736Ssam 1041154736Ssamstatic void 1042147765Ssamsta_disassoc(void *arg, struct ieee80211_node *ni) 1043147765Ssam{ 1044147765Ssam struct ieee80211com *ic = arg; 1045147765Ssam 1046147765Ssam if (ni->ni_associd != 0) { 1047147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 1048147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 1049147765Ssam ieee80211_node_leave(ic, ni); 1050147765Ssam } 1051147765Ssam} 1052147765Ssam 1053147765Ssamstatic void 1054147765Ssamsta_deauth(void *arg, struct ieee80211_node *ni) 1055147765Ssam{ 1056147765Ssam struct ieee80211com *ic = arg; 1057147765Ssam 1058147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 1059147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 1060147765Ssam} 1061147765Ssam 1062170530Ssam/* 1063170530Ssam * Handle deauth with reason. We retry only for 1064170530Ssam * the cases where we might succeed. Otherwise 1065170530Ssam * we downgrade the ap and scan. 1066170530Ssam */ 1067170530Ssamstatic void 1068170530Ssamsta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason) 1069170530Ssam{ 1070170530Ssam switch (reason) { 1071170817Ssam case IEEE80211_STATUS_SUCCESS: 1072170530Ssam case IEEE80211_STATUS_TIMEOUT: 1073170530Ssam case IEEE80211_REASON_ASSOC_EXPIRE: 1074170530Ssam case IEEE80211_REASON_NOT_AUTHED: 1075170530Ssam case IEEE80211_REASON_NOT_ASSOCED: 1076170530Ssam case IEEE80211_REASON_ASSOC_LEAVE: 1077170530Ssam case IEEE80211_REASON_ASSOC_NOT_AUTHED: 1078170530Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); 1079170530Ssam break; 1080170530Ssam default: 1081170530Ssam ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason); 1082170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1083170530Ssam ieee80211_check_scan(ic, 1084170530Ssam IEEE80211_SCAN_ACTIVE, 1085170530Ssam IEEE80211_SCAN_FOREVER, 1086170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 1087170530Ssam break; 1088170530Ssam } 1089170530Ssam} 1090170530Ssam 1091117811Ssamstatic int 1092138568Ssamieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 1093116742Ssam{ 1094138568Ssam struct ifnet *ifp = ic->ic_ifp; 1095116742Ssam struct ieee80211_node *ni; 1096117811Ssam enum ieee80211_state ostate; 1097116742Ssam 1098116742Ssam ostate = ic->ic_state; 1099138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 1100138568Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 1101117811Ssam ic->ic_state = nstate; /* state transition */ 1102170530Ssam callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */ 1103170530Ssam if (ostate != IEEE80211_S_SCAN) 1104170530Ssam ieee80211_cancel_scan(ic); /* background scan */ 1105116742Ssam ni = ic->ic_bss; /* NB: no reference held */ 1106154736Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) 1107154736Ssam callout_stop(&ic->ic_swbmiss); 1108116742Ssam switch (nstate) { 1109116742Ssam case IEEE80211_S_INIT: 1110116742Ssam switch (ostate) { 1111116742Ssam case IEEE80211_S_INIT: 1112116742Ssam break; 1113116742Ssam case IEEE80211_S_RUN: 1114116742Ssam switch (ic->ic_opmode) { 1115116742Ssam case IEEE80211_M_STA: 1116116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1117116742Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 1118116742Ssam IEEE80211_REASON_ASSOC_LEAVE); 1119138568Ssam ieee80211_sta_leave(ic, ni); 1120116742Ssam break; 1121116742Ssam case IEEE80211_M_HOSTAP: 1122147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1123147765Ssam sta_disassoc, ic); 1124116742Ssam break; 1125116742Ssam default: 1126116742Ssam break; 1127116742Ssam } 1128165894Ssam break; 1129116742Ssam case IEEE80211_S_ASSOC: 1130116742Ssam switch (ic->ic_opmode) { 1131116742Ssam case IEEE80211_M_STA: 1132116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1133116742Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 1134116742Ssam IEEE80211_REASON_AUTH_LEAVE); 1135116742Ssam break; 1136116742Ssam case IEEE80211_M_HOSTAP: 1137147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1138147765Ssam sta_deauth, ic); 1139116742Ssam break; 1140116742Ssam default: 1141116742Ssam break; 1142116742Ssam } 1143165894Ssam break; 1144140441Ssam case IEEE80211_S_SCAN: 1145140441Ssam ieee80211_cancel_scan(ic); 1146165894Ssam break; 1147116742Ssam case IEEE80211_S_AUTH: 1148165894Ssam break; 1149172058Ssam default: 1150172058Ssam break; 1151165894Ssam } 1152165894Ssam if (ostate != IEEE80211_S_INIT) { 1153165894Ssam /* NB: optimize INIT -> INIT case */ 1154165894Ssam ieee80211_drain_ifq(&ic->ic_mgtq); 1155138568Ssam ieee80211_reset_bss(ic); 1156170530Ssam ieee80211_scan_flush(ic); 1157116742Ssam } 1158138568Ssam if (ic->ic_auth->ia_detach != NULL) 1159138568Ssam ic->ic_auth->ia_detach(ic); 1160116742Ssam break; 1161116742Ssam case IEEE80211_S_SCAN: 1162116742Ssam switch (ostate) { 1163116742Ssam case IEEE80211_S_INIT: 1164170530Ssam createibss: 1165138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 1166138568Ssam ic->ic_opmode == IEEE80211_M_IBSS || 1167138568Ssam ic->ic_opmode == IEEE80211_M_AHDEMO) && 1168116742Ssam ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 1169116742Ssam /* 1170170530Ssam * Already have a channel; bypass the 1171170530Ssam * scan and startup immediately. Because 1172170530Ssam * of this explicitly sync the scanner state. 1173116742Ssam */ 1174170530Ssam ieee80211_scan_update(ic); 1175116742Ssam ieee80211_create_ibss(ic, ic->ic_des_chan); 1176116742Ssam } else { 1177170530Ssam ieee80211_check_scan(ic, 1178170530Ssam IEEE80211_SCAN_ACTIVE | 1179170530Ssam IEEE80211_SCAN_FLUSH, 1180170530Ssam IEEE80211_SCAN_FOREVER, 1181170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 1182116742Ssam } 1183116742Ssam break; 1184116742Ssam case IEEE80211_S_SCAN: 1185170530Ssam case IEEE80211_S_AUTH: 1186170530Ssam case IEEE80211_S_ASSOC: 1187138568Ssam /* 1188170530Ssam * These can happen either because of a timeout 1189170530Ssam * on an assoc/auth response or because of a 1190170530Ssam * change in state that requires a reset. For 1191170530Ssam * the former we're called with a non-zero arg 1192170530Ssam * that is the cause for the failure; pass this 1193170530Ssam * to the scan code so it can update state. 1194170530Ssam * Otherwise trigger a new scan unless we're in 1195170530Ssam * manual roaming mode in which case an application 1196170530Ssam * must issue an explicit scan request. 1197138568Ssam */ 1198170530Ssam if (arg != 0) 1199170530Ssam ieee80211_scan_assoc_fail(ic, 1200170530Ssam ic->ic_bss->ni_macaddr, arg); 1201170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1202170530Ssam ieee80211_check_scan(ic, 1203170530Ssam IEEE80211_SCAN_ACTIVE, 1204170530Ssam IEEE80211_SCAN_FOREVER, 1205170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 1206116742Ssam break; 1207170530Ssam case IEEE80211_S_RUN: /* beacon miss */ 1208170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1209170530Ssam ieee80211_sta_leave(ic, ni); 1210170530Ssam ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 1211170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1212170530Ssam ieee80211_check_scan(ic, 1213170530Ssam IEEE80211_SCAN_ACTIVE, 1214170530Ssam IEEE80211_SCAN_FOREVER, 1215170530Ssam ic->ic_des_nssid, 1216170530Ssam ic->ic_des_ssid); 1217170530Ssam } else { 1218170530Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1219170530Ssam sta_disassoc, ic); 1220170530Ssam goto createibss; 1221116742Ssam } 1222116742Ssam break; 1223172058Ssam default: 1224172058Ssam break; 1225116742Ssam } 1226116742Ssam break; 1227116742Ssam case IEEE80211_S_AUTH: 1228170530Ssam KASSERT(ic->ic_opmode == IEEE80211_M_STA, 1229170530Ssam ("switch to %s state when operating in mode %u", 1230170530Ssam ieee80211_state_name[nstate], ic->ic_opmode)); 1231116742Ssam switch (ostate) { 1232116742Ssam case IEEE80211_S_INIT: 1233116742Ssam case IEEE80211_S_SCAN: 1234116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1235116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 1236116742Ssam break; 1237116742Ssam case IEEE80211_S_AUTH: 1238116742Ssam case IEEE80211_S_ASSOC: 1239170530Ssam switch (arg & 0xff) { 1240116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1241116742Ssam /* ??? */ 1242116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1243116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 1244116742Ssam break; 1245116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1246170530Ssam sta_authretry(ic, ni, arg>>8); 1247116742Ssam break; 1248116742Ssam } 1249116742Ssam break; 1250116742Ssam case IEEE80211_S_RUN: 1251170530Ssam switch (arg & 0xff) { 1252116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1253116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1254116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 1255116742Ssam ic->ic_state = ostate; /* stay RUN */ 1256116742Ssam break; 1257116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1258138568Ssam ieee80211_sta_leave(ic, ni); 1259147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1260147116Ssam /* try to reauth */ 1261147116Ssam IEEE80211_SEND_MGMT(ic, ni, 1262147116Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 1263147116Ssam } 1264116742Ssam break; 1265116742Ssam } 1266116742Ssam break; 1267172058Ssam default: 1268172058Ssam break; 1269116742Ssam } 1270116742Ssam break; 1271116742Ssam case IEEE80211_S_ASSOC: 1272170530Ssam KASSERT(ic->ic_opmode == IEEE80211_M_STA, 1273170530Ssam ("switch to %s state when operating in mode %u", 1274170530Ssam ieee80211_state_name[nstate], ic->ic_opmode)); 1275116742Ssam switch (ostate) { 1276116742Ssam case IEEE80211_S_INIT: 1277116742Ssam case IEEE80211_S_SCAN: 1278138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1279138568Ssam "%s: invalid transition\n", __func__); 1280116742Ssam break; 1281116742Ssam case IEEE80211_S_AUTH: 1282170530Ssam case IEEE80211_S_ASSOC: 1283116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1284116742Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1285116742Ssam break; 1286116742Ssam case IEEE80211_S_RUN: 1287138568Ssam ieee80211_sta_leave(ic, ni); 1288147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1289170530Ssam IEEE80211_SEND_MGMT(ic, ni, arg ? 1290170530Ssam IEEE80211_FC0_SUBTYPE_REASSOC_REQ : 1291170530Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1292147116Ssam } 1293116742Ssam break; 1294172058Ssam default: 1295172058Ssam break; 1296116742Ssam } 1297116742Ssam break; 1298116742Ssam case IEEE80211_S_RUN: 1299138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) { 1300138568Ssam /* XXX validate prerequisites */ 1301138568Ssam } 1302116742Ssam switch (ostate) { 1303116742Ssam case IEEE80211_S_INIT: 1304170530Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR || 1305170530Ssam ic->ic_opmode == IEEE80211_M_WDS || 1306170530Ssam ic->ic_opmode == IEEE80211_M_HOSTAP) { 1307170530Ssam /* 1308170530Ssam * Already have a channel; bypass the 1309170530Ssam * scan and startup immediately. Because 1310170530Ssam * of this explicitly sync the scanner state. 1311170530Ssam */ 1312170530Ssam ieee80211_scan_update(ic); 1313170530Ssam ieee80211_create_ibss(ic, ic->ic_curchan); 1314138568Ssam break; 1315170530Ssam } 1316138568Ssam /* fall thru... */ 1317116742Ssam case IEEE80211_S_AUTH: 1318138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1319138568Ssam "%s: invalid transition\n", __func__); 1320140763Ssam /* fall thru... */ 1321140763Ssam case IEEE80211_S_RUN: 1322116742Ssam break; 1323116742Ssam case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 1324116742Ssam case IEEE80211_S_ASSOC: /* infra mode */ 1325116742Ssam KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 1326116742Ssam ("%s: bogus xmit rate %u setup\n", __func__, 1327116742Ssam ni->ni_txrate)); 1328138568Ssam#ifdef IEEE80211_DEBUG 1329138568Ssam if (ieee80211_msg_debug(ic)) { 1330116742Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1331138568Ssam if_printf(ifp, "associated "); 1332116742Ssam else 1333138568Ssam if_printf(ifp, "synchronized "); 1334116742Ssam printf("with %s ssid ", 1335116742Ssam ether_sprintf(ni->ni_bssid)); 1336116742Ssam ieee80211_print_essid(ic->ic_bss->ni_essid, 1337116742Ssam ni->ni_esslen); 1338116742Ssam printf(" channel %d start %uMb\n", 1339148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 1340116742Ssam IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 1341116742Ssam } 1342138568Ssam#endif 1343170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1344170530Ssam ieee80211_scan_assoc_success(ic, 1345170530Ssam ni->ni_macaddr); 1346138568Ssam ieee80211_notify_node_join(ic, ni, 1347138568Ssam arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 1348170530Ssam } 1349138568Ssam if_start(ifp); /* XXX not authorized yet */ 1350116742Ssam break; 1351172058Ssam default: 1352172058Ssam break; 1353116742Ssam } 1354154736Ssam if (ostate != IEEE80211_S_RUN && 1355154736Ssam ic->ic_opmode == IEEE80211_M_STA && 1356154736Ssam (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { 1357154736Ssam /* 1358154736Ssam * Start s/w beacon miss timer for devices w/o 1359154736Ssam * hardware support. We fudge a bit here since 1360154736Ssam * we're doing this in software. 1361154736Ssam */ 1362154736Ssam ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( 1363154736Ssam 2 * ic->ic_bmissthreshold * ni->ni_intval); 1364154736Ssam ic->ic_swbmiss_count = 0; 1365154736Ssam callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1366154736Ssam ieee80211_swbmiss, ic); 1367154736Ssam } 1368138568Ssam /* 1369138568Ssam * Start/stop the authenticator when operating as an 1370138568Ssam * AP. We delay until here to allow configuration to 1371138568Ssam * happen out of order. 1372138568Ssam */ 1373138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 1374138568Ssam ic->ic_auth->ia_attach != NULL) { 1375138568Ssam /* XXX check failure */ 1376138568Ssam ic->ic_auth->ia_attach(ic); 1377138568Ssam } else if (ic->ic_auth->ia_detach != NULL) { 1378138568Ssam ic->ic_auth->ia_detach(ic); 1379138568Ssam } 1380138568Ssam /* 1381138568Ssam * When 802.1x is not in use mark the port authorized 1382138568Ssam * at this point so traffic can flow. 1383138568Ssam */ 1384138568Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1385148302Ssam ieee80211_node_authorize(ni); 1386138568Ssam /* 1387138568Ssam * Enable inactivity processing. 1388138568Ssam * XXX 1389138568Ssam */ 1390170530Ssam callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, 1391170530Ssam ieee80211_node_timeout, ic); 1392116742Ssam break; 1393172058Ssam default: 1394172058Ssam break; 1395116742Ssam } 1396116742Ssam return 0; 1397116742Ssam} 1398