ieee80211_proto.c revision 179217
1116742Ssam/*- 2116904Ssam * Copyright (c) 2001 Atsushi Onoe 3178354Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 4116742Ssam * All rights reserved. 5116742Ssam * 6116742Ssam * Redistribution and use in source and binary forms, with or without 7116742Ssam * modification, are permitted provided that the following conditions 8116742Ssam * are met: 9116742Ssam * 1. Redistributions of source code must retain the above copyright 10116904Ssam * notice, this list of conditions and the following disclaimer. 11116904Ssam * 2. Redistributions in binary form must reproduce the above copyright 12116904Ssam * notice, this list of conditions and the following disclaimer in the 13116904Ssam * documentation and/or other materials provided with the distribution. 14116742Ssam * 15116904Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16116904Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17116904Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18116904Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19116904Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20116904Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21116904Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22116904Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23116904Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24116904Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25116742Ssam */ 26116742Ssam 27116742Ssam#include <sys/cdefs.h> 28116742Ssam__FBSDID("$FreeBSD: head/sys/net80211/ieee80211_proto.c 179217 2008-05-22 22:17:27Z sam $"); 29116742Ssam 30116742Ssam/* 31116742Ssam * IEEE 802.11 protocol support. 32116742Ssam */ 33116742Ssam 34116742Ssam#include "opt_inet.h" 35178354Ssam#include "opt_wlan.h" 36116742Ssam 37116742Ssam#include <sys/param.h> 38138568Ssam#include <sys/kernel.h> 39170530Ssam#include <sys/systm.h> 40178354Ssam#include <sys/taskqueue.h> 41170530Ssam 42116742Ssam#include <sys/socket.h> 43178354Ssam#include <sys/sockio.h> 44116742Ssam 45116742Ssam#include <net/if.h> 46116742Ssam#include <net/if_media.h> 47138568Ssam#include <net/ethernet.h> /* XXX for ether_sprintf */ 48116742Ssam 49116742Ssam#include <net80211/ieee80211_var.h> 50178354Ssam#include <net80211/ieee80211_adhoc.h> 51178354Ssam#include <net80211/ieee80211_sta.h> 52178354Ssam#include <net80211/ieee80211_hostap.h> 53178354Ssam#include <net80211/ieee80211_wds.h> 54178354Ssam#include <net80211/ieee80211_monitor.h> 55178354Ssam#include <net80211/ieee80211_input.h> 56116742Ssam 57138568Ssam/* XXX tunables */ 58138568Ssam#define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 59138568Ssam#define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 60116742Ssam 61116742Ssamconst char *ieee80211_mgt_subtype_name[] = { 62116742Ssam "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 63116742Ssam "probe_req", "probe_resp", "reserved#6", "reserved#7", 64116742Ssam "beacon", "atim", "disassoc", "auth", 65172230Ssam "deauth", "action", "reserved#14", "reserved#15" 66116742Ssam}; 67138568Ssamconst char *ieee80211_ctl_subtype_name[] = { 68138568Ssam "reserved#0", "reserved#1", "reserved#2", "reserved#3", 69138568Ssam "reserved#3", "reserved#5", "reserved#6", "reserved#7", 70138568Ssam "reserved#8", "reserved#9", "ps_poll", "rts", 71138568Ssam "cts", "ack", "cf_end", "cf_end_ack" 72138568Ssam}; 73167283Ssamconst char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 74167283Ssam "IBSS", /* IEEE80211_M_IBSS */ 75167283Ssam "STA", /* IEEE80211_M_STA */ 76178354Ssam "WDS", /* IEEE80211_M_WDS */ 77167283Ssam "AHDEMO", /* IEEE80211_M_AHDEMO */ 78167283Ssam "HOSTAP", /* IEEE80211_M_HOSTAP */ 79167283Ssam "MONITOR" /* IEEE80211_M_MONITOR */ 80167283Ssam}; 81117811Ssamconst char *ieee80211_state_name[IEEE80211_S_MAX] = { 82117811Ssam "INIT", /* IEEE80211_S_INIT */ 83117811Ssam "SCAN", /* IEEE80211_S_SCAN */ 84117811Ssam "AUTH", /* IEEE80211_S_AUTH */ 85117811Ssam "ASSOC", /* IEEE80211_S_ASSOC */ 86172058Ssam "CAC", /* IEEE80211_S_CAC */ 87172058Ssam "RUN", /* IEEE80211_S_RUN */ 88172058Ssam "CSA", /* IEEE80211_S_CSA */ 89172058Ssam "SLEEP", /* IEEE80211_S_SLEEP */ 90117811Ssam}; 91138568Ssamconst char *ieee80211_wme_acnames[] = { 92138568Ssam "WME_AC_BE", 93138568Ssam "WME_AC_BK", 94138568Ssam "WME_AC_VI", 95138568Ssam "WME_AC_VO", 96138568Ssam "WME_UPSD", 97138568Ssam}; 98116742Ssam 99178354Ssamstatic void parent_updown(void *, int); 100178354Ssamstatic int ieee80211_new_state_locked(struct ieee80211vap *, 101178354Ssam enum ieee80211_state, int); 102117811Ssam 103178354Ssamstatic int 104178354Ssamnull_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 105178354Ssam const struct ieee80211_bpf_params *params) 106172211Ssam{ 107178354Ssam struct ifnet *ifp = ni->ni_ic->ic_ifp; 108178354Ssam 109178354Ssam if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); 110178354Ssam m_freem(m); 111178354Ssam return ENETDOWN; 112172211Ssam} 113172211Ssam 114116742Ssamvoid 115138568Ssamieee80211_proto_attach(struct ieee80211com *ic) 116116742Ssam{ 117138568Ssam struct ifnet *ifp = ic->ic_ifp; 118116742Ssam 119178354Ssam /* override the 802.3 setting */ 120178354Ssam ifp->if_hdrlen = ic->ic_headroom 121178354Ssam + sizeof(struct ieee80211_qosframe_addr4) 122178354Ssam + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN 123178354Ssam + IEEE80211_WEP_EXTIVLEN; 124178354Ssam /* XXX no way to recalculate on ifdetach */ 125178354Ssam if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { 126178354Ssam /* XXX sanity check... */ 127178354Ssam max_linkhdr = ALIGN(ifp->if_hdrlen); 128178354Ssam max_hdr = max_linkhdr + max_protohdr; 129178354Ssam max_datalen = MHLEN - max_hdr; 130178354Ssam } 131127648Ssam ic->ic_protmode = IEEE80211_PROT_CTSONLY; 132116742Ssam 133178354Ssam TASK_INIT(&ic->ic_parent_task, 0, parent_updown, ifp); 134178354Ssam 135138568Ssam ic->ic_wme.wme_hipri_switch_hysteresis = 136138568Ssam AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 137138568Ssam 138116742Ssam /* initialize management frame handlers */ 139116742Ssam ic->ic_send_mgmt = ieee80211_send_mgmt; 140178354Ssam ic->ic_raw_xmit = null_raw_xmit; 141178354Ssam 142178354Ssam ieee80211_adhoc_attach(ic); 143178354Ssam ieee80211_sta_attach(ic); 144178354Ssam ieee80211_wds_attach(ic); 145178354Ssam ieee80211_hostap_attach(ic); 146178354Ssam ieee80211_monitor_attach(ic); 147116742Ssam} 148116742Ssam 149116742Ssamvoid 150138568Ssamieee80211_proto_detach(struct ieee80211com *ic) 151116742Ssam{ 152178354Ssam ieee80211_monitor_detach(ic); 153178354Ssam ieee80211_hostap_detach(ic); 154178354Ssam ieee80211_wds_detach(ic); 155178354Ssam ieee80211_adhoc_detach(ic); 156178354Ssam ieee80211_sta_detach(ic); 157178354Ssam} 158116742Ssam 159178354Ssamstatic void 160178354Ssamnull_update_beacon(struct ieee80211vap *vap, int item) 161178354Ssam{ 162178354Ssam} 163178354Ssam 164178354Ssamvoid 165178354Ssamieee80211_proto_vattach(struct ieee80211vap *vap) 166178354Ssam{ 167178354Ssam struct ieee80211com *ic = vap->iv_ic; 168178354Ssam struct ifnet *ifp = vap->iv_ifp; 169178354Ssam int i; 170178354Ssam 171178354Ssam /* override the 802.3 setting */ 172178354Ssam ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; 173178354Ssam 174178354Ssam vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; 175178354Ssam vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; 176178354Ssam vap->iv_bmiss_max = IEEE80211_BMISS_MAX; 177178354Ssam callout_init(&vap->iv_swbmiss, CALLOUT_MPSAFE); 178178354Ssam callout_init(&vap->iv_mgtsend, CALLOUT_MPSAFE); 179138568Ssam /* 180178354Ssam * Install default tx rate handling: no fixed rate, lowest 181178354Ssam * supported rate for mgmt and multicast frames. Default 182178354Ssam * max retry count. These settings can be changed by the 183178354Ssam * driver and/or user applications. 184178354Ssam */ 185178354Ssam for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_11NA; i++) { 186178354Ssam const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; 187178354Ssam 188178354Ssam vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; 189178354Ssam /* NB: we default to min supported rate for channel */ 190178354Ssam vap->iv_txparms[i].mgmtrate = 191178354Ssam rs->rs_rates[0] & IEEE80211_RATE_VAL; 192178354Ssam vap->iv_txparms[i].mcastrate = 193178354Ssam rs->rs_rates[0] & IEEE80211_RATE_VAL; 194178354Ssam vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; 195178354Ssam } 196178354Ssam for (; i < IEEE80211_MODE_MAX; i++) { 197178354Ssam vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; 198178354Ssam /* NB: default to MCS 0 */ 199178354Ssam vap->iv_txparms[i].mgmtrate = 0 | 0x80; 200178354Ssam vap->iv_txparms[i].mcastrate = 0 | 0x80; 201178354Ssam vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; 202178354Ssam } 203178354Ssam vap->iv_roaming = IEEE80211_ROAMING_AUTO; 204178354Ssam 205178354Ssam vap->iv_update_beacon = null_update_beacon; 206178354Ssam vap->iv_deliver_data = ieee80211_deliver_data; 207178354Ssam 208178354Ssam /* attach support for operating mode */ 209178354Ssam ic->ic_vattach[vap->iv_opmode](vap); 210178354Ssam} 211178354Ssam 212178354Ssamvoid 213178354Ssamieee80211_proto_vdetach(struct ieee80211vap *vap) 214178354Ssam{ 215178354Ssam#define FREEAPPIE(ie) do { \ 216178354Ssam if (ie != NULL) \ 217178354Ssam FREE(ie, M_80211_NODE_IE); \ 218178354Ssam} while (0) 219178354Ssam /* 220178354Ssam * Detach operating mode module. 221178354Ssam */ 222178354Ssam if (vap->iv_opdetach != NULL) 223178354Ssam vap->iv_opdetach(vap); 224178354Ssam /* 225138568Ssam * This should not be needed as we detach when reseting 226138568Ssam * the state but be conservative here since the 227138568Ssam * authenticator may do things like spawn kernel threads. 228138568Ssam */ 229178354Ssam if (vap->iv_auth->ia_detach != NULL) 230178354Ssam vap->iv_auth->ia_detach(vap); 231138568Ssam /* 232138568Ssam * Detach any ACL'ator. 233138568Ssam */ 234178354Ssam if (vap->iv_acl != NULL) 235178354Ssam vap->iv_acl->iac_detach(vap); 236178354Ssam 237178354Ssam FREEAPPIE(vap->iv_appie_beacon); 238178354Ssam FREEAPPIE(vap->iv_appie_probereq); 239178354Ssam FREEAPPIE(vap->iv_appie_proberesp); 240178354Ssam FREEAPPIE(vap->iv_appie_assocreq); 241178354Ssam FREEAPPIE(vap->iv_appie_assocresp); 242178354Ssam FREEAPPIE(vap->iv_appie_wpa); 243178354Ssam#undef FREEAPPIE 244116742Ssam} 245116742Ssam 246138568Ssam/* 247138568Ssam * Simple-minded authenticator module support. 248138568Ssam */ 249138568Ssam 250138568Ssam#define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 251138568Ssam/* XXX well-known names */ 252138568Ssamstatic const char *auth_modnames[IEEE80211_AUTH_MAX] = { 253138568Ssam "wlan_internal", /* IEEE80211_AUTH_NONE */ 254138568Ssam "wlan_internal", /* IEEE80211_AUTH_OPEN */ 255138568Ssam "wlan_internal", /* IEEE80211_AUTH_SHARED */ 256138568Ssam "wlan_xauth", /* IEEE80211_AUTH_8021X */ 257138568Ssam "wlan_internal", /* IEEE80211_AUTH_AUTO */ 258138568Ssam "wlan_xauth", /* IEEE80211_AUTH_WPA */ 259138568Ssam}; 260138568Ssamstatic const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 261138568Ssam 262138568Ssamstatic const struct ieee80211_authenticator auth_internal = { 263138568Ssam .ia_name = "wlan_internal", 264138568Ssam .ia_attach = NULL, 265138568Ssam .ia_detach = NULL, 266138568Ssam .ia_node_join = NULL, 267138568Ssam .ia_node_leave = NULL, 268138568Ssam}; 269138568Ssam 270138568Ssam/* 271138568Ssam * Setup internal authenticators once; they are never unregistered. 272138568Ssam */ 273138568Ssamstatic void 274138568Ssamieee80211_auth_setup(void) 275138568Ssam{ 276138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 277138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 278138568Ssam ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 279138568Ssam} 280138568SsamSYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 281138568Ssam 282138568Ssamconst struct ieee80211_authenticator * 283138568Ssamieee80211_authenticator_get(int auth) 284138568Ssam{ 285138568Ssam if (auth >= IEEE80211_AUTH_MAX) 286138568Ssam return NULL; 287138568Ssam if (authenticators[auth] == NULL) 288138568Ssam ieee80211_load_module(auth_modnames[auth]); 289138568Ssam return authenticators[auth]; 290138568Ssam} 291138568Ssam 292116742Ssamvoid 293138568Ssamieee80211_authenticator_register(int type, 294138568Ssam const struct ieee80211_authenticator *auth) 295116742Ssam{ 296138568Ssam if (type >= IEEE80211_AUTH_MAX) 297138568Ssam return; 298138568Ssam authenticators[type] = auth; 299138568Ssam} 300138568Ssam 301138568Ssamvoid 302138568Ssamieee80211_authenticator_unregister(int type) 303138568Ssam{ 304138568Ssam 305138568Ssam if (type >= IEEE80211_AUTH_MAX) 306138568Ssam return; 307138568Ssam authenticators[type] = NULL; 308138568Ssam} 309138568Ssam 310138568Ssam/* 311138568Ssam * Very simple-minded ACL module support. 312138568Ssam */ 313138568Ssam/* XXX just one for now */ 314138568Ssamstatic const struct ieee80211_aclator *acl = NULL; 315138568Ssam 316138568Ssamvoid 317138568Ssamieee80211_aclator_register(const struct ieee80211_aclator *iac) 318138568Ssam{ 319138568Ssam printf("wlan: %s acl policy registered\n", iac->iac_name); 320138568Ssam acl = iac; 321138568Ssam} 322138568Ssam 323138568Ssamvoid 324138568Ssamieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 325138568Ssam{ 326138568Ssam if (acl == iac) 327138568Ssam acl = NULL; 328138568Ssam printf("wlan: %s acl policy unregistered\n", iac->iac_name); 329138568Ssam} 330138568Ssam 331138568Ssamconst struct ieee80211_aclator * 332138568Ssamieee80211_aclator_get(const char *name) 333138568Ssam{ 334138568Ssam if (acl == NULL) 335138568Ssam ieee80211_load_module("wlan_acl"); 336138568Ssam return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 337138568Ssam} 338138568Ssam 339138568Ssamvoid 340170530Ssamieee80211_print_essid(const uint8_t *essid, int len) 341138568Ssam{ 342170530Ssam const uint8_t *p; 343116742Ssam int i; 344116742Ssam 345116742Ssam if (len > IEEE80211_NWID_LEN) 346116742Ssam len = IEEE80211_NWID_LEN; 347116742Ssam /* determine printable or not */ 348116742Ssam for (i = 0, p = essid; i < len; i++, p++) { 349116742Ssam if (*p < ' ' || *p > 0x7e) 350116742Ssam break; 351116742Ssam } 352116742Ssam if (i == len) { 353116742Ssam printf("\""); 354116742Ssam for (i = 0, p = essid; i < len; i++, p++) 355116742Ssam printf("%c", *p); 356116742Ssam printf("\""); 357116742Ssam } else { 358116742Ssam printf("0x"); 359116742Ssam for (i = 0, p = essid; i < len; i++, p++) 360116742Ssam printf("%02x", *p); 361116742Ssam } 362116742Ssam} 363116742Ssam 364116742Ssamvoid 365170530Ssamieee80211_dump_pkt(struct ieee80211com *ic, 366170530Ssam const uint8_t *buf, int len, int rate, int rssi) 367116742Ssam{ 368138568Ssam const struct ieee80211_frame *wh; 369116742Ssam int i; 370116742Ssam 371138568Ssam wh = (const struct ieee80211_frame *)buf; 372116742Ssam switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 373116742Ssam case IEEE80211_FC1_DIR_NODS: 374116742Ssam printf("NODS %s", ether_sprintf(wh->i_addr2)); 375116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 376116742Ssam printf("(%s)", ether_sprintf(wh->i_addr3)); 377116742Ssam break; 378116742Ssam case IEEE80211_FC1_DIR_TODS: 379116742Ssam printf("TODS %s", ether_sprintf(wh->i_addr2)); 380116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 381116742Ssam printf("(%s)", ether_sprintf(wh->i_addr1)); 382116742Ssam break; 383116742Ssam case IEEE80211_FC1_DIR_FROMDS: 384116742Ssam printf("FRDS %s", ether_sprintf(wh->i_addr3)); 385116742Ssam printf("->%s", ether_sprintf(wh->i_addr1)); 386116742Ssam printf("(%s)", ether_sprintf(wh->i_addr2)); 387116742Ssam break; 388116742Ssam case IEEE80211_FC1_DIR_DSTODS: 389170530Ssam printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); 390116742Ssam printf("->%s", ether_sprintf(wh->i_addr3)); 391116742Ssam printf("(%s", ether_sprintf(wh->i_addr2)); 392116742Ssam printf("->%s)", ether_sprintf(wh->i_addr1)); 393116742Ssam break; 394116742Ssam } 395116742Ssam switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 396116742Ssam case IEEE80211_FC0_TYPE_DATA: 397116742Ssam printf(" data"); 398116742Ssam break; 399116742Ssam case IEEE80211_FC0_TYPE_MGT: 400116742Ssam printf(" %s", ieee80211_mgt_subtype_name[ 401116742Ssam (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 402116742Ssam >> IEEE80211_FC0_SUBTYPE_SHIFT]); 403116742Ssam break; 404116742Ssam default: 405116742Ssam printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 406116742Ssam break; 407116742Ssam } 408170530Ssam if (IEEE80211_QOS_HAS_SEQ(wh)) { 409170530Ssam const struct ieee80211_qosframe *qwh = 410170530Ssam (const struct ieee80211_qosframe *)buf; 411170530Ssam printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, 412170530Ssam qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); 413170530Ssam } 414138568Ssam if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 415170530Ssam int off; 416170530Ssam 417170530Ssam off = ieee80211_anyhdrspace(ic, wh); 418170530Ssam printf(" WEP [IV %.02x %.02x %.02x", 419170530Ssam buf[off+0], buf[off+1], buf[off+2]); 420170530Ssam if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) 421170530Ssam printf(" %.02x %.02x %.02x", 422170530Ssam buf[off+4], buf[off+5], buf[off+6]); 423170530Ssam printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); 424138568Ssam } 425116742Ssam if (rate >= 0) 426116742Ssam printf(" %dM", rate / 2); 427116742Ssam if (rssi >= 0) 428116742Ssam printf(" +%d", rssi); 429116742Ssam printf("\n"); 430116742Ssam if (len > 0) { 431116742Ssam for (i = 0; i < len; i++) { 432116742Ssam if ((i & 1) == 0) 433116742Ssam printf(" "); 434116742Ssam printf("%02x", buf[i]); 435116742Ssam } 436116742Ssam printf("\n"); 437116742Ssam } 438116742Ssam} 439116742Ssam 440165887Ssamstatic __inline int 441165887Ssamfindrix(const struct ieee80211_rateset *rs, int r) 442165887Ssam{ 443165887Ssam int i; 444165887Ssam 445165887Ssam for (i = 0; i < rs->rs_nrates; i++) 446165887Ssam if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 447165887Ssam return i; 448165887Ssam return -1; 449165887Ssam} 450165887Ssam 451116742Ssamint 452167442Ssamieee80211_fix_rate(struct ieee80211_node *ni, 453167442Ssam struct ieee80211_rateset *nrs, int flags) 454116742Ssam{ 455116742Ssam#define RV(v) ((v) & IEEE80211_RATE_VAL) 456178354Ssam struct ieee80211vap *vap = ni->ni_vap; 457148299Ssam struct ieee80211com *ic = ni->ni_ic; 458165887Ssam int i, j, rix, error; 459178354Ssam int okrate, badrate, fixedrate, ucastrate; 460165569Ssam const struct ieee80211_rateset *srs; 461170530Ssam uint8_t r; 462116742Ssam 463116742Ssam error = 0; 464170530Ssam okrate = badrate = 0; 465178354Ssam ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; 466178354Ssam if (ucastrate != IEEE80211_FIXED_RATE_NONE) { 467178354Ssam /* 468178354Ssam * Workaround awkwardness with fixed rate. We are called 469178354Ssam * to check both the legacy rate set and the HT rate set 470178354Ssam * but we must apply any legacy fixed rate check only to the 471178354Ssam * legacy rate set and vice versa. We cannot tell what type 472178354Ssam * of rate set we've been given (legacy or HT) but we can 473178354Ssam * distinguish the fixed rate type (MCS have 0x80 set). 474178354Ssam * So to deal with this the caller communicates whether to 475178354Ssam * check MCS or legacy rate using the flags and we use the 476178354Ssam * type of any fixed rate to avoid applying an MCS to a 477178354Ssam * legacy rate and vice versa. 478178354Ssam */ 479178354Ssam if (ucastrate & 0x80) { 480178354Ssam if (flags & IEEE80211_F_DOFRATE) 481178354Ssam flags &= ~IEEE80211_F_DOFRATE; 482178354Ssam } else if ((ucastrate & 0x80) == 0) { 483178354Ssam if (flags & IEEE80211_F_DOFMCS) 484178354Ssam flags &= ~IEEE80211_F_DOFMCS; 485178354Ssam } 486178354Ssam /* NB: required to make MCS match below work */ 487178354Ssam ucastrate &= IEEE80211_RATE_VAL; 488178354Ssam } 489170530Ssam fixedrate = IEEE80211_FIXED_RATE_NONE; 490178354Ssam /* 491178354Ssam * XXX we are called to process both MCS and legacy rates; 492178354Ssam * we must use the appropriate basic rate set or chaos will 493178354Ssam * ensue; for now callers that want MCS must supply 494178354Ssam * IEEE80211_F_DOBRS; at some point we'll need to split this 495178354Ssam * function so there are two variants, one for MCS and one 496178354Ssam * for legacy rates. 497178354Ssam */ 498178354Ssam if (flags & IEEE80211_F_DOBRS) 499178354Ssam srs = (const struct ieee80211_rateset *) 500178354Ssam ieee80211_get_suphtrates(ic, ni->ni_chan); 501178354Ssam else 502178354Ssam srs = ieee80211_get_suprates(ic, ni->ni_chan); 503120482Ssam for (i = 0; i < nrs->rs_nrates; ) { 504116742Ssam if (flags & IEEE80211_F_DOSORT) { 505116742Ssam /* 506116742Ssam * Sort rates. 507116742Ssam */ 508116742Ssam for (j = i + 1; j < nrs->rs_nrates; j++) { 509116742Ssam if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 510116742Ssam r = nrs->rs_rates[i]; 511116742Ssam nrs->rs_rates[i] = nrs->rs_rates[j]; 512116742Ssam nrs->rs_rates[j] = r; 513116742Ssam } 514116742Ssam } 515116742Ssam } 516116742Ssam r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 517116742Ssam badrate = r; 518165887Ssam /* 519170530Ssam * Check for fixed rate. 520170530Ssam */ 521178354Ssam if (r == ucastrate) 522170530Ssam fixedrate = r; 523170530Ssam /* 524165887Ssam * Check against supported rates. 525165887Ssam */ 526165887Ssam rix = findrix(srs, r); 527116742Ssam if (flags & IEEE80211_F_DONEGO) { 528165887Ssam if (rix < 0) { 529120482Ssam /* 530120482Ssam * A rate in the node's rate set is not 531120482Ssam * supported. If this is a basic rate and we 532165887Ssam * are operating as a STA then this is an error. 533120482Ssam * Otherwise we just discard/ignore the rate. 534120482Ssam */ 535165887Ssam if ((flags & IEEE80211_F_JOIN) && 536120482Ssam (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 537116742Ssam error++; 538165887Ssam } else if ((flags & IEEE80211_F_JOIN) == 0) { 539165887Ssam /* 540165887Ssam * Overwrite with the supported rate 541165887Ssam * value so any basic rate bit is set. 542165887Ssam */ 543165887Ssam nrs->rs_rates[i] = srs->rs_rates[rix]; 544116742Ssam } 545116742Ssam } 546165887Ssam if ((flags & IEEE80211_F_DODEL) && rix < 0) { 547116742Ssam /* 548116742Ssam * Delete unacceptable rates. 549116742Ssam */ 550165887Ssam nrs->rs_nrates--; 551165887Ssam for (j = i; j < nrs->rs_nrates; j++) 552165887Ssam nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 553165887Ssam nrs->rs_rates[j] = 0; 554165887Ssam continue; 555116742Ssam } 556165887Ssam if (rix >= 0) 557116742Ssam okrate = nrs->rs_rates[i]; 558116742Ssam i++; 559116742Ssam } 560138568Ssam if (okrate == 0 || error != 0 || 561178354Ssam ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && 562178354Ssam fixedrate != ucastrate)) { 563178354Ssam IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, 564178354Ssam "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " 565178354Ssam "ucastrate %x\n", __func__, fixedrate, ucastrate, flags); 566116742Ssam return badrate | IEEE80211_RATE_BASIC; 567178354Ssam } else 568116742Ssam return RV(okrate); 569116742Ssam#undef RV 570116742Ssam} 571116742Ssam 572138568Ssam/* 573138568Ssam * Reset 11g-related state. 574138568Ssam */ 575138568Ssamvoid 576138568Ssamieee80211_reset_erp(struct ieee80211com *ic) 577138568Ssam{ 578138568Ssam ic->ic_flags &= ~IEEE80211_F_USEPROT; 579138568Ssam ic->ic_nonerpsta = 0; 580138568Ssam ic->ic_longslotsta = 0; 581138568Ssam /* 582138568Ssam * Short slot time is enabled only when operating in 11g 583138568Ssam * and not in an IBSS. We must also honor whether or not 584138568Ssam * the driver is capable of doing it. 585138568Ssam */ 586138568Ssam ieee80211_set_shortslottime(ic, 587170530Ssam IEEE80211_IS_CHAN_A(ic->ic_curchan) || 588170530Ssam IEEE80211_IS_CHAN_HT(ic->ic_curchan) || 589170530Ssam (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 590138568Ssam ic->ic_opmode == IEEE80211_M_HOSTAP && 591138568Ssam (ic->ic_caps & IEEE80211_C_SHSLOT))); 592138568Ssam /* 593138568Ssam * Set short preamble and ERP barker-preamble flags. 594138568Ssam */ 595170530Ssam if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 596138568Ssam (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 597138568Ssam ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 598138568Ssam ic->ic_flags &= ~IEEE80211_F_USEBARKER; 599138568Ssam } else { 600138568Ssam ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 601138568Ssam ic->ic_flags |= IEEE80211_F_USEBARKER; 602138568Ssam } 603138568Ssam} 604138568Ssam 605138568Ssam/* 606138568Ssam * Set the short slot time state and notify the driver. 607138568Ssam */ 608138568Ssamvoid 609138568Ssamieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 610138568Ssam{ 611138568Ssam if (onoff) 612138568Ssam ic->ic_flags |= IEEE80211_F_SHSLOT; 613138568Ssam else 614138568Ssam ic->ic_flags &= ~IEEE80211_F_SHSLOT; 615138568Ssam /* notify driver */ 616138568Ssam if (ic->ic_updateslot != NULL) 617138568Ssam ic->ic_updateslot(ic->ic_ifp); 618138568Ssam} 619138568Ssam 620138568Ssam/* 621138568Ssam * Check if the specified rate set supports ERP. 622138568Ssam * NB: the rate set is assumed to be sorted. 623138568Ssam */ 624138568Ssamint 625178354Ssamieee80211_iserp_rateset(const struct ieee80211_rateset *rs) 626138568Ssam{ 627138568Ssam#define N(a) (sizeof(a) / sizeof(a[0])) 628138568Ssam static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 629138568Ssam int i, j; 630138568Ssam 631138568Ssam if (rs->rs_nrates < N(rates)) 632138568Ssam return 0; 633138568Ssam for (i = 0; i < N(rates); i++) { 634138568Ssam for (j = 0; j < rs->rs_nrates; j++) { 635138568Ssam int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 636138568Ssam if (rates[i] == r) 637138568Ssam goto next; 638138568Ssam if (r > rates[i]) 639138568Ssam return 0; 640138568Ssam } 641138568Ssam return 0; 642138568Ssam next: 643138568Ssam ; 644138568Ssam } 645138568Ssam return 1; 646138568Ssam#undef N 647138568Ssam} 648138568Ssam 649138568Ssam/* 650178354Ssam * Mark the basic rates for the rate table based on the 651138568Ssam * operating mode. For real 11g we mark all the 11b rates 652138568Ssam * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 653138568Ssam * 11b rates. There's also a pseudo 11a-mode used to mark only 654138568Ssam * the basic OFDM rates. 655138568Ssam */ 656178354Ssamstatic void 657178354Ssamsetbasicrates(struct ieee80211_rateset *rs, 658178354Ssam enum ieee80211_phymode mode, int add) 659138568Ssam{ 660170530Ssam static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { 661170530Ssam { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ 662138568Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 663138568Ssam { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 664138568Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 665170530Ssam { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ 666178354Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_TURBO_A */ 667178354Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_TURBO_G (mixed b/g) */ 668178354Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_STURBO_A */ 669170530Ssam { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ 670178354Ssam { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11NG (mixed b/g) */ 671138568Ssam }; 672138568Ssam int i, j; 673138568Ssam 674138568Ssam for (i = 0; i < rs->rs_nrates; i++) { 675178354Ssam if (!add) 676178354Ssam rs->rs_rates[i] &= IEEE80211_RATE_VAL; 677138568Ssam for (j = 0; j < basic[mode].rs_nrates; j++) 678138568Ssam if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 679138568Ssam rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 680138568Ssam break; 681138568Ssam } 682138568Ssam } 683138568Ssam} 684138568Ssam 685138568Ssam/* 686178354Ssam * Set the basic rates in a rate set. 687138568Ssam */ 688178354Ssamvoid 689178354Ssamieee80211_setbasicrates(struct ieee80211_rateset *rs, 690178354Ssam enum ieee80211_phymode mode) 691178354Ssam{ 692178354Ssam setbasicrates(rs, mode, 0); 693178354Ssam} 694178354Ssam 695178354Ssam/* 696178354Ssam * Add basic rates to a rate set. 697178354Ssam */ 698178354Ssamvoid 699178354Ssamieee80211_addbasicrates(struct ieee80211_rateset *rs, 700178354Ssam enum ieee80211_phymode mode) 701178354Ssam{ 702178354Ssam setbasicrates(rs, mode, 1); 703178354Ssam} 704178354Ssam 705178354Ssam/* 706178354Ssam * WME protocol support. 707178354Ssam * 708178354Ssam * The default 11a/b/g/n parameters come from the WiFi Alliance WMM 709178354Ssam * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n 710178354Ssam * Draft 2.0 Test Plan (Appendix D). 711178354Ssam * 712178354Ssam * Static/Dynamic Turbo mode settings come from Atheros. 713178354Ssam */ 714138568Ssamtypedef struct phyParamType { 715178354Ssam uint8_t aifsn; 716178354Ssam uint8_t logcwmin; 717178354Ssam uint8_t logcwmax; 718178354Ssam uint16_t txopLimit; 719178354Ssam uint8_t acm; 720138568Ssam} paramType; 721138568Ssam 722138568Ssamstatic const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 723173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */ 724173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */ 725173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11B */ 726173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */ 727173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_FH */ 728173860Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 729173860Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 730173860Ssam { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 731173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */ 732173860Ssam { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */ 733138568Ssam}; 734138568Ssamstatic const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 735170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 736170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 737173860Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 738170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 739173860Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 740170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 741170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 742170530Ssam { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 743170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 744170530Ssam { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 745138568Ssam}; 746138568Ssamstatic const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 747170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 748170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 749173860Ssam { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */ 750170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 751173860Ssam { 1, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */ 752170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 753170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 754170530Ssam { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 755170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 756170530Ssam { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 757138568Ssam}; 758138568Ssamstatic const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 759170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 760170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 761173860Ssam { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */ 762170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 763173860Ssam { 1, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */ 764170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 765170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 766170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 767170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 768170530Ssam { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 769138568Ssam}; 770138568Ssam 771138568Ssamstatic const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 772170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 773170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 774173860Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 775170530Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 776173860Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 777170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 778170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 779170530Ssam { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 780173860Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 781173860Ssam { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 782138568Ssam}; 783138568Ssamstatic const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 784170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 785170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 786173860Ssam { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_11B */ 787170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 788173860Ssam { 2, 3, 4, 188, 0 }, /* IEEE80211_MODE_FH */ 789170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 790170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 791170530Ssam { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 792170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 793170530Ssam { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 794138568Ssam}; 795138568Ssamstatic const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 796170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 797170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 798173860Ssam { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_11B */ 799170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 800173860Ssam { 2, 2, 3, 102, 0 }, /* IEEE80211_MODE_FH */ 801170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 802170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 803170530Ssam { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 804170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 805170530Ssam { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 806138568Ssam}; 807138568Ssam 808178354Ssamstatic void 809178354Ssamieee80211_wme_initparams_locked(struct ieee80211vap *vap) 810138568Ssam{ 811178354Ssam struct ieee80211com *ic = vap->iv_ic; 812138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 813138568Ssam const paramType *pPhyParam, *pBssPhyParam; 814138568Ssam struct wmeParams *wmep; 815170530Ssam enum ieee80211_phymode mode; 816138568Ssam int i; 817138568Ssam 818178354Ssam IEEE80211_LOCK_ASSERT(ic); 819178354Ssam 820138568Ssam if ((ic->ic_caps & IEEE80211_C_WME) == 0) 821138568Ssam return; 822138568Ssam 823170530Ssam /* 824170530Ssam * Select mode; we can be called early in which case we 825170530Ssam * always use auto mode. We know we'll be called when 826170530Ssam * entering the RUN state with bsschan setup properly 827170530Ssam * so state will eventually get set correctly 828170530Ssam */ 829170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 830170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 831170530Ssam else 832170530Ssam mode = IEEE80211_MODE_AUTO; 833138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 834138568Ssam switch (i) { 835138568Ssam case WME_AC_BK: 836170530Ssam pPhyParam = &phyParamForAC_BK[mode]; 837170530Ssam pBssPhyParam = &phyParamForAC_BK[mode]; 838138568Ssam break; 839138568Ssam case WME_AC_VI: 840170530Ssam pPhyParam = &phyParamForAC_VI[mode]; 841170530Ssam pBssPhyParam = &bssPhyParamForAC_VI[mode]; 842138568Ssam break; 843138568Ssam case WME_AC_VO: 844170530Ssam pPhyParam = &phyParamForAC_VO[mode]; 845170530Ssam pBssPhyParam = &bssPhyParamForAC_VO[mode]; 846138568Ssam break; 847138568Ssam case WME_AC_BE: 848138568Ssam default: 849170530Ssam pPhyParam = &phyParamForAC_BE[mode]; 850170530Ssam pBssPhyParam = &bssPhyParamForAC_BE[mode]; 851138568Ssam break; 852138568Ssam } 853138568Ssam 854138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 855138568Ssam if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 856138568Ssam wmep->wmep_acm = pPhyParam->acm; 857138568Ssam wmep->wmep_aifsn = pPhyParam->aifsn; 858138568Ssam wmep->wmep_logcwmin = pPhyParam->logcwmin; 859138568Ssam wmep->wmep_logcwmax = pPhyParam->logcwmax; 860138568Ssam wmep->wmep_txopLimit = pPhyParam->txopLimit; 861138568Ssam } else { 862138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 863138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 864138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 865138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 866138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 867138568Ssam 868138568Ssam } 869178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 870138568Ssam "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 871138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 872138568Ssam , ieee80211_wme_acnames[i] 873138568Ssam , wmep->wmep_acm 874138568Ssam , wmep->wmep_aifsn 875138568Ssam , wmep->wmep_logcwmin 876138568Ssam , wmep->wmep_logcwmax 877138568Ssam , wmep->wmep_txopLimit 878138568Ssam ); 879138568Ssam 880138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 881138568Ssam wmep->wmep_acm = pBssPhyParam->acm; 882138568Ssam wmep->wmep_aifsn = pBssPhyParam->aifsn; 883138568Ssam wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 884138568Ssam wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 885138568Ssam wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 886178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 887138568Ssam "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 888138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 889138568Ssam , ieee80211_wme_acnames[i] 890138568Ssam , wmep->wmep_acm 891138568Ssam , wmep->wmep_aifsn 892138568Ssam , wmep->wmep_logcwmin 893138568Ssam , wmep->wmep_logcwmax 894138568Ssam , wmep->wmep_txopLimit 895138568Ssam ); 896138568Ssam } 897138568Ssam /* NB: check ic_bss to avoid NULL deref on initial attach */ 898178354Ssam if (vap->iv_bss != NULL) { 899138568Ssam /* 900138568Ssam * Calculate agressive mode switching threshold based 901138568Ssam * on beacon interval. This doesn't need locking since 902138568Ssam * we're only called before entering the RUN state at 903138568Ssam * which point we start sending beacon frames. 904138568Ssam */ 905138568Ssam wme->wme_hipri_switch_thresh = 906178354Ssam (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; 907178354Ssam ieee80211_wme_updateparams(vap); 908138568Ssam } 909138568Ssam} 910138568Ssam 911178354Ssamvoid 912178354Ssamieee80211_wme_initparams(struct ieee80211vap *vap) 913178354Ssam{ 914178354Ssam struct ieee80211com *ic = vap->iv_ic; 915178354Ssam 916178354Ssam IEEE80211_LOCK(ic); 917178354Ssam ieee80211_wme_initparams_locked(vap); 918178354Ssam IEEE80211_UNLOCK(ic); 919178354Ssam} 920178354Ssam 921138568Ssam/* 922138568Ssam * Update WME parameters for ourself and the BSS. 923138568Ssam */ 924138568Ssamvoid 925178354Ssamieee80211_wme_updateparams_locked(struct ieee80211vap *vap) 926138568Ssam{ 927138568Ssam static const paramType phyParam[IEEE80211_MODE_MAX] = { 928170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ 929170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */ 930170530Ssam { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */ 931170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */ 932170530Ssam { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */ 933170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */ 934170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */ 935170530Ssam { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */ 936170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ 937170530Ssam { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ 938138568Ssam }; 939178354Ssam struct ieee80211com *ic = vap->iv_ic; 940138568Ssam struct ieee80211_wme_state *wme = &ic->ic_wme; 941138568Ssam const struct wmeParams *wmep; 942138568Ssam struct wmeParams *chanp, *bssp; 943170530Ssam enum ieee80211_phymode mode; 944138568Ssam int i; 945138568Ssam 946138568Ssam /* set up the channel access parameters for the physical device */ 947138568Ssam for (i = 0; i < WME_NUM_AC; i++) { 948138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[i]; 949138568Ssam wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 950138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 951138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 952138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 953138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 954138568Ssam 955138568Ssam chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 956138568Ssam wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 957138568Ssam chanp->wmep_aifsn = wmep->wmep_aifsn; 958138568Ssam chanp->wmep_logcwmin = wmep->wmep_logcwmin; 959138568Ssam chanp->wmep_logcwmax = wmep->wmep_logcwmax; 960138568Ssam chanp->wmep_txopLimit = wmep->wmep_txopLimit; 961138568Ssam } 962138568Ssam 963138568Ssam /* 964170530Ssam * Select mode; we can be called early in which case we 965170530Ssam * always use auto mode. We know we'll be called when 966170530Ssam * entering the RUN state with bsschan setup properly 967170530Ssam * so state will eventually get set correctly 968170530Ssam */ 969170530Ssam if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 970170530Ssam mode = ieee80211_chan2mode(ic->ic_bsschan); 971170530Ssam else 972170530Ssam mode = IEEE80211_MODE_AUTO; 973170530Ssam 974170530Ssam /* 975138568Ssam * This implements agressive mode as found in certain 976138568Ssam * vendors' AP's. When there is significant high 977138568Ssam * priority (VI/VO) traffic in the BSS throttle back BE 978138568Ssam * traffic by using conservative parameters. Otherwise 979138568Ssam * BE uses agressive params to optimize performance of 980138568Ssam * legacy/non-QoS traffic. 981138568Ssam */ 982178354Ssam if ((vap->iv_opmode == IEEE80211_M_HOSTAP && 983156524Ssam (wme->wme_flags & WME_F_AGGRMODE) != 0) || 984178354Ssam (vap->iv_opmode == IEEE80211_M_STA && 985178354Ssam (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 986178354Ssam (vap->iv_flags & IEEE80211_F_WME) == 0) { 987138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 988138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 989138568Ssam 990170530Ssam chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; 991138568Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = 992170530Ssam phyParam[mode].logcwmin; 993138568Ssam chanp->wmep_logcwmax = bssp->wmep_logcwmax = 994170530Ssam phyParam[mode].logcwmax; 995138568Ssam chanp->wmep_txopLimit = bssp->wmep_txopLimit = 996178354Ssam (vap->iv_flags & IEEE80211_F_BURST) ? 997170530Ssam phyParam[mode].txopLimit : 0; 998178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 999138568Ssam "%s: %s [acm %u aifsn %u log2(cwmin) %u " 1000138568Ssam "log2(cwmax) %u txpoLimit %u]\n", __func__ 1001138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 1002138568Ssam , chanp->wmep_acm 1003138568Ssam , chanp->wmep_aifsn 1004138568Ssam , chanp->wmep_logcwmin 1005138568Ssam , chanp->wmep_logcwmax 1006138568Ssam , chanp->wmep_txopLimit 1007138568Ssam ); 1008138568Ssam } 1009138568Ssam 1010178354Ssam /* XXX multi-bss */ 1011178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 1012156524Ssam ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 1013170530Ssam static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 1014138568Ssam 3, /* IEEE80211_MODE_AUTO */ 1015138568Ssam 3, /* IEEE80211_MODE_11A */ 1016138568Ssam 4, /* IEEE80211_MODE_11B */ 1017138568Ssam 3, /* IEEE80211_MODE_11G */ 1018138568Ssam 4, /* IEEE80211_MODE_FH */ 1019138568Ssam 3, /* IEEE80211_MODE_TURBO_A */ 1020138568Ssam 3, /* IEEE80211_MODE_TURBO_G */ 1021170530Ssam 3, /* IEEE80211_MODE_STURBO_A */ 1022170530Ssam 3, /* IEEE80211_MODE_11NA */ 1023170530Ssam 3, /* IEEE80211_MODE_11NG */ 1024138568Ssam }; 1025138568Ssam chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 1026138568Ssam bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 1027138568Ssam 1028170530Ssam chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; 1029178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1030138568Ssam "%s: %s log2(cwmin) %u\n", __func__ 1031138568Ssam , ieee80211_wme_acnames[WME_AC_BE] 1032138568Ssam , chanp->wmep_logcwmin 1033138568Ssam ); 1034138568Ssam } 1035178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 1036138568Ssam /* 1037138568Ssam * Arrange for a beacon update and bump the parameter 1038138568Ssam * set number so associated stations load the new values. 1039138568Ssam */ 1040138568Ssam wme->wme_bssChanParams.cap_info = 1041138568Ssam (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 1042178354Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); 1043138568Ssam } 1044138568Ssam 1045138568Ssam wme->wme_update(ic); 1046138568Ssam 1047178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, 1048138568Ssam "%s: WME params updated, cap_info 0x%x\n", __func__, 1049178354Ssam vap->iv_opmode == IEEE80211_M_STA ? 1050138568Ssam wme->wme_wmeChanParams.cap_info : 1051138568Ssam wme->wme_bssChanParams.cap_info); 1052138568Ssam} 1053138568Ssam 1054138568Ssamvoid 1055178354Ssamieee80211_wme_updateparams(struct ieee80211vap *vap) 1056138568Ssam{ 1057178354Ssam struct ieee80211com *ic = vap->iv_ic; 1058138568Ssam 1059138568Ssam if (ic->ic_caps & IEEE80211_C_WME) { 1060178354Ssam IEEE80211_LOCK(ic); 1061178354Ssam ieee80211_wme_updateparams_locked(vap); 1062178354Ssam IEEE80211_UNLOCK(ic); 1063138568Ssam } 1064138568Ssam} 1065138568Ssam 1066178354Ssamstatic void 1067178354Ssamparent_updown(void *arg, int npending) 1068178354Ssam{ 1069178354Ssam struct ifnet *parent = arg; 1070178354Ssam 1071178354Ssam parent->if_ioctl(parent, SIOCSIFFLAGS, NULL); 1072178354Ssam} 1073178354Ssam 1074170530Ssam/* 1075178354Ssam * Start a vap running. If this is the first vap to be 1076178354Ssam * set running on the underlying device then we 1077178354Ssam * automatically bring the device up. 1078170530Ssam */ 1079178354Ssamvoid 1080178354Ssamieee80211_start_locked(struct ieee80211vap *vap) 1081170530Ssam{ 1082178354Ssam struct ifnet *ifp = vap->iv_ifp; 1083178354Ssam struct ieee80211com *ic = vap->iv_ic; 1084178354Ssam struct ifnet *parent = ic->ic_ifp; 1085170530Ssam 1086178354Ssam IEEE80211_LOCK_ASSERT(ic); 1087178354Ssam 1088178354Ssam IEEE80211_DPRINTF(vap, 1089170530Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1090178354Ssam "start running, %d vaps running\n", ic->ic_nrunning); 1091170530Ssam 1092178354Ssam if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1093178354Ssam /* 1094178354Ssam * Mark us running. Note that it's ok to do this first; 1095178354Ssam * if we need to bring the parent device up we defer that 1096178354Ssam * to avoid dropping the com lock. We expect the device 1097178354Ssam * to respond to being marked up by calling back into us 1098178354Ssam * through ieee80211_start_all at which point we'll come 1099178354Ssam * back in here and complete the work. 1100178354Ssam */ 1101178354Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 1102178354Ssam /* 1103178354Ssam * We are not running; if this we are the first vap 1104178354Ssam * to be brought up auto-up the parent if necessary. 1105178354Ssam */ 1106178354Ssam if (ic->ic_nrunning++ == 0 && 1107178354Ssam (parent->if_drv_flags & IFF_DRV_RUNNING) == 0) { 1108178354Ssam IEEE80211_DPRINTF(vap, 1109178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1110178354Ssam "%s: up parent %s\n", __func__, parent->if_xname); 1111178354Ssam parent->if_flags |= IFF_UP; 1112178354Ssam taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); 1113178354Ssam return; 1114178354Ssam } 1115178354Ssam } 1116170530Ssam /* 1117178354Ssam * If the parent is up and running, then kick the 1118178354Ssam * 802.11 state machine as appropriate. 1119170530Ssam */ 1120178354Ssam if ((parent->if_drv_flags & IFF_DRV_RUNNING) && 1121178354Ssam vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { 1122178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) { 1123178354Ssam#if 0 1124178354Ssam /* XXX bypasses scan too easily; disable for now */ 1125178354Ssam /* 1126178354Ssam * Try to be intelligent about clocking the state 1127178354Ssam * machine. If we're currently in RUN state then 1128178354Ssam * we should be able to apply any new state/parameters 1129178354Ssam * simply by re-associating. Otherwise we need to 1130178354Ssam * re-scan to select an appropriate ap. 1131178354Ssam */ 1132178354Ssam if (vap->iv_state >= IEEE80211_S_RUN) 1133178354Ssam ieee80211_new_state_locked(vap, 1134178354Ssam IEEE80211_S_ASSOC, 1); 1135178354Ssam else 1136178354Ssam#endif 1137178354Ssam ieee80211_new_state_locked(vap, 1138178354Ssam IEEE80211_S_SCAN, 0); 1139170530Ssam } else { 1140170530Ssam /* 1141178354Ssam * For monitor+wds mode there's nothing to do but 1142178354Ssam * start running. Otherwise if this is the first 1143170530Ssam * vap to be brought up, start a scan which may be 1144170530Ssam * preempted if the station is locked to a particular 1145170530Ssam * channel. 1146170530Ssam */ 1147178354Ssam /* XXX needed? */ 1148178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_INIT, 0); 1149178354Ssam if (vap->iv_opmode == IEEE80211_M_MONITOR || 1150178354Ssam vap->iv_opmode == IEEE80211_M_WDS) 1151178354Ssam ieee80211_new_state_locked(vap, 1152178354Ssam IEEE80211_S_RUN, -1); 1153178354Ssam else 1154178354Ssam ieee80211_new_state_locked(vap, 1155178354Ssam IEEE80211_S_SCAN, 0); 1156170530Ssam } 1157170530Ssam } 1158170530Ssam} 1159170530Ssam 1160170530Ssam/* 1161178354Ssam * Start a single vap. 1162178354Ssam */ 1163178354Ssamvoid 1164178354Ssamieee80211_init(void *arg) 1165178354Ssam{ 1166178354Ssam struct ieee80211vap *vap = arg; 1167178354Ssam 1168178354Ssam /* 1169178354Ssam * This routine is publicly accessible through the vap's 1170178354Ssam * if_init method so guard against calls during detach. 1171178354Ssam * ieee80211_vap_detach null's the backpointer before 1172178354Ssam * tearing down state to signal any callback should be 1173178354Ssam * rejected/ignored. 1174178354Ssam */ 1175178354Ssam if (vap != NULL) { 1176178354Ssam IEEE80211_DPRINTF(vap, 1177178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1178178354Ssam "%s\n", __func__); 1179178354Ssam 1180178354Ssam IEEE80211_LOCK(vap->iv_ic); 1181178354Ssam ieee80211_start_locked(vap); 1182178354Ssam IEEE80211_UNLOCK(vap->iv_ic); 1183178354Ssam } 1184178354Ssam} 1185178354Ssam 1186178354Ssam/* 1187178354Ssam * Start all runnable vap's on a device. 1188178354Ssam */ 1189178354Ssamvoid 1190178354Ssamieee80211_start_all(struct ieee80211com *ic) 1191178354Ssam{ 1192178354Ssam struct ieee80211vap *vap; 1193178354Ssam 1194178354Ssam IEEE80211_LOCK(ic); 1195178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1196178354Ssam struct ifnet *ifp = vap->iv_ifp; 1197178354Ssam if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ 1198178354Ssam ieee80211_start_locked(vap); 1199178354Ssam } 1200178354Ssam IEEE80211_UNLOCK(ic); 1201178354Ssam} 1202178354Ssam 1203178354Ssam/* 1204178354Ssam * Stop a vap. We force it down using the state machine 1205178354Ssam * then mark it's ifnet not running. If this is the last 1206178354Ssam * vap running on the underlying device then we close it 1207178354Ssam * too to insure it will be properly initialized when the 1208178354Ssam * next vap is brought up. 1209178354Ssam */ 1210178354Ssamvoid 1211178354Ssamieee80211_stop_locked(struct ieee80211vap *vap) 1212178354Ssam{ 1213178354Ssam struct ieee80211com *ic = vap->iv_ic; 1214178354Ssam struct ifnet *ifp = vap->iv_ifp; 1215178354Ssam struct ifnet *parent = ic->ic_ifp; 1216178354Ssam 1217178354Ssam IEEE80211_LOCK_ASSERT(ic); 1218178354Ssam 1219178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1220178354Ssam "stop running, %d vaps running\n", ic->ic_nrunning); 1221178354Ssam 1222178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); 1223178354Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1224178354Ssam ifp->if_drv_flags &= ~IFF_DRV_RUNNING; /* mark us stopped */ 1225178354Ssam if (--ic->ic_nrunning == 0 && 1226178354Ssam (parent->if_drv_flags & IFF_DRV_RUNNING)) { 1227178354Ssam IEEE80211_DPRINTF(vap, 1228178354Ssam IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 1229178354Ssam "down parent %s\n", parent->if_xname); 1230178354Ssam parent->if_flags &= ~IFF_UP; 1231178354Ssam taskqueue_enqueue(taskqueue_thread, &ic->ic_parent_task); 1232178354Ssam } 1233178354Ssam } 1234178354Ssam} 1235178354Ssam 1236178354Ssamvoid 1237178354Ssamieee80211_stop(struct ieee80211vap *vap) 1238178354Ssam{ 1239178354Ssam struct ieee80211com *ic = vap->iv_ic; 1240178354Ssam 1241178354Ssam IEEE80211_LOCK(ic); 1242178354Ssam ieee80211_stop_locked(vap); 1243178354Ssam IEEE80211_UNLOCK(ic); 1244178354Ssam} 1245178354Ssam 1246178354Ssam/* 1247178354Ssam * Stop all vap's running on a device. 1248178354Ssam */ 1249178354Ssamvoid 1250178354Ssamieee80211_stop_all(struct ieee80211com *ic) 1251178354Ssam{ 1252178354Ssam struct ieee80211vap *vap; 1253178354Ssam 1254178354Ssam IEEE80211_LOCK(ic); 1255178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1256178354Ssam struct ifnet *ifp = vap->iv_ifp; 1257178354Ssam if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ 1258178354Ssam ieee80211_stop_locked(vap); 1259178354Ssam } 1260178354Ssam IEEE80211_UNLOCK(ic); 1261178354Ssam} 1262178354Ssam 1263178354Ssam/* 1264170530Ssam * Switch between turbo and non-turbo operating modes. 1265170530Ssam * Use the specified channel flags to locate the new 1266170530Ssam * channel, update 802.11 state, and then call back into 1267170530Ssam * the driver to effect the change. 1268170530Ssam */ 1269153349Ssamvoid 1270178354Ssamieee80211_dturbo_switch(struct ieee80211vap *vap, int newflags) 1271170530Ssam{ 1272178354Ssam struct ieee80211com *ic = vap->iv_ic; 1273170530Ssam struct ieee80211_channel *chan; 1274170530Ssam 1275170530Ssam chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); 1276170530Ssam if (chan == NULL) { /* XXX should not happen */ 1277178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 1278170530Ssam "%s: no channel with freq %u flags 0x%x\n", 1279170530Ssam __func__, ic->ic_bsschan->ic_freq, newflags); 1280170530Ssam return; 1281170530Ssam } 1282170530Ssam 1283178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_SUPERG, 1284170530Ssam "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, 1285170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], 1286170530Ssam ieee80211_phymode_name[ieee80211_chan2mode(chan)], 1287170530Ssam chan->ic_freq, chan->ic_flags); 1288170530Ssam 1289170530Ssam ic->ic_bsschan = chan; 1290170530Ssam ic->ic_prevchan = ic->ic_curchan; 1291170530Ssam ic->ic_curchan = chan; 1292170530Ssam ic->ic_set_channel(ic); 1293170530Ssam /* NB: do not need to reset ERP state 'cuz we're in sta mode */ 1294170530Ssam} 1295170530Ssam 1296170530Ssamvoid 1297153349Ssamieee80211_beacon_miss(struct ieee80211com *ic) 1298153349Ssam{ 1299178354Ssam struct ieee80211vap *vap; 1300153349Ssam 1301178354Ssam if (ic->ic_flags & IEEE80211_F_SCAN) 1302153349Ssam return; 1303178354Ssam /* XXX locking */ 1304178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1305153349Ssam /* 1306178354Ssam * We only pass events through for sta vap's in RUN state; 1307178354Ssam * may be too restrictive but for now this saves all the 1308178354Ssam * handlers duplicating these checks. 1309153349Ssam */ 1310178354Ssam if (vap->iv_opmode == IEEE80211_M_STA && 1311178354Ssam vap->iv_state == IEEE80211_S_RUN && 1312178354Ssam vap->iv_bmiss != NULL) 1313178354Ssam vap->iv_bmiss(vap); 1314153349Ssam } 1315153349Ssam} 1316153349Ssam 1317154736Ssam/* 1318154736Ssam * Software beacon miss handling. Check if any beacons 1319154736Ssam * were received in the last period. If not post a 1320154736Ssam * beacon miss; otherwise reset the counter. 1321154736Ssam */ 1322178354Ssamvoid 1323154736Ssamieee80211_swbmiss(void *arg) 1324154736Ssam{ 1325178354Ssam struct ieee80211vap *vap = arg; 1326179217Ssam struct ieee80211com *ic = vap->iv_ic; 1327154736Ssam 1328179217Ssam /* XXX sleep state? */ 1329179217Ssam KASSERT(vap->iv_state == IEEE80211_S_RUN, 1330179217Ssam ("wrong state %d", vap->iv_state)); 1331179217Ssam 1332179217Ssam if (ic->ic_flags & IEEE80211_F_SCAN) { 1333179217Ssam /* 1334179217Ssam * If scanning just ignore and reset state. If we get a 1335179217Ssam * bmiss after coming out of scan because we haven't had 1336179217Ssam * time to receive a beacon then we should probe the AP 1337179217Ssam * before posting a real bmiss (unless iv_bmiss_max has 1338179217Ssam * been artifiically lowered). A cleaner solution might 1339179217Ssam * be to disable the timer on scan start/end but to handle 1340179217Ssam * case of multiple sta vap's we'd need to disable the 1341179217Ssam * timers of all affected vap's. 1342179217Ssam */ 1343179217Ssam vap->iv_swbmiss_count = 0; 1344179217Ssam } else if (vap->iv_swbmiss_count == 0) { 1345178354Ssam if (vap->iv_bmiss != NULL) 1346178354Ssam vap->iv_bmiss(vap); 1347178354Ssam if (vap->iv_bmiss_count == 0) /* don't re-arm timer */ 1348154736Ssam return; 1349154736Ssam } else 1350178354Ssam vap->iv_swbmiss_count = 0; 1351178354Ssam callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, 1352178354Ssam ieee80211_swbmiss, vap); 1353154736Ssam} 1354154736Ssam 1355178354Ssam/* 1356178354Ssam * Start an 802.11h channel switch. We record the parameters, 1357178354Ssam * mark the operation pending, notify each vap through the 1358178354Ssam * beacon update mechanism so it can update the beacon frame 1359178354Ssam * contents, and then switch vap's to CSA state to block outbound 1360178354Ssam * traffic. Devices that handle CSA directly can use the state 1361178354Ssam * switch to do the right thing so long as they call 1362178354Ssam * ieee80211_csa_completeswitch when it's time to complete the 1363178354Ssam * channel change. Devices that depend on the net80211 layer can 1364178354Ssam * use ieee80211_beacon_update to handle the countdown and the 1365178354Ssam * channel switch. 1366178354Ssam */ 1367178354Ssamvoid 1368178354Ssamieee80211_csa_startswitch(struct ieee80211com *ic, 1369178354Ssam struct ieee80211_channel *c, int mode, int count) 1370178354Ssam{ 1371178354Ssam struct ieee80211vap *vap; 1372178354Ssam 1373178354Ssam IEEE80211_LOCK_ASSERT(ic); 1374178354Ssam 1375178354Ssam ic->ic_csa_newchan = c; 1376178354Ssam ic->ic_csa_count = count; 1377178354Ssam /* XXX record mode? */ 1378178354Ssam ic->ic_flags |= IEEE80211_F_CSAPENDING; 1379178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1380178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP || 1381178354Ssam vap->iv_opmode == IEEE80211_M_IBSS) 1382178354Ssam ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); 1383178354Ssam /* switch to CSA state to block outbound traffic */ 1384178354Ssam if (vap->iv_state == IEEE80211_S_RUN) 1385178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); 1386178354Ssam } 1387178354Ssam ieee80211_notify_csa(ic, c, mode, count); 1388178354Ssam} 1389178354Ssam 1390178354Ssam/* 1391178354Ssam * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. 1392178354Ssam * We clear state and move all vap's in CSA state to RUN state 1393178354Ssam * so they can again transmit. 1394178354Ssam */ 1395178354Ssamvoid 1396178354Ssamieee80211_csa_completeswitch(struct ieee80211com *ic) 1397178354Ssam{ 1398178354Ssam struct ieee80211vap *vap; 1399178354Ssam 1400178354Ssam IEEE80211_LOCK_ASSERT(ic); 1401178354Ssam 1402178354Ssam KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); 1403178354Ssam 1404178354Ssam ieee80211_setcurchan(ic, ic->ic_csa_newchan); 1405178354Ssam ic->ic_csa_newchan = NULL; 1406178354Ssam ic->ic_flags &= ~IEEE80211_F_CSAPENDING; 1407178354Ssam 1408178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1409178354Ssam if (vap->iv_state == IEEE80211_S_CSA) 1410178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); 1411178354Ssam} 1412178354Ssam 1413178354Ssam/* 1414178354Ssam * Complete a DFS CAC started by ieee80211_dfs_cac_start. 1415178354Ssam * We clear state and move all vap's in CAC state to RUN state. 1416178354Ssam */ 1417178354Ssamvoid 1418178354Ssamieee80211_cac_completeswitch(struct ieee80211vap *vap0) 1419178354Ssam{ 1420178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1421178354Ssam struct ieee80211vap *vap; 1422178354Ssam 1423178354Ssam IEEE80211_LOCK(ic); 1424178354Ssam /* 1425178354Ssam * Complete CAC state change for lead vap first; then 1426178354Ssam * clock all the other vap's waiting. 1427178354Ssam */ 1428178354Ssam KASSERT(vap0->iv_state == IEEE80211_S_CAC, 1429178354Ssam ("wrong state %d", vap0->iv_state)); 1430178354Ssam ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); 1431178354Ssam 1432178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) 1433178354Ssam if (vap->iv_state == IEEE80211_S_CAC) 1434178354Ssam ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); 1435178354Ssam IEEE80211_UNLOCK(ic); 1436178354Ssam} 1437178354Ssam 1438178354Ssam/* 1439178354Ssam * Force all vap's other than the specified vap to the INIT state 1440178354Ssam * and mark them as waiting for a scan to complete. These vaps 1441178354Ssam * will be brought up when the scan completes and the scanning vap 1442178354Ssam * reaches RUN state by wakeupwaiting. 1443178354Ssam * XXX if we do this in threads we can use sleep/wakeup. 1444178354Ssam */ 1445154736Ssamstatic void 1446178354Ssammarkwaiting(struct ieee80211vap *vap0) 1447147765Ssam{ 1448178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1449178354Ssam struct ieee80211vap *vap; 1450147765Ssam 1451178354Ssam IEEE80211_LOCK_ASSERT(ic); 1452178354Ssam 1453178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1454178354Ssam if (vap == vap0) 1455178354Ssam continue; 1456178354Ssam if (vap->iv_state != IEEE80211_S_INIT) { 1457178354Ssam vap->iv_newstate(vap, IEEE80211_S_INIT, 0); 1458178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1459178354Ssam } 1460147765Ssam } 1461147765Ssam} 1462147765Ssam 1463178354Ssam/* 1464178354Ssam * Wakeup all vap's waiting for a scan to complete. This is the 1465178354Ssam * companion to markwaiting (above) and is used to coordinate 1466178354Ssam * multiple vaps scanning. 1467178354Ssam */ 1468147765Ssamstatic void 1469178354Ssamwakeupwaiting(struct ieee80211vap *vap0) 1470147765Ssam{ 1471178354Ssam struct ieee80211com *ic = vap0->iv_ic; 1472178354Ssam struct ieee80211vap *vap; 1473147765Ssam 1474178354Ssam IEEE80211_LOCK_ASSERT(ic); 1475178354Ssam 1476178354Ssam TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { 1477178354Ssam if (vap == vap0) 1478178354Ssam continue; 1479178354Ssam if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { 1480178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; 1481178354Ssam /* NB: sta's cannot go INIT->RUN */ 1482178354Ssam vap->iv_newstate(vap, 1483178354Ssam vap->iv_opmode == IEEE80211_M_STA ? 1484178354Ssam IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); 1485178354Ssam } 1486178354Ssam } 1487147765Ssam} 1488147765Ssam 1489170530Ssam/* 1490178354Ssam * Handle post state change work common to all operating modes. 1491170530Ssam */ 1492170530Ssamstatic void 1493178354Ssamieee80211_newstate_cb(struct ieee80211vap *vap, 1494178354Ssam enum ieee80211_state nstate, int arg) 1495170530Ssam{ 1496178354Ssam struct ieee80211com *ic = vap->iv_ic; 1497178354Ssam 1498178354Ssam IEEE80211_LOCK_ASSERT(ic); 1499178354Ssam 1500178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1501178354Ssam "%s: %s arg %d\n", __func__, ieee80211_state_name[nstate], arg); 1502178354Ssam 1503178354Ssam if (nstate == IEEE80211_S_RUN) { 1504178354Ssam /* 1505178354Ssam * OACTIVE may be set on the vap if the upper layer 1506178354Ssam * tried to transmit (e.g. IPv6 NDP) before we reach 1507178354Ssam * RUN state. Clear it and restart xmit. 1508178354Ssam * 1509178354Ssam * Note this can also happen as a result of SLEEP->RUN 1510178354Ssam * (i.e. coming out of power save mode). 1511178354Ssam */ 1512178354Ssam vap->iv_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1513178354Ssam if_start(vap->iv_ifp); 1514178354Ssam 1515178354Ssam /* bring up any vaps waiting on us */ 1516178354Ssam wakeupwaiting(vap); 1517178354Ssam } else if (nstate == IEEE80211_S_INIT) { 1518178354Ssam /* 1519178354Ssam * Flush the scan cache if we did the last scan (XXX?) 1520178354Ssam * and flush any frames on send queues from this vap. 1521178354Ssam * Note the mgt q is used only for legacy drivers and 1522178354Ssam * will go away shortly. 1523178354Ssam */ 1524178354Ssam ieee80211_scan_flush(vap); 1525178354Ssam 1526178354Ssam /* XXX NB: cast for altq */ 1527178354Ssam ieee80211_flush_ifq((struct ifqueue *)&ic->ic_ifp->if_snd, vap); 1528170530Ssam } 1529178354Ssam vap->iv_newstate_cb = NULL; 1530170530Ssam} 1531170530Ssam 1532178354Ssam/* 1533178354Ssam * Public interface for initiating a state machine change. 1534178354Ssam * This routine single-threads the request and coordinates 1535178354Ssam * the scheduling of multiple vaps for the purpose of selecting 1536178354Ssam * an operating channel. Specifically the following scenarios 1537178354Ssam * are handled: 1538178354Ssam * o only one vap can be selecting a channel so on transition to 1539178354Ssam * SCAN state if another vap is already scanning then 1540178354Ssam * mark the caller for later processing and return without 1541178354Ssam * doing anything (XXX? expectations by caller of synchronous operation) 1542178354Ssam * o only one vap can be doing CAC of a channel so on transition to 1543178354Ssam * CAC state if another vap is already scanning for radar then 1544178354Ssam * mark the caller for later processing and return without 1545178354Ssam * doing anything (XXX? expectations by caller of synchronous operation) 1546178354Ssam * o if another vap is already running when a request is made 1547178354Ssam * to SCAN then an operating channel has been chosen; bypass 1548178354Ssam * the scan and just join the channel 1549178354Ssam * 1550178354Ssam * Note that the state change call is done through the iv_newstate 1551178354Ssam * method pointer so any driver routine gets invoked. The driver 1552178354Ssam * will normally call back into operating mode-specific 1553178354Ssam * ieee80211_newstate routines (below) unless it needs to completely 1554178354Ssam * bypass the state machine (e.g. because the firmware has it's 1555178354Ssam * own idea how things should work). Bypassing the net80211 layer 1556178354Ssam * is usually a mistake and indicates lack of proper integration 1557178354Ssam * with the net80211 layer. 1558178354Ssam */ 1559117811Ssamstatic int 1560178354Ssamieee80211_new_state_locked(struct ieee80211vap *vap, 1561178354Ssam enum ieee80211_state nstate, int arg) 1562116742Ssam{ 1563178354Ssam struct ieee80211com *ic = vap->iv_ic; 1564178354Ssam struct ieee80211vap *vp; 1565117811Ssam enum ieee80211_state ostate; 1566178354Ssam int nrunning, nscanning, rc; 1567116742Ssam 1568178354Ssam IEEE80211_LOCK_ASSERT(ic); 1569178354Ssam 1570178354Ssam nrunning = nscanning = 0; 1571178354Ssam /* XXX can track this state instead of calculating */ 1572178354Ssam TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { 1573178354Ssam if (vp != vap) { 1574178354Ssam if (vp->iv_state >= IEEE80211_S_RUN) 1575178354Ssam nrunning++; 1576178354Ssam /* XXX doesn't handle bg scan */ 1577178354Ssam /* NB: CAC+AUTH+ASSOC treated like SCAN */ 1578178354Ssam else if (vp->iv_state > IEEE80211_S_INIT) 1579178354Ssam nscanning++; 1580178354Ssam } 1581178354Ssam } 1582178354Ssam ostate = vap->iv_state; 1583178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1584178354Ssam "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, 1585178354Ssam ieee80211_state_name[ostate], ieee80211_state_name[nstate], 1586178354Ssam nrunning, nscanning); 1587116742Ssam switch (nstate) { 1588116742Ssam case IEEE80211_S_SCAN: 1589178354Ssam if (ostate == IEEE80211_S_INIT) { 1590178354Ssam /* 1591178354Ssam * INIT -> SCAN happens on initial bringup. 1592178354Ssam */ 1593178354Ssam KASSERT(!(nscanning && nrunning), 1594178354Ssam ("%d scanning and %d running", nscanning, nrunning)); 1595178354Ssam if (nscanning) { 1596116742Ssam /* 1597178354Ssam * Someone is scanning, defer our state 1598178354Ssam * change until the work has completed. 1599116742Ssam */ 1600178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1601178354Ssam "%s: defer %s -> %s\n", 1602178354Ssam __func__, ieee80211_state_name[ostate], 1603178354Ssam ieee80211_state_name[nstate]); 1604178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1605178354Ssam rc = 0; 1606178354Ssam goto done; 1607116742Ssam } 1608178354Ssam if (nrunning) { 1609170530Ssam /* 1610178354Ssam * Someone is operating; just join the channel 1611178354Ssam * they have chosen. 1612170530Ssam */ 1613178354Ssam /* XXX kill arg? */ 1614178354Ssam /* XXX check each opmode, adhoc? */ 1615178354Ssam if (vap->iv_opmode == IEEE80211_M_STA) 1616178354Ssam nstate = IEEE80211_S_SCAN; 1617178354Ssam else 1618178354Ssam nstate = IEEE80211_S_RUN; 1619138568Ssam#ifdef IEEE80211_DEBUG 1620178354Ssam if (nstate != IEEE80211_S_SCAN) { 1621178354Ssam IEEE80211_DPRINTF(vap, 1622178354Ssam IEEE80211_MSG_STATE, 1623178354Ssam "%s: override, now %s -> %s\n", 1624178354Ssam __func__, 1625178354Ssam ieee80211_state_name[ostate], 1626178354Ssam ieee80211_state_name[nstate]); 1627178354Ssam } 1628138568Ssam#endif 1629170530Ssam } 1630178354Ssam } else { 1631178354Ssam /* 1632178354Ssam * SCAN was forced; e.g. on beacon miss. Force 1633178354Ssam * other running vap's to INIT state and mark 1634178354Ssam * them as waiting for the scan to complete. This 1635178354Ssam * insures they don't interfere with our scanning. 1636178354Ssam * 1637178354Ssam * XXX not always right, assumes ap follows sta 1638178354Ssam */ 1639178354Ssam markwaiting(vap); 1640116742Ssam } 1641178354Ssam break; 1642178354Ssam case IEEE80211_S_RUN: 1643178354Ssam if (vap->iv_opmode == IEEE80211_M_WDS && 1644178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && 1645178354Ssam nscanning) { 1646154736Ssam /* 1647178354Ssam * Legacy WDS with someone else scanning; don't 1648178354Ssam * go online until that completes as we should 1649178354Ssam * follow the other vap to the channel they choose. 1650154736Ssam */ 1651178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1652178354Ssam "%s: defer %s -> %s (legacy WDS)\n", __func__, 1653178354Ssam ieee80211_state_name[ostate], 1654178354Ssam ieee80211_state_name[nstate]); 1655178354Ssam vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; 1656178354Ssam rc = 0; 1657178354Ssam goto done; 1658154736Ssam } 1659178354Ssam if (vap->iv_opmode == IEEE80211_M_HOSTAP && 1660178354Ssam IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && 1661178354Ssam (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && 1662178354Ssam !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { 1663178354Ssam /* 1664178354Ssam * This is a DFS channel, transition to CAC state 1665178354Ssam * instead of RUN. This allows us to initiate 1666178354Ssam * Channel Availability Check (CAC) as specified 1667178354Ssam * by 11h/DFS. 1668178354Ssam */ 1669178354Ssam nstate = IEEE80211_S_CAC; 1670178354Ssam IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, 1671178354Ssam "%s: override %s -> %s (DFS)\n", __func__, 1672178354Ssam ieee80211_state_name[ostate], 1673178354Ssam ieee80211_state_name[nstate]); 1674138568Ssam } 1675116742Ssam break; 1676178354Ssam case IEEE80211_S_INIT: 1677178354Ssam if (ostate == IEEE80211_S_INIT ) { 1678178354Ssam /* XXX don't believe this */ 1679178354Ssam /* INIT -> INIT. nothing to do */ 1680178354Ssam vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; 1681178354Ssam } 1682178354Ssam /* fall thru... */ 1683172058Ssam default: 1684172058Ssam break; 1685116742Ssam } 1686178354Ssam /* XXX on transition RUN->CAC do we need to set nstate = iv_state? */ 1687178354Ssam if (ostate != nstate) { 1688178354Ssam /* 1689178354Ssam * Arrange for work to happen after state change completes. 1690178354Ssam * If this happens asynchronously the caller must arrange 1691178354Ssam * for the com lock to be held. 1692178354Ssam */ 1693178354Ssam vap->iv_newstate_cb = ieee80211_newstate_cb; 1694178354Ssam } 1695178354Ssam rc = vap->iv_newstate(vap, nstate, arg); 1696178354Ssam if (rc == 0 && vap->iv_newstate_cb != NULL) 1697178354Ssam vap->iv_newstate_cb(vap, nstate, arg); 1698178354Ssamdone: 1699178354Ssam return rc; 1700116742Ssam} 1701178354Ssam 1702178354Ssamint 1703178354Ssamieee80211_new_state(struct ieee80211vap *vap, 1704178354Ssam enum ieee80211_state nstate, int arg) 1705178354Ssam{ 1706178354Ssam struct ieee80211com *ic = vap->iv_ic; 1707178354Ssam int rc; 1708178354Ssam 1709178354Ssam IEEE80211_LOCK(ic); 1710178354Ssam rc = ieee80211_new_state_locked(vap, nstate, arg); 1711178354Ssam IEEE80211_UNLOCK(ic); 1712178354Ssam return rc; 1713178354Ssam} 1714