ieee80211_proto.c revision 170530
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 170530 2007-06-11 03:36:55Z 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 */ 81117811Ssam "RUN" /* IEEE80211_S_RUN */ 82117811Ssam}; 83138568Ssamconst char *ieee80211_wme_acnames[] = { 84138568Ssam "WME_AC_BE", 85138568Ssam "WME_AC_BK", 86138568Ssam "WME_AC_VI", 87138568Ssam "WME_AC_VO", 88138568Ssam "WME_UPSD", 89138568Ssam}; 90116742Ssam 91117811Ssamstatic int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 92117811Ssam 93116742Ssamvoid 94138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 95116742Ssam{ 96138568Ssam struct ifnet *ifp = ic->ic_ifp; 97116742Ssam 98138568Ssam /* XXX room for crypto */ 99138568Ssam ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 100116742Ssam 101116742Ssam ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 102148291Ssam ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 103148290Ssam ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 104153349Ssam ic->ic_bmiss_max = IEEE80211_BMISS_MAX; 105154736Ssam callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); 106170530Ssam callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE); 107153346Ssam ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; 108127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 109138568Ssam ic->ic_roaming = IEEE80211_ROAMING_AUTO; 110116742Ssam 111138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 112138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 113138568Ssam 114121816Sbrooks mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 115116742Ssam 116117811Ssam /* protocol state change handler */ 117117811Ssam ic->ic_newstate = ieee80211_newstate; 118117811Ssam 119116742Ssam /* initialize management frame handlers */ 120116742Ssam ic->ic_recv_mgmt = ieee80211_recv_mgmt; 121116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 122160690Ssam ic->ic_raw_xmit = ieee80211_raw_xmit; 123116742Ssam} 124116742Ssam 125116742Ssamvoid 126138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 127116742Ssam{ 128116742Ssam 129138568Ssam /* 130138568Ssam * This should not be needed as we detach when reseting 131138568Ssam * the state but be conservative here since the 132138568Ssam * authenticator may do things like spawn kernel threads. 133138568Ssam */ 134138568Ssam if (ic->ic_auth->ia_detach) 135138568Ssam ic->ic_auth->ia_detach(ic); 136138568Ssam 137165894Ssam ieee80211_drain_ifq(&ic->ic_mgtq); 138116742Ssam mtx_destroy(&ic->ic_mgtq.ifq_mtx); 139138568Ssam 140138568Ssam /* 141138568Ssam * Detach any ACL'ator. 142138568Ssam */ 143138568Ssam if (ic->ic_acl != NULL) 144138568Ssam ic->ic_acl->iac_detach(ic); 145116742Ssam} 146116742Ssam 147138568Ssam/* 148138568Ssam * Simple-minded authenticator module support. 149138568Ssam */ 150138568Ssam 151138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 152138568Ssam/* XXX well-known names */ 153138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 154138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 155138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 156138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 157138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 158138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 159138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 160138568Ssam}; 161138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 162138568Ssam 163138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 164138568Ssam .ia_name = "wlan_internal", 165138568Ssam .ia_attach = NULL, 166138568Ssam .ia_detach = NULL, 167138568Ssam .ia_node_join = NULL, 168138568Ssam .ia_node_leave = NULL, 169138568Ssam}; 170138568Ssam 171138568Ssam/* 172138568Ssam * Setup internal authenticators once; they are never unregistered. 173138568Ssam */ 174138568Ssamstatic void 175138568Ssamieee80211_auth_setup(void) 176138568Ssam{ 177138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 178138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 179138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 180138568Ssam} 181138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 182138568Ssam 183138568Ssamconst struct ieee80211_authenticator * 184138568Ssamieee80211_authenticator_get(int auth) 185138568Ssam{ 186138568Ssam if (auth >= IEEE80211_AUTH_MAX) 187138568Ssam return NULL; 188138568Ssam if (authenticators[auth] == NULL) 189138568Ssam ieee80211_load_module(auth_modnames[auth]); 190138568Ssam return authenticators[auth]; 191138568Ssam} 192138568Ssam 193116742Ssamvoid 194138568Ssamieee80211_authenticator_register(int type, 195138568Ssam const struct ieee80211_authenticator *auth) 196116742Ssam{ 197138568Ssam if (type >= IEEE80211_AUTH_MAX) 198138568Ssam return; 199138568Ssam authenticators[type] = auth; 200138568Ssam} 201138568Ssam 202138568Ssamvoid 203138568Ssamieee80211_authenticator_unregister(int type) 204138568Ssam{ 205138568Ssam 206138568Ssam if (type >= IEEE80211_AUTH_MAX) 207138568Ssam return; 208138568Ssam authenticators[type] = NULL; 209138568Ssam} 210138568Ssam 211138568Ssam/* 212138568Ssam * Very simple-minded ACL module support. 213138568Ssam */ 214138568Ssam/* XXX just one for now */ 215138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 216138568Ssam 217138568Ssamvoid 218138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 219138568Ssam{ 220138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 221138568Ssam acl = iac; 222138568Ssam} 223138568Ssam 224138568Ssamvoid 225138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 226138568Ssam{ 227138568Ssam if (acl == iac) 228138568Ssam acl = NULL; 229138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 230138568Ssam} 231138568Ssam 232138568Ssamconst struct ieee80211_aclator * 233138568Ssamieee80211_aclator_get(const char *name) 234138568Ssam{ 235138568Ssam if (acl == NULL) 236138568Ssam ieee80211_load_module("wlan_acl"); 237138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 238138568Ssam} 239138568Ssam 240138568Ssamvoid 241170530Ssamieee80211_print_essid(const uint8_t *essid, int len) 242138568Ssam{ 243170530Ssam const uint8_t *p; 244116742Ssam int i; 245116742Ssam 246116742Ssam if (len > IEEE80211_NWID_LEN) 247116742Ssam len = IEEE80211_NWID_LEN; 248116742Ssam /* determine printable or not */ 249116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 250116742Ssam if (*p < ' ' || *p > 0x7e) 251116742Ssam break; 252116742Ssam } 253116742Ssam if (i == len) { 254116742Ssam printf("\""); 255116742Ssam for (i = 0, p = essid; i < len; i++, p++) 256116742Ssam printf("%c", *p); 257116742Ssam printf("\""); 258116742Ssam } else { 259116742Ssam printf("0x"); 260116742Ssam for (i = 0, p = essid; i < len; i++, p++) 261116742Ssam printf("%02x", *p); 262116742Ssam } 263116742Ssam} 264116742Ssam 265116742Ssamvoid 266170530Ssamieee80211_dump_pkt(struct ieee80211com *ic, 267170530Ssam const uint8_t *buf, int len, int rate, int rssi) 268116742Ssam{ 269138568Ssam const struct ieee80211_frame *wh; 270116742Ssam int i; 271116742Ssam 272138568Ssam wh = (const struct ieee80211_frame *)buf; 273116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 274116742Ssam case IEEE80211_FC1_DIR_NODS: 275116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 276116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 277116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 278116742Ssam break; 279116742Ssam case IEEE80211_FC1_DIR_TODS: 280116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 281116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 282116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 283116742Ssam break; 284116742Ssam case IEEE80211_FC1_DIR_FROMDS: 285116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 286116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 287116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 288116742Ssam break; 289116742Ssam case IEEE80211_FC1_DIR_DSTODS: 290170530Ssam printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); 291116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 292116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 293116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 294116742Ssam break; 295116742Ssam } 296116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 297116742Ssam case IEEE80211_FC0_TYPE_DATA: 298116742Ssam printf(" data"); 299116742Ssam break; 300116742Ssam case IEEE80211_FC0_TYPE_MGT: 301116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 302116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 303116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 304116742Ssam break; 305116742Ssam default: 306116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 307116742Ssam break; 308116742Ssam } 309170530Ssam if (IEEE80211_QOS_HAS_SEQ(wh)) { 310170530Ssam const struct ieee80211_qosframe *qwh = 311170530Ssam (const struct ieee80211_qosframe *)buf; 312170530Ssam printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, 313170530Ssam qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); 314170530Ssam } 315138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 316170530Ssam int off; 317170530Ssam 318170530Ssam off = ieee80211_anyhdrspace(ic, wh); 319170530Ssam printf(" WEP [IV %.02x %.02x %.02x", 320170530Ssam buf[off+0], buf[off+1], buf[off+2]); 321170530Ssam if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) 322170530Ssam printf(" %.02x %.02x %.02x", 323170530Ssam buf[off+4], buf[off+5], buf[off+6]); 324170530Ssam printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); 325138568Ssam } 326116742Ssam if (rate >= 0) 327116742Ssam printf(" %dM", rate / 2); 328116742Ssam if (rssi >= 0) 329116742Ssam printf(" +%d", rssi); 330116742Ssam printf("\n"); 331116742Ssam if (len > 0) { 332116742Ssam for (i = 0; i < len; i++) { 333116742Ssam if ((i & 1) == 0) 334116742Ssam printf(" "); 335116742Ssam printf("%02x", buf[i]); 336116742Ssam } 337116742Ssam printf("\n"); 338116742Ssam } 339116742Ssam} 340116742Ssam 341165887Ssamstatic __inline int 342165887Ssamfindrix(const struct ieee80211_rateset *rs, int r) 343165887Ssam{ 344165887Ssam int i; 345165887Ssam 346165887Ssam for (i = 0; i < rs->rs_nrates; i++) 347165887Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 348165887Ssam return i; 349165887Ssam return -1; 350165887Ssam} 351165887Ssam 352116742Ssamint 353167442Ssamieee80211_fix_rate(struct ieee80211_node *ni, 354167442Ssam struct ieee80211_rateset *nrs, int flags) 355116742Ssam{ 356116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 357148299Ssam struct ieee80211com *ic = ni->ni_ic; 358165887Ssam int i, j, rix, error; 359138568Ssam int okrate, badrate, fixedrate; 360165569Ssam const struct ieee80211_rateset *srs; 361170530Ssam uint8_t r; 362116742Ssam 363116742Ssam error = 0; 364170530Ssam okrate = badrate = 0; 365170530Ssam fixedrate = IEEE80211_FIXED_RATE_NONE; 366165569Ssam srs = ieee80211_get_suprates(ic, ni->ni_chan); 367120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 368116742Ssam if (flags & IEEE80211_F_DOSORT) { 369116742Ssam /* 370116742Ssam * Sort rates. 371116742Ssam */ 372116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 373116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 374116742Ssam r = nrs->rs_rates[i]; 375116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 376116742Ssam nrs->rs_rates[j] = r; 377116742Ssam } 378116742Ssam } 379116742Ssam } 380116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 381116742Ssam badrate = r; 382165887Ssam /* 383170530Ssam * Check for fixed rate. 384170530Ssam */ 385170530Ssam if (r == ic->ic_fixed_rate) 386170530Ssam fixedrate = r; 387170530Ssam /* 388165887Ssam * Check against supported rates. 389165887Ssam */ 390165887Ssam rix = findrix(srs, r); 391116742Ssam if (flags & IEEE80211_F_DONEGO) { 392165887Ssam if (rix < 0) { 393120482Ssam /* 394120482Ssam * A rate in the node's rate set is not 395120482Ssam * supported. If this is a basic rate and we 396165887Ssam * are operating as a STA then this is an error. 397120482Ssam * Otherwise we just discard/ignore the rate. 398120482Ssam */ 399165887Ssam if ((flags & IEEE80211_F_JOIN) && 400120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 401116742Ssam error++; 402165887Ssam } else if ((flags & IEEE80211_F_JOIN) == 0) { 403165887Ssam /* 404165887Ssam * Overwrite with the supported rate 405165887Ssam * value so any basic rate bit is set. 406165887Ssam */ 407165887Ssam nrs->rs_rates[i] = srs->rs_rates[rix]; 408116742Ssam } 409116742Ssam } 410165887Ssam if ((flags & IEEE80211_F_DODEL) && rix < 0) { 411116742Ssam /* 412116742Ssam * Delete unacceptable rates. 413116742Ssam */ 414165887Ssam nrs->rs_nrates--; 415165887Ssam for (j = i; j < nrs->rs_nrates; j++) 416165887Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 417165887Ssam nrs->rs_rates[j] = 0; 418165887Ssam continue; 419116742Ssam } 420165887Ssam if (rix >= 0) 421116742Ssam okrate = nrs->rs_rates[i]; 422116742Ssam i++; 423116742Ssam } 424138568Ssam if (okrate == 0 || error != 0 || 425170530Ssam ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate)) 426116742Ssam return badrate | IEEE80211_RATE_BASIC; 427116742Ssam else 428116742Ssam return RV(okrate); 429116742Ssam#undef RV 430116742Ssam} 431116742Ssam 432138568Ssam/* 433138568Ssam * Reset 11g-related state. 434138568Ssam */ 435138568Ssamvoid 436138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 437138568Ssam{ 438138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 439138568Ssam ic->ic_nonerpsta = 0; 440138568Ssam ic->ic_longslotsta = 0; 441138568Ssam /* 442138568Ssam * Short slot time is enabled only when operating in 11g 443138568Ssam * and not in an IBSS. We must also honor whether or not 444138568Ssam * the driver is capable of doing it. 445138568Ssam */ 446138568Ssam ieee80211_set_shortslottime(ic, 447170530Ssam IEEE80211_IS_CHAN_A(ic->ic_curchan) || 448170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_curchan) || 449170530Ssam (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 450138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 451138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 452138568Ssam /* 453138568Ssam * Set short preamble and ERP barker-preamble flags. 454138568Ssam */ 455170530Ssam if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 456138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 457138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 458138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 459138568Ssam } else { 460138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 461138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 462138568Ssam } 463138568Ssam} 464138568Ssam 465138568Ssam/* 466138568Ssam * Set the short slot time state and notify the driver. 467138568Ssam */ 468138568Ssamvoid 469138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 470138568Ssam{ 471138568Ssam if (onoff) 472138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 473138568Ssam else 474138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 475138568Ssam /* notify driver */ 476138568Ssam if (ic->ic_updateslot != NULL) 477138568Ssam ic->ic_updateslot(ic->ic_ifp); 478138568Ssam} 479138568Ssam 480138568Ssam/* 481138568Ssam * Check if the specified rate set supports ERP. 482138568Ssam * NB: the rate set is assumed to be sorted. 483138568Ssam */ 484138568Ssamint 485138568Ssamieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 486138568Ssam{ 487138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 488138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 489138568Ssam int i, j; 490138568Ssam 491138568Ssam if (rs->rs_nrates < N(rates)) 492138568Ssam return 0; 493138568Ssam for (i = 0; i < N(rates); i++) { 494138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 495138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 496138568Ssam if (rates[i] == r) 497138568Ssam goto next; 498138568Ssam if (r > rates[i]) 499138568Ssam return 0; 500138568Ssam } 501138568Ssam return 0; 502138568Ssam next: 503138568Ssam ; 504138568Ssam } 505138568Ssam return 1; 506138568Ssam#undef N 507138568Ssam} 508138568Ssam 509138568Ssam/* 510138568Ssam * Mark the basic rates for the 11g rate table based on the 511138568Ssam * operating mode. For real 11g we mark all the 11b rates 512138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 513138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 514138568Ssam * the basic OFDM rates. 515138568Ssam */ 516138568Ssamvoid 517138568Ssamieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 518138568Ssam{ 519170530Ssam static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { 520170530Ssam { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ 521138568Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 522138568Ssam { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 523138568Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 524170530Ssam { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ 525138568Ssam /* IEEE80211_MODE_PUREG (not yet) */ 526138568Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 527170530Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ 528170530Ssam /* IEEE80211_MODE_11NG (mixed b/g) */ 529170530Ssam { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 530138568Ssam }; 531138568Ssam int i, j; 532138568Ssam 533138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 534138568Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 535138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 536138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 537138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 538138568Ssam break; 539138568Ssam } 540138568Ssam } 541138568Ssam} 542138568Ssam 543138568Ssam/* 544138568Ssam * WME protocol support. The following parameters come from the spec. 545138568Ssam */ 546138568Ssamtypedef struct phyParamType { 547170530Ssam uint8_t aifsn; 548170530Ssam uint8_t logcwmin; 549170530Ssam uint8_t logcwmax; 550170530Ssam uint16_t txopLimit; 551170530Ssam uint8_t acm; 552138568Ssam} paramType; 553138568Ssam 554138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 555170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */ 556170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */ 557170530Ssam { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_11B */ 558170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */ 559170530Ssam { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_FH */ 560170530Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 561170530Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 562170530Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 563170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */ /* XXXcheck*/ 564170530Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */ /* XXXcheck*/ 565138568Ssam}; 566138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 567170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 568170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 569170530Ssam { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 570170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 571170530Ssam { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 572170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 573170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 574170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 575170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 576170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 577138568Ssam}; 578138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 579170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 580170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 581170530Ssam { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ 582170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 583170530Ssam { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ 584170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 585170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 586170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 587170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 588170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 589138568Ssam}; 590138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 591170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 592170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 593170530Ssam { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ 594170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 595170530Ssam { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ 596170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 597170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 598170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 599170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 600170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 601138568Ssam}; 602138568Ssam 603138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 604170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 605170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 606170530Ssam { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 607170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 608170530Ssam { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 609170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 610170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 611170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 612170530Ssam { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 613170530Ssam { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 614138568Ssam}; 615138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 616170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 617170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 618170530Ssam { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ 619170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 620170530Ssam { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ 621170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 622170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 623170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 624170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 625170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 626138568Ssam}; 627138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 628170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 629170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 630170530Ssam { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ 631170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 632170530Ssam { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ 633170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 634170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 635170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 636170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 637170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 638138568Ssam}; 639138568Ssam 640138568Ssamvoid 641138568Ssamieee80211_wme_initparams(struct ieee80211com *ic) 642138568Ssam{ 643138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 644138568Ssam const paramType *pPhyParam, *pBssPhyParam; 645138568Ssam struct wmeParams *wmep; 646170530Ssam enum ieee80211_phymode mode; 647138568Ssam int i; 648138568Ssam 649138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 650138568Ssam return; 651138568Ssam 652170530Ssam /* 653170530Ssam * Select mode; we can be called early in which case we 654170530Ssam * always use auto mode. We know we'll be called when 655170530Ssam * entering the RUN state with bsschan setup properly 656170530Ssam * so state will eventually get set correctly 657170530Ssam */ 658170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 659170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 660170530Ssam else 661170530Ssam mode = IEEE80211_MODE_AUTO; 662138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 663138568Ssam switch (i) { 664138568Ssam case WME_AC_BK: 665170530Ssam pPhyParam = &phyParamForAC_BK[mode]; 666170530Ssam pBssPhyParam = &phyParamForAC_BK[mode]; 667138568Ssam break; 668138568Ssam case WME_AC_VI: 669170530Ssam pPhyParam = &phyParamForAC_VI[mode]; 670170530Ssam pBssPhyParam = &bssPhyParamForAC_VI[mode]; 671138568Ssam break; 672138568Ssam case WME_AC_VO: 673170530Ssam pPhyParam = &phyParamForAC_VO[mode]; 674170530Ssam pBssPhyParam = &bssPhyParamForAC_VO[mode]; 675138568Ssam break; 676138568Ssam case WME_AC_BE: 677138568Ssam default: 678170530Ssam pPhyParam = &phyParamForAC_BE[mode]; 679170530Ssam pBssPhyParam = &bssPhyParamForAC_BE[mode]; 680138568Ssam break; 681138568Ssam } 682138568Ssam 683138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 684138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 685138568Ssam wmep->wmep_acm = pPhyParam->acm; 686138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 687138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 688138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 689138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 690138568Ssam } else { 691138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 692138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 693138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 694138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 695138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 696138568Ssam 697138568Ssam } 698138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 699138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 700138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 701138568Ssam , ieee80211_wme_acnames[i] 702138568Ssam , wmep->wmep_acm 703138568Ssam , wmep->wmep_aifsn 704138568Ssam , wmep->wmep_logcwmin 705138568Ssam , wmep->wmep_logcwmax 706138568Ssam , wmep->wmep_txopLimit 707138568Ssam ); 708138568Ssam 709138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 710138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 711138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 712138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 713138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 714138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 715138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 716138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 717138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 718138568Ssam , ieee80211_wme_acnames[i] 719138568Ssam , wmep->wmep_acm 720138568Ssam , wmep->wmep_aifsn 721138568Ssam , wmep->wmep_logcwmin 722138568Ssam , wmep->wmep_logcwmax 723138568Ssam , wmep->wmep_txopLimit 724138568Ssam ); 725138568Ssam } 726138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 727138568Ssam if (ic->ic_bss != NULL) { 728138568Ssam /* 729138568Ssam * Calculate agressive mode switching threshold based 730138568Ssam * on beacon interval. This doesn't need locking since 731138568Ssam * we're only called before entering the RUN state at 732138568Ssam * which point we start sending beacon frames. 733138568Ssam */ 734138568Ssam wme->wme_hipri_switch_thresh = 735138568Ssam (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 736138568Ssam ieee80211_wme_updateparams(ic); 737138568Ssam } 738138568Ssam} 739138568Ssam 740138568Ssam/* 741138568Ssam * Update WME parameters for ourself and the BSS. 742138568Ssam */ 743138568Ssamvoid 744138568Ssamieee80211_wme_updateparams_locked(struct ieee80211com *ic) 745138568Ssam{ 746138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 747170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ 748170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */ 749170530Ssam { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */ 750170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */ 751170530Ssam { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */ 752170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */ 753170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */ 754170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */ 755170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ 756170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ 757138568Ssam }; 758138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 759138568Ssam const struct wmeParams *wmep; 760138568Ssam struct wmeParams *chanp, *bssp; 761170530Ssam enum ieee80211_phymode mode; 762138568Ssam int i; 763138568Ssam 764138568Ssam /* set up the channel access parameters for the physical device */ 765138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 766138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 767138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 768138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 769138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 770138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 771138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 772138568Ssam 773138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 774138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 775138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 776138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 777138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 778138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 779138568Ssam } 780138568Ssam 781138568Ssam /* 782170530Ssam * Select mode; we can be called early in which case we 783170530Ssam * always use auto mode. We know we'll be called when 784170530Ssam * entering the RUN state with bsschan setup properly 785170530Ssam * so state will eventually get set correctly 786170530Ssam */ 787170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 788170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 789170530Ssam else 790170530Ssam mode = IEEE80211_MODE_AUTO; 791170530Ssam 792170530Ssam /* 793138568Ssam * This implements agressive mode as found in certain 794138568Ssam * vendors' AP's. When there is significant high 795138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 796138568Ssam * traffic by using conservative parameters. Otherwise 797138568Ssam * BE uses agressive params to optimize performance of 798138568Ssam * legacy/non-QoS traffic. 799138568Ssam */ 800138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 801156524Ssam (wme->wme_flags & WME_F_AGGRMODE) != 0) || 802153974Ssam (ic->ic_opmode == IEEE80211_M_STA && 803138568Ssam (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 804138568Ssam (ic->ic_flags & IEEE80211_F_WME) == 0) { 805138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 806138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 807138568Ssam 808170530Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; 809138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 810170530Ssam phyParam[mode].logcwmin; 811138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 812170530Ssam phyParam[mode].logcwmax; 813138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 814153421Ssam (ic->ic_flags & IEEE80211_F_BURST) ? 815170530Ssam phyParam[mode].txopLimit : 0; 816138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 817138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 818138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 819138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 820138568Ssam , chanp->wmep_acm 821138568Ssam , chanp->wmep_aifsn 822138568Ssam , chanp->wmep_logcwmin 823138568Ssam , chanp->wmep_logcwmax 824138568Ssam , chanp->wmep_txopLimit 825138568Ssam ); 826138568Ssam } 827138568Ssam 828138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && 829156524Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 830170530Ssam static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 831138568Ssam 3, /* IEEE80211_MODE_AUTO */ 832138568Ssam 3, /* IEEE80211_MODE_11A */ 833138568Ssam 4, /* IEEE80211_MODE_11B */ 834138568Ssam 3, /* IEEE80211_MODE_11G */ 835138568Ssam 4, /* IEEE80211_MODE_FH */ 836138568Ssam 3, /* IEEE80211_MODE_TURBO_A */ 837138568Ssam 3, /* IEEE80211_MODE_TURBO_G */ 838170530Ssam 3, /* IEEE80211_MODE_STURBO_A */ 839170530Ssam 3, /* IEEE80211_MODE_11NA */ 840170530Ssam 3, /* IEEE80211_MODE_11NG */ 841138568Ssam }; 842138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 843138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 844138568Ssam 845170530Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; 846138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 847138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 848138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 849138568Ssam , chanp->wmep_logcwmin 850138568Ssam ); 851138568Ssam } 852138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 853138568Ssam /* 854138568Ssam * Arrange for a beacon update and bump the parameter 855138568Ssam * set number so associated stations load the new values. 856138568Ssam */ 857138568Ssam wme->wme_bssChanParams.cap_info = 858138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 859138568Ssam ic->ic_flags |= IEEE80211_F_WMEUPDATE; 860138568Ssam } 861138568Ssam 862138568Ssam wme->wme_update(ic); 863138568Ssam 864138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 865138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 866138568Ssam ic->ic_opmode == IEEE80211_M_STA ? 867138568Ssam wme->wme_wmeChanParams.cap_info : 868138568Ssam wme->wme_bssChanParams.cap_info); 869138568Ssam} 870138568Ssam 871138568Ssamvoid 872138568Ssamieee80211_wme_updateparams(struct ieee80211com *ic) 873138568Ssam{ 874138568Ssam 875138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 876138568Ssam IEEE80211_BEACON_LOCK(ic); 877138568Ssam ieee80211_wme_updateparams_locked(ic); 878138568Ssam IEEE80211_BEACON_UNLOCK(ic); 879138568Ssam } 880138568Ssam} 881138568Ssam 882170530Ssam/* 883170530Ssam * Start a device. If this is the first vap running on the 884170530Ssam * underlying device then we first bring it up. 885170530Ssam */ 886170530Ssamint 887170530Ssamieee80211_init(struct ieee80211com *ic, int forcescan) 888170530Ssam{ 889170530Ssam 890170530Ssam IEEE80211_DPRINTF(ic, 891170530Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 892170530Ssam "%s\n", "start running"); 893170530Ssam 894170530Ssam /* 895170530Ssam * Kick the 802.11 state machine as appropriate. 896170530Ssam */ 897170530Ssam if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { 898170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 899170530Ssam /* 900170530Ssam * Try to be intelligent about clocking the state 901170530Ssam * machine. If we're currently in RUN state then 902170530Ssam * we should be able to apply any new state/parameters 903170530Ssam * simply by re-associating. Otherwise we need to 904170530Ssam * re-scan to select an appropriate ap. 905170530Ssam */ 906170530Ssam if (ic->ic_state != IEEE80211_S_RUN || forcescan) 907170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 908170530Ssam else 909170530Ssam ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); 910170530Ssam } else { 911170530Ssam /* 912170530Ssam * For monitor+wds modes there's nothing to do but 913170530Ssam * start running. Otherwise, if this is the first 914170530Ssam * vap to be brought up, start a scan which may be 915170530Ssam * preempted if the station is locked to a particular 916170530Ssam * channel. 917170530Ssam */ 918170530Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR || 919170530Ssam ic->ic_opmode == IEEE80211_M_WDS) { 920170530Ssam ic->ic_state = IEEE80211_S_INIT; /* XXX*/ 921170530Ssam ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 922170530Ssam } else 923170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 924170530Ssam } 925170530Ssam } 926170530Ssam return 0; 927170530Ssam} 928170530Ssam 929170530Ssam/* 930170530Ssam * Switch between turbo and non-turbo operating modes. 931170530Ssam * Use the specified channel flags to locate the new 932170530Ssam * channel, update 802.11 state, and then call back into 933170530Ssam * the driver to effect the change. 934170530Ssam */ 935153349Ssamvoid 936170530Ssamieee80211_dturbo_switch(struct ieee80211com *ic, int newflags) 937170530Ssam{ 938170530Ssam struct ieee80211_channel *chan; 939170530Ssam 940170530Ssam chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); 941170530Ssam if (chan == NULL) { /* XXX should not happen */ 942170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 943170530Ssam "%s: no channel with freq %u flags 0x%x\n", 944170530Ssam __func__, ic->ic_bsschan->ic_freq, newflags); 945170530Ssam return; 946170530Ssam } 947170530Ssam 948170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 949170530Ssam "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, 950170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], 951170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(chan)], 952170530Ssam chan->ic_freq, chan->ic_flags); 953170530Ssam 954170530Ssam ic->ic_bsschan = chan; 955170530Ssam ic->ic_prevchan = ic->ic_curchan; 956170530Ssam ic->ic_curchan = chan; 957170530Ssam ic->ic_set_channel(ic); 958170530Ssam /* NB: do not need to reset ERP state 'cuz we're in sta mode */ 959170530Ssam} 960170530Ssam 961170530Ssamvoid 962153349Ssamieee80211_beacon_miss(struct ieee80211com *ic) 963153349Ssam{ 964153349Ssam 965153349Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 966153349Ssam /* XXX check ic_curchan != ic_bsschan? */ 967153349Ssam return; 968153349Ssam } 969170530Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 970153349Ssam "%s\n", "beacon miss"); 971153349Ssam 972153349Ssam /* 973153349Ssam * Our handling is only meaningful for stations that are 974153349Ssam * associated; any other conditions else will be handled 975153349Ssam * through different means (e.g. the tx timeout on mgt frames). 976153349Ssam */ 977153349Ssam if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) 978153349Ssam return; 979153349Ssam 980153349Ssam if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { 981153349Ssam /* 982153349Ssam * Send a directed probe req before falling back to a scan; 983153349Ssam * if we receive a response ic_bmiss_count will be reset. 984153349Ssam * Some cards mistakenly report beacon miss so this avoids 985153349Ssam * the expensive scan if the ap is still there. 986153349Ssam */ 987153349Ssam ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, 988153349Ssam ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, 989153349Ssam ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, 990153349Ssam ic->ic_opt_ie, ic->ic_opt_ie_len); 991153349Ssam return; 992153349Ssam } 993153349Ssam ic->ic_bmiss_count = 0; 994170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 995170530Ssam /* 996170530Ssam * If we receive a beacon miss interrupt when using 997170530Ssam * dynamic turbo, attempt to switch modes before 998170530Ssam * reassociating. 999170530Ssam */ 1000170530Ssam if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP)) 1001170530Ssam ieee80211_dturbo_switch(ic, 1002170530Ssam ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); 1003170530Ssam /* 1004170530Ssam * Try to reassociate before scanning for a new ap. 1005170530Ssam */ 1006170530Ssam ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); 1007170530Ssam } else { 1008170530Ssam /* 1009170530Ssam * Somebody else is controlling state changes (e.g. 1010170530Ssam * a user-mode app) don't do anything that would 1011170530Ssam * confuse them; just drop into scan mode so they'll 1012170530Ssam * notified of the state change and given control. 1013170530Ssam */ 1014170530Ssam ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 1015170530Ssam } 1016153349Ssam} 1017153349Ssam 1018154736Ssam/* 1019154736Ssam * Software beacon miss handling. Check if any beacons 1020154736Ssam * were received in the last period. If not post a 1021154736Ssam * beacon miss; otherwise reset the counter. 1022154736Ssam */ 1023147765Ssamstatic void 1024154736Ssamieee80211_swbmiss(void *arg) 1025154736Ssam{ 1026154736Ssam struct ieee80211com *ic = arg; 1027154736Ssam 1028154736Ssam if (ic->ic_swbmiss_count == 0) { 1029154736Ssam ieee80211_beacon_miss(ic); 1030154736Ssam if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ 1031154736Ssam return; 1032154736Ssam } else 1033154736Ssam ic->ic_swbmiss_count = 0; 1034154736Ssam callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1035154736Ssam ieee80211_swbmiss, ic); 1036154736Ssam} 1037154736Ssam 1038154736Ssamstatic void 1039147765Ssamsta_disassoc(void *arg, struct ieee80211_node *ni) 1040147765Ssam{ 1041147765Ssam struct ieee80211com *ic = arg; 1042147765Ssam 1043147765Ssam if (ni->ni_associd != 0) { 1044147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 1045147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 1046147765Ssam ieee80211_node_leave(ic, ni); 1047147765Ssam } 1048147765Ssam} 1049147765Ssam 1050147765Ssamstatic void 1051147765Ssamsta_deauth(void *arg, struct ieee80211_node *ni) 1052147765Ssam{ 1053147765Ssam struct ieee80211com *ic = arg; 1054147765Ssam 1055147765Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 1056147765Ssam IEEE80211_REASON_ASSOC_LEAVE); 1057147765Ssam} 1058147765Ssam 1059170530Ssam/* 1060170530Ssam * Handle deauth with reason. We retry only for 1061170530Ssam * the cases where we might succeed. Otherwise 1062170530Ssam * we downgrade the ap and scan. 1063170530Ssam */ 1064170530Ssamstatic void 1065170530Ssamsta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason) 1066170530Ssam{ 1067170530Ssam switch (reason) { 1068170530Ssam case IEEE80211_STATUS_TIMEOUT: 1069170530Ssam case IEEE80211_REASON_ASSOC_EXPIRE: 1070170530Ssam case IEEE80211_REASON_NOT_AUTHED: 1071170530Ssam case IEEE80211_REASON_NOT_ASSOCED: 1072170530Ssam case IEEE80211_REASON_ASSOC_LEAVE: 1073170530Ssam case IEEE80211_REASON_ASSOC_NOT_AUTHED: 1074170530Ssam IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); 1075170530Ssam break; 1076170530Ssam default: 1077170530Ssam ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason); 1078170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1079170530Ssam ieee80211_check_scan(ic, 1080170530Ssam IEEE80211_SCAN_ACTIVE, 1081170530Ssam IEEE80211_SCAN_FOREVER, 1082170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 1083170530Ssam break; 1084170530Ssam } 1085170530Ssam} 1086170530Ssam 1087117811Ssamstatic int 1088138568Ssamieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 1089116742Ssam{ 1090138568Ssam struct ifnet *ifp = ic->ic_ifp; 1091116742Ssam struct ieee80211_node *ni; 1092117811Ssam enum ieee80211_state ostate; 1093116742Ssam 1094116742Ssam ostate = ic->ic_state; 1095138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 1096138568Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 1097117811Ssam ic->ic_state = nstate; /* state transition */ 1098170530Ssam callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */ 1099170530Ssam if (ostate != IEEE80211_S_SCAN) 1100170530Ssam ieee80211_cancel_scan(ic); /* background scan */ 1101116742Ssam ni = ic->ic_bss; /* NB: no reference held */ 1102154736Ssam if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) 1103154736Ssam callout_stop(&ic->ic_swbmiss); 1104116742Ssam switch (nstate) { 1105116742Ssam case IEEE80211_S_INIT: 1106116742Ssam switch (ostate) { 1107116742Ssam case IEEE80211_S_INIT: 1108116742Ssam break; 1109116742Ssam case IEEE80211_S_RUN: 1110116742Ssam switch (ic->ic_opmode) { 1111116742Ssam case IEEE80211_M_STA: 1112116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1113116742Ssam IEEE80211_FC0_SUBTYPE_DISASSOC, 1114116742Ssam IEEE80211_REASON_ASSOC_LEAVE); 1115138568Ssam ieee80211_sta_leave(ic, ni); 1116116742Ssam break; 1117116742Ssam case IEEE80211_M_HOSTAP: 1118147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1119147765Ssam sta_disassoc, ic); 1120116742Ssam break; 1121116742Ssam default: 1122116742Ssam break; 1123116742Ssam } 1124165894Ssam break; 1125116742Ssam case IEEE80211_S_ASSOC: 1126116742Ssam switch (ic->ic_opmode) { 1127116742Ssam case IEEE80211_M_STA: 1128116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1129116742Ssam IEEE80211_FC0_SUBTYPE_DEAUTH, 1130116742Ssam IEEE80211_REASON_AUTH_LEAVE); 1131116742Ssam break; 1132116742Ssam case IEEE80211_M_HOSTAP: 1133147765Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1134147765Ssam sta_deauth, ic); 1135116742Ssam break; 1136116742Ssam default: 1137116742Ssam break; 1138116742Ssam } 1139165894Ssam break; 1140140441Ssam case IEEE80211_S_SCAN: 1141140441Ssam ieee80211_cancel_scan(ic); 1142165894Ssam break; 1143116742Ssam case IEEE80211_S_AUTH: 1144165894Ssam break; 1145165894Ssam } 1146165894Ssam if (ostate != IEEE80211_S_INIT) { 1147165894Ssam /* NB: optimize INIT -> INIT case */ 1148165894Ssam ieee80211_drain_ifq(&ic->ic_mgtq); 1149138568Ssam ieee80211_reset_bss(ic); 1150170530Ssam ieee80211_scan_flush(ic); 1151116742Ssam } 1152138568Ssam if (ic->ic_auth->ia_detach != NULL) 1153138568Ssam ic->ic_auth->ia_detach(ic); 1154116742Ssam break; 1155116742Ssam case IEEE80211_S_SCAN: 1156116742Ssam switch (ostate) { 1157116742Ssam case IEEE80211_S_INIT: 1158170530Ssam createibss: 1159138568Ssam if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 1160138568Ssam ic->ic_opmode == IEEE80211_M_IBSS || 1161138568Ssam ic->ic_opmode == IEEE80211_M_AHDEMO) && 1162116742Ssam ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 1163116742Ssam /* 1164170530Ssam * Already have a channel; bypass the 1165170530Ssam * scan and startup immediately. Because 1166170530Ssam * of this explicitly sync the scanner state. 1167116742Ssam */ 1168170530Ssam ieee80211_scan_update(ic); 1169116742Ssam ieee80211_create_ibss(ic, ic->ic_des_chan); 1170116742Ssam } else { 1171170530Ssam ieee80211_check_scan(ic, 1172170530Ssam IEEE80211_SCAN_ACTIVE | 1173170530Ssam IEEE80211_SCAN_FLUSH, 1174170530Ssam IEEE80211_SCAN_FOREVER, 1175170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 1176116742Ssam } 1177116742Ssam break; 1178116742Ssam case IEEE80211_S_SCAN: 1179170530Ssam case IEEE80211_S_AUTH: 1180170530Ssam case IEEE80211_S_ASSOC: 1181138568Ssam /* 1182170530Ssam * These can happen either because of a timeout 1183170530Ssam * on an assoc/auth response or because of a 1184170530Ssam * change in state that requires a reset. For 1185170530Ssam * the former we're called with a non-zero arg 1186170530Ssam * that is the cause for the failure; pass this 1187170530Ssam * to the scan code so it can update state. 1188170530Ssam * Otherwise trigger a new scan unless we're in 1189170530Ssam * manual roaming mode in which case an application 1190170530Ssam * must issue an explicit scan request. 1191138568Ssam */ 1192170530Ssam if (arg != 0) 1193170530Ssam ieee80211_scan_assoc_fail(ic, 1194170530Ssam ic->ic_bss->ni_macaddr, arg); 1195170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1196170530Ssam ieee80211_check_scan(ic, 1197170530Ssam IEEE80211_SCAN_ACTIVE, 1198170530Ssam IEEE80211_SCAN_FOREVER, 1199170530Ssam ic->ic_des_nssid, ic->ic_des_ssid); 1200116742Ssam break; 1201170530Ssam case IEEE80211_S_RUN: /* beacon miss */ 1202170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1203170530Ssam ieee80211_sta_leave(ic, ni); 1204170530Ssam ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 1205170530Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 1206170530Ssam ieee80211_check_scan(ic, 1207170530Ssam IEEE80211_SCAN_ACTIVE, 1208170530Ssam IEEE80211_SCAN_FOREVER, 1209170530Ssam ic->ic_des_nssid, 1210170530Ssam ic->ic_des_ssid); 1211170530Ssam } else { 1212170530Ssam ieee80211_iterate_nodes(&ic->ic_sta, 1213170530Ssam sta_disassoc, ic); 1214170530Ssam goto createibss; 1215116742Ssam } 1216116742Ssam break; 1217116742Ssam } 1218116742Ssam break; 1219116742Ssam case IEEE80211_S_AUTH: 1220170530Ssam KASSERT(ic->ic_opmode == IEEE80211_M_STA, 1221170530Ssam ("switch to %s state when operating in mode %u", 1222170530Ssam ieee80211_state_name[nstate], ic->ic_opmode)); 1223116742Ssam switch (ostate) { 1224116742Ssam case IEEE80211_S_INIT: 1225116742Ssam case IEEE80211_S_SCAN: 1226116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1227116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 1228116742Ssam break; 1229116742Ssam case IEEE80211_S_AUTH: 1230116742Ssam case IEEE80211_S_ASSOC: 1231170530Ssam switch (arg & 0xff) { 1232116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1233116742Ssam /* ??? */ 1234116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1235116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 1236116742Ssam break; 1237116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1238170530Ssam sta_authretry(ic, ni, arg>>8); 1239116742Ssam break; 1240116742Ssam } 1241116742Ssam break; 1242116742Ssam case IEEE80211_S_RUN: 1243170530Ssam switch (arg & 0xff) { 1244116742Ssam case IEEE80211_FC0_SUBTYPE_AUTH: 1245116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1246116742Ssam IEEE80211_FC0_SUBTYPE_AUTH, 2); 1247116742Ssam ic->ic_state = ostate; /* stay RUN */ 1248116742Ssam break; 1249116742Ssam case IEEE80211_FC0_SUBTYPE_DEAUTH: 1250138568Ssam ieee80211_sta_leave(ic, ni); 1251147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1252147116Ssam /* try to reauth */ 1253147116Ssam IEEE80211_SEND_MGMT(ic, ni, 1254147116Ssam IEEE80211_FC0_SUBTYPE_AUTH, 1); 1255147116Ssam } 1256116742Ssam break; 1257116742Ssam } 1258116742Ssam break; 1259116742Ssam } 1260116742Ssam break; 1261116742Ssam case IEEE80211_S_ASSOC: 1262170530Ssam KASSERT(ic->ic_opmode == IEEE80211_M_STA, 1263170530Ssam ("switch to %s state when operating in mode %u", 1264170530Ssam ieee80211_state_name[nstate], ic->ic_opmode)); 1265116742Ssam switch (ostate) { 1266116742Ssam case IEEE80211_S_INIT: 1267116742Ssam case IEEE80211_S_SCAN: 1268138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1269138568Ssam "%s: invalid transition\n", __func__); 1270116742Ssam break; 1271116742Ssam case IEEE80211_S_AUTH: 1272170530Ssam case IEEE80211_S_ASSOC: 1273116742Ssam IEEE80211_SEND_MGMT(ic, ni, 1274116742Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1275116742Ssam break; 1276116742Ssam case IEEE80211_S_RUN: 1277138568Ssam ieee80211_sta_leave(ic, ni); 1278147116Ssam if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 1279170530Ssam IEEE80211_SEND_MGMT(ic, ni, arg ? 1280170530Ssam IEEE80211_FC0_SUBTYPE_REASSOC_REQ : 1281170530Ssam IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1282147116Ssam } 1283116742Ssam break; 1284116742Ssam } 1285116742Ssam break; 1286116742Ssam case IEEE80211_S_RUN: 1287138568Ssam if (ic->ic_flags & IEEE80211_F_WPA) { 1288138568Ssam /* XXX validate prerequisites */ 1289138568Ssam } 1290116742Ssam switch (ostate) { 1291116742Ssam case IEEE80211_S_INIT: 1292170530Ssam if (ic->ic_opmode == IEEE80211_M_MONITOR || 1293170530Ssam ic->ic_opmode == IEEE80211_M_WDS || 1294170530Ssam ic->ic_opmode == IEEE80211_M_HOSTAP) { 1295170530Ssam /* 1296170530Ssam * Already have a channel; bypass the 1297170530Ssam * scan and startup immediately. Because 1298170530Ssam * of this explicitly sync the scanner state. 1299170530Ssam */ 1300170530Ssam ieee80211_scan_update(ic); 1301170530Ssam ieee80211_create_ibss(ic, ic->ic_curchan); 1302138568Ssam break; 1303170530Ssam } 1304138568Ssam /* fall thru... */ 1305116742Ssam case IEEE80211_S_AUTH: 1306138568Ssam IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 1307138568Ssam "%s: invalid transition\n", __func__); 1308140763Ssam /* fall thru... */ 1309140763Ssam case IEEE80211_S_RUN: 1310116742Ssam break; 1311116742Ssam case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 1312116742Ssam case IEEE80211_S_ASSOC: /* infra mode */ 1313116742Ssam KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 1314116742Ssam ("%s: bogus xmit rate %u setup\n", __func__, 1315116742Ssam ni->ni_txrate)); 1316138568Ssam#ifdef IEEE80211_DEBUG 1317138568Ssam if (ieee80211_msg_debug(ic)) { 1318116742Ssam if (ic->ic_opmode == IEEE80211_M_STA) 1319138568Ssam if_printf(ifp, "associated "); 1320116742Ssam else 1321138568Ssam if_printf(ifp, "synchronized "); 1322116742Ssam printf("with %s ssid ", 1323116742Ssam ether_sprintf(ni->ni_bssid)); 1324116742Ssam ieee80211_print_essid(ic->ic_bss->ni_essid, 1325116742Ssam ni->ni_esslen); 1326116742Ssam printf(" channel %d start %uMb\n", 1327148936Ssam ieee80211_chan2ieee(ic, ic->ic_curchan), 1328116742Ssam IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 1329116742Ssam } 1330138568Ssam#endif 1331170530Ssam if (ic->ic_opmode == IEEE80211_M_STA) { 1332170530Ssam ieee80211_scan_assoc_success(ic, 1333170530Ssam ni->ni_macaddr); 1334138568Ssam ieee80211_notify_node_join(ic, ni, 1335138568Ssam arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 1336170530Ssam } 1337138568Ssam if_start(ifp); /* XXX not authorized yet */ 1338116742Ssam break; 1339116742Ssam } 1340154736Ssam if (ostate != IEEE80211_S_RUN && 1341154736Ssam ic->ic_opmode == IEEE80211_M_STA && 1342154736Ssam (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { 1343154736Ssam /* 1344154736Ssam * Start s/w beacon miss timer for devices w/o 1345154736Ssam * hardware support. We fudge a bit here since 1346154736Ssam * we're doing this in software. 1347154736Ssam */ 1348154736Ssam ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( 1349154736Ssam 2 * ic->ic_bmissthreshold * ni->ni_intval); 1350154736Ssam ic->ic_swbmiss_count = 0; 1351154736Ssam callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1352154736Ssam ieee80211_swbmiss, ic); 1353154736Ssam } 1354138568Ssam /* 1355138568Ssam * Start/stop the authenticator when operating as an 1356138568Ssam * AP. We delay until here to allow configuration to 1357138568Ssam * happen out of order. 1358138568Ssam */ 1359138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 1360138568Ssam ic->ic_auth->ia_attach != NULL) { 1361138568Ssam /* XXX check failure */ 1362138568Ssam ic->ic_auth->ia_attach(ic); 1363138568Ssam } else if (ic->ic_auth->ia_detach != NULL) { 1364138568Ssam ic->ic_auth->ia_detach(ic); 1365138568Ssam } 1366138568Ssam /* 1367138568Ssam * When 802.1x is not in use mark the port authorized 1368138568Ssam * at this point so traffic can flow. 1369138568Ssam */ 1370138568Ssam if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1371148302Ssam ieee80211_node_authorize(ni); 1372138568Ssam /* 1373138568Ssam * Enable inactivity processing. 1374138568Ssam * XXX 1375138568Ssam */ 1376170530Ssam callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, 1377170530Ssam ieee80211_node_timeout, ic); 1378116742Ssam break; 1379116742Ssam } 1380116742Ssam return 0; 1381116742Ssam} 1382