11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 3ae8b7333SSam Leffler * Copyright (c) 2002-2007 Sam Leffler, Errno Consulting 41a1e1d21SSam Leffler * All rights reserved. 51a1e1d21SSam Leffler * 61a1e1d21SSam Leffler * Redistribution and use in source and binary forms, with or without 71a1e1d21SSam Leffler * modification, are permitted provided that the following conditions 81a1e1d21SSam Leffler * are met: 91a1e1d21SSam Leffler * 1. Redistributions of source code must retain the above copyright 107535e66aSSam Leffler * notice, this list of conditions and the following disclaimer. 117535e66aSSam Leffler * 2. Redistributions in binary form must reproduce the above copyright 127535e66aSSam Leffler * notice, this list of conditions and the following disclaimer in the 137535e66aSSam Leffler * documentation and/or other materials provided with the distribution. 141a1e1d21SSam Leffler * 157535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 167535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 177535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 187535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 197535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 207535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 217535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 227535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 237535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 247535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 251a1e1d21SSam Leffler */ 261a1e1d21SSam Leffler 271a1e1d21SSam Leffler #include <sys/cdefs.h> 281a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 291a1e1d21SSam Leffler 301a1e1d21SSam Leffler /* 311a1e1d21SSam Leffler * IEEE 802.11 protocol support. 321a1e1d21SSam Leffler */ 331a1e1d21SSam Leffler 341a1e1d21SSam Leffler #include "opt_inet.h" 351a1e1d21SSam Leffler 361a1e1d21SSam Leffler #include <sys/param.h> 371a1e1d21SSam Leffler #include <sys/kernel.h> 388a1b9b6aSSam Leffler #include <sys/systm.h> 391a1e1d21SSam Leffler 408a1b9b6aSSam Leffler #include <sys/socket.h> 411a1e1d21SSam Leffler 421a1e1d21SSam Leffler #include <net/if.h> 431a1e1d21SSam Leffler #include <net/if_media.h> 448a1b9b6aSSam Leffler #include <net/ethernet.h> /* XXX for ether_sprintf */ 451a1e1d21SSam Leffler 461a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 471a1e1d21SSam Leffler 488a1b9b6aSSam Leffler /* XXX tunables */ 498a1b9b6aSSam Leffler #define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 508a1b9b6aSSam Leffler #define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 511a1e1d21SSam Leffler 521a1e1d21SSam Leffler #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 531a1e1d21SSam Leffler 541a1e1d21SSam Leffler const char *ieee80211_mgt_subtype_name[] = { 551a1e1d21SSam Leffler "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 561a1e1d21SSam Leffler "probe_req", "probe_resp", "reserved#6", "reserved#7", 571a1e1d21SSam Leffler "beacon", "atim", "disassoc", "auth", 581a1e1d21SSam Leffler "deauth", "reserved#13", "reserved#14", "reserved#15" 591a1e1d21SSam Leffler }; 608a1b9b6aSSam Leffler const char *ieee80211_ctl_subtype_name[] = { 618a1b9b6aSSam Leffler "reserved#0", "reserved#1", "reserved#2", "reserved#3", 628a1b9b6aSSam Leffler "reserved#3", "reserved#5", "reserved#6", "reserved#7", 638a1b9b6aSSam Leffler "reserved#8", "reserved#9", "ps_poll", "rts", 648a1b9b6aSSam Leffler "cts", "ack", "cf_end", "cf_end_ack" 658a1b9b6aSSam Leffler }; 6649aa47d6SSam Leffler const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 6749aa47d6SSam Leffler "IBSS", /* IEEE80211_M_IBSS */ 6849aa47d6SSam Leffler "STA", /* IEEE80211_M_STA */ 6949aa47d6SSam Leffler "#2", 7049aa47d6SSam Leffler "AHDEMO", /* IEEE80211_M_AHDEMO */ 7149aa47d6SSam Leffler "#4", "#5", 7249aa47d6SSam Leffler "HOSTAP", /* IEEE80211_M_HOSTAP */ 7349aa47d6SSam Leffler "#7", 7449aa47d6SSam Leffler "MONITOR" /* IEEE80211_M_MONITOR */ 7549aa47d6SSam Leffler }; 76a11c9a5cSSam Leffler const char *ieee80211_state_name[IEEE80211_S_MAX] = { 77a11c9a5cSSam Leffler "INIT", /* IEEE80211_S_INIT */ 78a11c9a5cSSam Leffler "SCAN", /* IEEE80211_S_SCAN */ 79a11c9a5cSSam Leffler "AUTH", /* IEEE80211_S_AUTH */ 80a11c9a5cSSam Leffler "ASSOC", /* IEEE80211_S_ASSOC */ 81a11c9a5cSSam Leffler "RUN" /* IEEE80211_S_RUN */ 82a11c9a5cSSam Leffler }; 838a1b9b6aSSam Leffler const char *ieee80211_wme_acnames[] = { 848a1b9b6aSSam Leffler "WME_AC_BE", 858a1b9b6aSSam Leffler "WME_AC_BK", 868a1b9b6aSSam Leffler "WME_AC_VI", 878a1b9b6aSSam Leffler "WME_AC_VO", 888a1b9b6aSSam Leffler "WME_UPSD", 898a1b9b6aSSam Leffler }; 90a11c9a5cSSam Leffler 91a11c9a5cSSam Leffler static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 921a1e1d21SSam Leffler 931a1e1d21SSam Leffler void 948a1b9b6aSSam Leffler ieee80211_proto_attach(struct ieee80211com *ic) 951a1e1d21SSam Leffler { 968a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 971a1e1d21SSam Leffler 988a1b9b6aSSam Leffler /* XXX room for crypto */ 998a1b9b6aSSam Leffler ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 1001a1e1d21SSam Leffler 1011a1e1d21SSam Leffler ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 10233acb1ceSSam Leffler ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 1032c39b32cSSam Leffler ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 104e701e041SSam Leffler ic->ic_bmiss_max = IEEE80211_BMISS_MAX; 105e99662a6SSam Leffler callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); 10668e8e04eSSam Leffler callout_init(&ic->ic_mgtsend, CALLOUT_MPSAFE); 10764353cb0SSam Leffler ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; 1082e79ca97SSam Leffler ic->ic_protmode = IEEE80211_PROT_CTSONLY; 1098a1b9b6aSSam Leffler ic->ic_roaming = IEEE80211_ROAMING_AUTO; 1108a1b9b6aSSam Leffler 1118a1b9b6aSSam Leffler ic->ic_wme.wme_hipri_switch_hysteresis = 1128a1b9b6aSSam Leffler AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 1131a1e1d21SSam Leffler 1149bf40edeSBrooks Davis mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 1151a1e1d21SSam Leffler 116a11c9a5cSSam Leffler /* protocol state change handler */ 117a11c9a5cSSam Leffler ic->ic_newstate = ieee80211_newstate; 118a11c9a5cSSam Leffler 1191a1e1d21SSam Leffler /* initialize management frame handlers */ 1201a1e1d21SSam Leffler ic->ic_recv_mgmt = ieee80211_recv_mgmt; 1211a1e1d21SSam Leffler ic->ic_send_mgmt = ieee80211_send_mgmt; 122246b5467SSam Leffler ic->ic_raw_xmit = ieee80211_raw_xmit; 1231a1e1d21SSam Leffler } 1241a1e1d21SSam Leffler 1251a1e1d21SSam Leffler void 1268a1b9b6aSSam Leffler ieee80211_proto_detach(struct ieee80211com *ic) 1271a1e1d21SSam Leffler { 1288a1b9b6aSSam Leffler 1298a1b9b6aSSam Leffler /* 1308a1b9b6aSSam Leffler * This should not be needed as we detach when reseting 1318a1b9b6aSSam Leffler * the state but be conservative here since the 1328a1b9b6aSSam Leffler * authenticator may do things like spawn kernel threads. 1338a1b9b6aSSam Leffler */ 1348a1b9b6aSSam Leffler if (ic->ic_auth->ia_detach) 1358a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 1361a1e1d21SSam Leffler 137915f1482SSam Leffler ieee80211_drain_ifq(&ic->ic_mgtq); 1381a1e1d21SSam Leffler mtx_destroy(&ic->ic_mgtq.ifq_mtx); 1398a1b9b6aSSam Leffler 1408a1b9b6aSSam Leffler /* 1418a1b9b6aSSam Leffler * Detach any ACL'ator. 1428a1b9b6aSSam Leffler */ 1438a1b9b6aSSam Leffler if (ic->ic_acl != NULL) 1448a1b9b6aSSam Leffler ic->ic_acl->iac_detach(ic); 1458a1b9b6aSSam Leffler } 1468a1b9b6aSSam Leffler 1478a1b9b6aSSam Leffler /* 1488a1b9b6aSSam Leffler * Simple-minded authenticator module support. 1498a1b9b6aSSam Leffler */ 1508a1b9b6aSSam Leffler 1518a1b9b6aSSam Leffler #define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 1528a1b9b6aSSam Leffler /* XXX well-known names */ 1538a1b9b6aSSam Leffler static const char *auth_modnames[IEEE80211_AUTH_MAX] = { 1548a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_NONE */ 1558a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_OPEN */ 1568a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_SHARED */ 1578a1b9b6aSSam Leffler "wlan_xauth", /* IEEE80211_AUTH_8021X */ 1588a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_AUTO */ 1598a1b9b6aSSam Leffler "wlan_xauth", /* IEEE80211_AUTH_WPA */ 1608a1b9b6aSSam Leffler }; 1618a1b9b6aSSam Leffler static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 1628a1b9b6aSSam Leffler 1638a1b9b6aSSam Leffler static const struct ieee80211_authenticator auth_internal = { 1648a1b9b6aSSam Leffler .ia_name = "wlan_internal", 1658a1b9b6aSSam Leffler .ia_attach = NULL, 1668a1b9b6aSSam Leffler .ia_detach = NULL, 1678a1b9b6aSSam Leffler .ia_node_join = NULL, 1688a1b9b6aSSam Leffler .ia_node_leave = NULL, 1698a1b9b6aSSam Leffler }; 1708a1b9b6aSSam Leffler 1718a1b9b6aSSam Leffler /* 1728a1b9b6aSSam Leffler * Setup internal authenticators once; they are never unregistered. 1738a1b9b6aSSam Leffler */ 1748a1b9b6aSSam Leffler static void 1758a1b9b6aSSam Leffler ieee80211_auth_setup(void) 1768a1b9b6aSSam Leffler { 1778a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 1788a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 1798a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 1808a1b9b6aSSam Leffler } 1818a1b9b6aSSam Leffler SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 1828a1b9b6aSSam Leffler 1838a1b9b6aSSam Leffler const struct ieee80211_authenticator * 1848a1b9b6aSSam Leffler ieee80211_authenticator_get(int auth) 1858a1b9b6aSSam Leffler { 1868a1b9b6aSSam Leffler if (auth >= IEEE80211_AUTH_MAX) 1878a1b9b6aSSam Leffler return NULL; 1888a1b9b6aSSam Leffler if (authenticators[auth] == NULL) 1898a1b9b6aSSam Leffler ieee80211_load_module(auth_modnames[auth]); 1908a1b9b6aSSam Leffler return authenticators[auth]; 1911a1e1d21SSam Leffler } 1921a1e1d21SSam Leffler 1931a1e1d21SSam Leffler void 1948a1b9b6aSSam Leffler ieee80211_authenticator_register(int type, 1958a1b9b6aSSam Leffler const struct ieee80211_authenticator *auth) 1961a1e1d21SSam Leffler { 1978a1b9b6aSSam Leffler if (type >= IEEE80211_AUTH_MAX) 1988a1b9b6aSSam Leffler return; 1998a1b9b6aSSam Leffler authenticators[type] = auth; 2008a1b9b6aSSam Leffler } 2018a1b9b6aSSam Leffler 2028a1b9b6aSSam Leffler void 2038a1b9b6aSSam Leffler ieee80211_authenticator_unregister(int type) 2048a1b9b6aSSam Leffler { 2058a1b9b6aSSam Leffler 2068a1b9b6aSSam Leffler if (type >= IEEE80211_AUTH_MAX) 2078a1b9b6aSSam Leffler return; 2088a1b9b6aSSam Leffler authenticators[type] = NULL; 2098a1b9b6aSSam Leffler } 2108a1b9b6aSSam Leffler 2118a1b9b6aSSam Leffler /* 2128a1b9b6aSSam Leffler * Very simple-minded ACL module support. 2138a1b9b6aSSam Leffler */ 2148a1b9b6aSSam Leffler /* XXX just one for now */ 2158a1b9b6aSSam Leffler static const struct ieee80211_aclator *acl = NULL; 2168a1b9b6aSSam Leffler 2178a1b9b6aSSam Leffler void 2188a1b9b6aSSam Leffler ieee80211_aclator_register(const struct ieee80211_aclator *iac) 2198a1b9b6aSSam Leffler { 2208a1b9b6aSSam Leffler printf("wlan: %s acl policy registered\n", iac->iac_name); 2218a1b9b6aSSam Leffler acl = iac; 2228a1b9b6aSSam Leffler } 2238a1b9b6aSSam Leffler 2248a1b9b6aSSam Leffler void 2258a1b9b6aSSam Leffler ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 2268a1b9b6aSSam Leffler { 2278a1b9b6aSSam Leffler if (acl == iac) 2288a1b9b6aSSam Leffler acl = NULL; 2298a1b9b6aSSam Leffler printf("wlan: %s acl policy unregistered\n", iac->iac_name); 2308a1b9b6aSSam Leffler } 2318a1b9b6aSSam Leffler 2328a1b9b6aSSam Leffler const struct ieee80211_aclator * 2338a1b9b6aSSam Leffler ieee80211_aclator_get(const char *name) 2348a1b9b6aSSam Leffler { 2358a1b9b6aSSam Leffler if (acl == NULL) 2368a1b9b6aSSam Leffler ieee80211_load_module("wlan_acl"); 2378a1b9b6aSSam Leffler return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 2388a1b9b6aSSam Leffler } 2398a1b9b6aSSam Leffler 2408a1b9b6aSSam Leffler void 24168e8e04eSSam Leffler ieee80211_print_essid(const uint8_t *essid, int len) 2428a1b9b6aSSam Leffler { 24368e8e04eSSam Leffler const uint8_t *p; 2441a1e1d21SSam Leffler int i; 2451a1e1d21SSam Leffler 2461a1e1d21SSam Leffler if (len > IEEE80211_NWID_LEN) 2471a1e1d21SSam Leffler len = IEEE80211_NWID_LEN; 2481a1e1d21SSam Leffler /* determine printable or not */ 2491a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) { 2501a1e1d21SSam Leffler if (*p < ' ' || *p > 0x7e) 2511a1e1d21SSam Leffler break; 2521a1e1d21SSam Leffler } 2531a1e1d21SSam Leffler if (i == len) { 2541a1e1d21SSam Leffler printf("\""); 2551a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) 2561a1e1d21SSam Leffler printf("%c", *p); 2571a1e1d21SSam Leffler printf("\""); 2581a1e1d21SSam Leffler } else { 2591a1e1d21SSam Leffler printf("0x"); 2601a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) 2611a1e1d21SSam Leffler printf("%02x", *p); 2621a1e1d21SSam Leffler } 2631a1e1d21SSam Leffler } 2641a1e1d21SSam Leffler 2651a1e1d21SSam Leffler void 26668e8e04eSSam Leffler ieee80211_dump_pkt(struct ieee80211com *ic, 26768e8e04eSSam Leffler const uint8_t *buf, int len, int rate, int rssi) 2681a1e1d21SSam Leffler { 2698a1b9b6aSSam Leffler const struct ieee80211_frame *wh; 2701a1e1d21SSam Leffler int i; 2711a1e1d21SSam Leffler 2728a1b9b6aSSam Leffler wh = (const struct ieee80211_frame *)buf; 2731a1e1d21SSam Leffler switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 2741a1e1d21SSam Leffler case IEEE80211_FC1_DIR_NODS: 2751a1e1d21SSam Leffler printf("NODS %s", ether_sprintf(wh->i_addr2)); 2761a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr1)); 2771a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr3)); 2781a1e1d21SSam Leffler break; 2791a1e1d21SSam Leffler case IEEE80211_FC1_DIR_TODS: 2801a1e1d21SSam Leffler printf("TODS %s", ether_sprintf(wh->i_addr2)); 2811a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr3)); 2821a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr1)); 2831a1e1d21SSam Leffler break; 2841a1e1d21SSam Leffler case IEEE80211_FC1_DIR_FROMDS: 2851a1e1d21SSam Leffler printf("FRDS %s", ether_sprintf(wh->i_addr3)); 2861a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr1)); 2871a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr2)); 2881a1e1d21SSam Leffler break; 2891a1e1d21SSam Leffler case IEEE80211_FC1_DIR_DSTODS: 29068e8e04eSSam Leffler printf("DSDS %s", ether_sprintf((const uint8_t *)&wh[1])); 2911a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr3)); 2921a1e1d21SSam Leffler printf("(%s", ether_sprintf(wh->i_addr2)); 2931a1e1d21SSam Leffler printf("->%s)", ether_sprintf(wh->i_addr1)); 2941a1e1d21SSam Leffler break; 2951a1e1d21SSam Leffler } 2961a1e1d21SSam Leffler switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 2971a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_DATA: 2981a1e1d21SSam Leffler printf(" data"); 2991a1e1d21SSam Leffler break; 3001a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_MGT: 3011a1e1d21SSam Leffler printf(" %s", ieee80211_mgt_subtype_name[ 3021a1e1d21SSam Leffler (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 3031a1e1d21SSam Leffler >> IEEE80211_FC0_SUBTYPE_SHIFT]); 3041a1e1d21SSam Leffler break; 3051a1e1d21SSam Leffler default: 3061a1e1d21SSam Leffler printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 3071a1e1d21SSam Leffler break; 3081a1e1d21SSam Leffler } 30968e8e04eSSam Leffler if (IEEE80211_QOS_HAS_SEQ(wh)) { 31068e8e04eSSam Leffler const struct ieee80211_qosframe *qwh = 31168e8e04eSSam Leffler (const struct ieee80211_qosframe *)buf; 31268e8e04eSSam Leffler printf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, 31368e8e04eSSam Leffler qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); 31468e8e04eSSam Leffler } 3158a1b9b6aSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 31668e8e04eSSam Leffler int off; 31768e8e04eSSam Leffler 31868e8e04eSSam Leffler off = ieee80211_anyhdrspace(ic, wh); 31968e8e04eSSam Leffler printf(" WEP [IV %.02x %.02x %.02x", 32068e8e04eSSam Leffler buf[off+0], buf[off+1], buf[off+2]); 32168e8e04eSSam Leffler if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) 32268e8e04eSSam Leffler printf(" %.02x %.02x %.02x", 32368e8e04eSSam Leffler buf[off+4], buf[off+5], buf[off+6]); 32468e8e04eSSam Leffler printf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); 3258a1b9b6aSSam Leffler } 3261a1e1d21SSam Leffler if (rate >= 0) 3271a1e1d21SSam Leffler printf(" %dM", rate / 2); 3281a1e1d21SSam Leffler if (rssi >= 0) 3291a1e1d21SSam Leffler printf(" +%d", rssi); 3301a1e1d21SSam Leffler printf("\n"); 3311a1e1d21SSam Leffler if (len > 0) { 3321a1e1d21SSam Leffler for (i = 0; i < len; i++) { 3331a1e1d21SSam Leffler if ((i & 1) == 0) 3341a1e1d21SSam Leffler printf(" "); 3351a1e1d21SSam Leffler printf("%02x", buf[i]); 3361a1e1d21SSam Leffler } 3371a1e1d21SSam Leffler printf("\n"); 3381a1e1d21SSam Leffler } 3391a1e1d21SSam Leffler } 3401a1e1d21SSam Leffler 34179edaebfSSam Leffler static __inline int 34279edaebfSSam Leffler findrix(const struct ieee80211_rateset *rs, int r) 34379edaebfSSam Leffler { 34479edaebfSSam Leffler int i; 34579edaebfSSam Leffler 34679edaebfSSam Leffler for (i = 0; i < rs->rs_nrates; i++) 34779edaebfSSam Leffler if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 34879edaebfSSam Leffler return i; 34979edaebfSSam Leffler return -1; 35079edaebfSSam Leffler } 35179edaebfSSam Leffler 3521a1e1d21SSam Leffler int 35370e28b9aSSam Leffler ieee80211_fix_rate(struct ieee80211_node *ni, 35470e28b9aSSam Leffler struct ieee80211_rateset *nrs, int flags) 3551a1e1d21SSam Leffler { 3561a1e1d21SSam Leffler #define RV(v) ((v) & IEEE80211_RATE_VAL) 3577d77cd53SSam Leffler struct ieee80211com *ic = ni->ni_ic; 35879edaebfSSam Leffler int i, j, rix, error; 3598a1b9b6aSSam Leffler int okrate, badrate, fixedrate; 36041b3c790SSam Leffler const struct ieee80211_rateset *srs; 36168e8e04eSSam Leffler uint8_t r; 3621a1e1d21SSam Leffler 3631a1e1d21SSam Leffler error = 0; 36468e8e04eSSam Leffler okrate = badrate = 0; 36568e8e04eSSam Leffler fixedrate = IEEE80211_FIXED_RATE_NONE; 36641b3c790SSam Leffler srs = ieee80211_get_suprates(ic, ni->ni_chan); 367ef39d4beSSam Leffler for (i = 0; i < nrs->rs_nrates; ) { 3681a1e1d21SSam Leffler if (flags & IEEE80211_F_DOSORT) { 3691a1e1d21SSam Leffler /* 3701a1e1d21SSam Leffler * Sort rates. 3711a1e1d21SSam Leffler */ 3721a1e1d21SSam Leffler for (j = i + 1; j < nrs->rs_nrates; j++) { 3731a1e1d21SSam Leffler if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 3741a1e1d21SSam Leffler r = nrs->rs_rates[i]; 3751a1e1d21SSam Leffler nrs->rs_rates[i] = nrs->rs_rates[j]; 3761a1e1d21SSam Leffler nrs->rs_rates[j] = r; 3771a1e1d21SSam Leffler } 3781a1e1d21SSam Leffler } 3791a1e1d21SSam Leffler } 3801a1e1d21SSam Leffler r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 3811a1e1d21SSam Leffler badrate = r; 3821a1e1d21SSam Leffler /* 38368e8e04eSSam Leffler * Check for fixed rate. 3841a1e1d21SSam Leffler */ 38568e8e04eSSam Leffler if (r == ic->ic_fixed_rate) 3868a1b9b6aSSam Leffler fixedrate = r; 3871a1e1d21SSam Leffler /* 3881a1e1d21SSam Leffler * Check against supported rates. 3891a1e1d21SSam Leffler */ 39079edaebfSSam Leffler rix = findrix(srs, r); 39179edaebfSSam Leffler if (flags & IEEE80211_F_DONEGO) { 39279edaebfSSam Leffler if (rix < 0) { 393ef39d4beSSam Leffler /* 394ef39d4beSSam Leffler * A rate in the node's rate set is not 395ef39d4beSSam Leffler * supported. If this is a basic rate and we 39679edaebfSSam Leffler * are operating as a STA then this is an error. 397ef39d4beSSam Leffler * Otherwise we just discard/ignore the rate. 398ef39d4beSSam Leffler */ 39979edaebfSSam Leffler if ((flags & IEEE80211_F_JOIN) && 400ef39d4beSSam Leffler (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 4011a1e1d21SSam Leffler error++; 40279edaebfSSam Leffler } else if ((flags & IEEE80211_F_JOIN) == 0) { 40379edaebfSSam Leffler /* 40479edaebfSSam Leffler * Overwrite with the supported rate 40579edaebfSSam Leffler * value so any basic rate bit is set. 40679edaebfSSam Leffler */ 40779edaebfSSam Leffler nrs->rs_rates[i] = srs->rs_rates[rix]; 4081a1e1d21SSam Leffler } 4091a1e1d21SSam Leffler } 41079edaebfSSam Leffler if ((flags & IEEE80211_F_DODEL) && rix < 0) { 4111a1e1d21SSam Leffler /* 4121a1e1d21SSam Leffler * Delete unacceptable rates. 4131a1e1d21SSam Leffler */ 4141a1e1d21SSam Leffler nrs->rs_nrates--; 4151a1e1d21SSam Leffler for (j = i; j < nrs->rs_nrates; j++) 4161a1e1d21SSam Leffler nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 4171a1e1d21SSam Leffler nrs->rs_rates[j] = 0; 4181a1e1d21SSam Leffler continue; 4191a1e1d21SSam Leffler } 42079edaebfSSam Leffler if (rix >= 0) 4211a1e1d21SSam Leffler okrate = nrs->rs_rates[i]; 4221a1e1d21SSam Leffler i++; 4231a1e1d21SSam Leffler } 4248a1b9b6aSSam Leffler if (okrate == 0 || error != 0 || 42568e8e04eSSam Leffler ((flags & IEEE80211_F_DOFRATE) && fixedrate != ic->ic_fixed_rate)) 4261a1e1d21SSam Leffler return badrate | IEEE80211_RATE_BASIC; 4271a1e1d21SSam Leffler else 4281a1e1d21SSam Leffler return RV(okrate); 4291a1e1d21SSam Leffler #undef RV 4301a1e1d21SSam Leffler } 4311a1e1d21SSam Leffler 4328a1b9b6aSSam Leffler /* 4338a1b9b6aSSam Leffler * Reset 11g-related state. 4348a1b9b6aSSam Leffler */ 4358a1b9b6aSSam Leffler void 4368a1b9b6aSSam Leffler ieee80211_reset_erp(struct ieee80211com *ic) 4371a1e1d21SSam Leffler { 4388a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 4398a1b9b6aSSam Leffler ic->ic_nonerpsta = 0; 4408a1b9b6aSSam Leffler ic->ic_longslotsta = 0; 4418a1b9b6aSSam Leffler /* 4428a1b9b6aSSam Leffler * Short slot time is enabled only when operating in 11g 4438a1b9b6aSSam Leffler * and not in an IBSS. We must also honor whether or not 4448a1b9b6aSSam Leffler * the driver is capable of doing it. 4458a1b9b6aSSam Leffler */ 4468a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 44768e8e04eSSam Leffler IEEE80211_IS_CHAN_A(ic->ic_curchan) || 44868e8e04eSSam Leffler IEEE80211_IS_CHAN_HT(ic->ic_curchan) || 44968e8e04eSSam Leffler (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && 4508a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_HOSTAP && 4518a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHSLOT))); 4528a1b9b6aSSam Leffler /* 4538a1b9b6aSSam Leffler * Set short preamble and ERP barker-preamble flags. 4548a1b9b6aSSam Leffler */ 45568e8e04eSSam Leffler if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || 4568a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 4578a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 4588a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEBARKER; 4598a1b9b6aSSam Leffler } else { 4608a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 4618a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEBARKER; 4628a1b9b6aSSam Leffler } 4638a1b9b6aSSam Leffler } 4648a1b9b6aSSam Leffler 4658a1b9b6aSSam Leffler /* 4668a1b9b6aSSam Leffler * Set the short slot time state and notify the driver. 4678a1b9b6aSSam Leffler */ 4688a1b9b6aSSam Leffler void 4698a1b9b6aSSam Leffler ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 4708a1b9b6aSSam Leffler { 4718a1b9b6aSSam Leffler if (onoff) 4728a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHSLOT; 4738a1b9b6aSSam Leffler else 4748a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHSLOT; 4758a1b9b6aSSam Leffler /* notify driver */ 4768a1b9b6aSSam Leffler if (ic->ic_updateslot != NULL) 4778a1b9b6aSSam Leffler ic->ic_updateslot(ic->ic_ifp); 4788a1b9b6aSSam Leffler } 4798a1b9b6aSSam Leffler 4808a1b9b6aSSam Leffler /* 4818a1b9b6aSSam Leffler * Check if the specified rate set supports ERP. 4828a1b9b6aSSam Leffler * NB: the rate set is assumed to be sorted. 4838a1b9b6aSSam Leffler */ 4848a1b9b6aSSam Leffler int 4858a1b9b6aSSam Leffler ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 4868a1b9b6aSSam Leffler { 4878a1b9b6aSSam Leffler #define N(a) (sizeof(a) / sizeof(a[0])) 4888a1b9b6aSSam Leffler static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 4898a1b9b6aSSam Leffler int i, j; 4908a1b9b6aSSam Leffler 4918a1b9b6aSSam Leffler if (rs->rs_nrates < N(rates)) 4928a1b9b6aSSam Leffler return 0; 4938a1b9b6aSSam Leffler for (i = 0; i < N(rates); i++) { 4948a1b9b6aSSam Leffler for (j = 0; j < rs->rs_nrates; j++) { 4958a1b9b6aSSam Leffler int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 4968a1b9b6aSSam Leffler if (rates[i] == r) 4978a1b9b6aSSam Leffler goto next; 4988a1b9b6aSSam Leffler if (r > rates[i]) 4998a1b9b6aSSam Leffler return 0; 5008a1b9b6aSSam Leffler } 5018a1b9b6aSSam Leffler return 0; 5028a1b9b6aSSam Leffler next: 5038a1b9b6aSSam Leffler ; 5048a1b9b6aSSam Leffler } 5058a1b9b6aSSam Leffler return 1; 5068a1b9b6aSSam Leffler #undef N 5078a1b9b6aSSam Leffler } 5088a1b9b6aSSam Leffler 5098a1b9b6aSSam Leffler /* 5108a1b9b6aSSam Leffler * Mark the basic rates for the 11g rate table based on the 5118a1b9b6aSSam Leffler * operating mode. For real 11g we mark all the 11b rates 5128a1b9b6aSSam Leffler * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 5138a1b9b6aSSam Leffler * 11b rates. There's also a pseudo 11a-mode used to mark only 5148a1b9b6aSSam Leffler * the basic OFDM rates. 5158a1b9b6aSSam Leffler */ 5168a1b9b6aSSam Leffler void 5178a1b9b6aSSam Leffler ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 5188a1b9b6aSSam Leffler { 51968e8e04eSSam Leffler static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { 52068e8e04eSSam Leffler { .rs_nrates = 0 }, /* IEEE80211_MODE_AUTO */ 5218a1b9b6aSSam Leffler { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 5228a1b9b6aSSam Leffler { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 5238a1b9b6aSSam Leffler { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 52468e8e04eSSam Leffler { .rs_nrates = 0 }, /* IEEE80211_MODE_FH */ 5258a1b9b6aSSam Leffler /* IEEE80211_MODE_PUREG (not yet) */ 5268a1b9b6aSSam Leffler { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 52768e8e04eSSam Leffler { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11NA */ 52868e8e04eSSam Leffler /* IEEE80211_MODE_11NG (mixed b/g) */ 52968e8e04eSSam Leffler { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 5308a1b9b6aSSam Leffler }; 5318a1b9b6aSSam Leffler int i, j; 5328a1b9b6aSSam Leffler 5338a1b9b6aSSam Leffler for (i = 0; i < rs->rs_nrates; i++) { 5348a1b9b6aSSam Leffler rs->rs_rates[i] &= IEEE80211_RATE_VAL; 5358a1b9b6aSSam Leffler for (j = 0; j < basic[mode].rs_nrates; j++) 5368a1b9b6aSSam Leffler if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 5378a1b9b6aSSam Leffler rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 5388a1b9b6aSSam Leffler break; 5398a1b9b6aSSam Leffler } 5408a1b9b6aSSam Leffler } 5418a1b9b6aSSam Leffler } 5428a1b9b6aSSam Leffler 5438a1b9b6aSSam Leffler /* 5448a1b9b6aSSam Leffler * WME protocol support. The following parameters come from the spec. 5458a1b9b6aSSam Leffler */ 5468a1b9b6aSSam Leffler typedef struct phyParamType { 54768e8e04eSSam Leffler uint8_t aifsn; 54868e8e04eSSam Leffler uint8_t logcwmin; 54968e8e04eSSam Leffler uint8_t logcwmax; 55068e8e04eSSam Leffler uint16_t txopLimit; 55168e8e04eSSam Leffler uint8_t acm; 5528a1b9b6aSSam Leffler } paramType; 5538a1b9b6aSSam Leffler 5548a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 55568e8e04eSSam Leffler { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_AUTO */ 55668e8e04eSSam Leffler { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11A */ 55768e8e04eSSam Leffler { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_11B */ 55868e8e04eSSam Leffler { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11G */ 55968e8e04eSSam Leffler { 3, 5, 7, 0, 0 }, /* IEEE80211_MODE_FH */ 56068e8e04eSSam Leffler { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 56168e8e04eSSam Leffler { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 56268e8e04eSSam Leffler { 2, 3, 5, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 56368e8e04eSSam Leffler { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NA */ /* XXXcheck*/ 56468e8e04eSSam Leffler { 3, 4, 6, 0, 0 }, /* IEEE80211_MODE_11NG */ /* XXXcheck*/ 5658a1b9b6aSSam Leffler }; 5668a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 56768e8e04eSSam Leffler { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 56868e8e04eSSam Leffler { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 56968e8e04eSSam Leffler { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 57068e8e04eSSam Leffler { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 57168e8e04eSSam Leffler { 7, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 57268e8e04eSSam Leffler { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 57368e8e04eSSam Leffler { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 57468e8e04eSSam Leffler { 7, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 57568e8e04eSSam Leffler { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 57668e8e04eSSam Leffler { 7, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 5778a1b9b6aSSam Leffler }; 5788a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 57968e8e04eSSam Leffler { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 58068e8e04eSSam Leffler { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 58168e8e04eSSam Leffler { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ 58268e8e04eSSam Leffler { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 58368e8e04eSSam Leffler { 1, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ 58468e8e04eSSam Leffler { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 58568e8e04eSSam Leffler { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 58668e8e04eSSam Leffler { 1, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 58768e8e04eSSam Leffler { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 58868e8e04eSSam Leffler { 1, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 5898a1b9b6aSSam Leffler }; 5908a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 59168e8e04eSSam Leffler { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 59268e8e04eSSam Leffler { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 59368e8e04eSSam Leffler { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ 59468e8e04eSSam Leffler { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 59568e8e04eSSam Leffler { 1, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ 59668e8e04eSSam Leffler { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 59768e8e04eSSam Leffler { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 59868e8e04eSSam Leffler { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 59968e8e04eSSam Leffler { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 60068e8e04eSSam Leffler { 1, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 6018a1b9b6aSSam Leffler }; 6028a1b9b6aSSam Leffler 6038a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 60468e8e04eSSam Leffler { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_AUTO */ 60568e8e04eSSam Leffler { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11A */ 60668e8e04eSSam Leffler { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_11B */ 60768e8e04eSSam Leffler { 3, 4, 10, 0, 0 }, /* IEEE80211_MODE_11G */ 60868e8e04eSSam Leffler { 3, 5, 10, 0, 0 }, /* IEEE80211_MODE_FH */ 60968e8e04eSSam Leffler { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_A */ 61068e8e04eSSam Leffler { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_TURBO_G */ 61168e8e04eSSam Leffler { 2, 3, 10, 0, 0 }, /* IEEE80211_MODE_STURBO_A */ 61268e8e04eSSam Leffler { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NA */ 61368e8e04eSSam Leffler { 1, 4, 10, 0, 0 }, /* IEEE80211_MODE_11NG */ 6148a1b9b6aSSam Leffler }; 6158a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 61668e8e04eSSam Leffler { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_AUTO */ 61768e8e04eSSam Leffler { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11A */ 61868e8e04eSSam Leffler { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_11B */ 61968e8e04eSSam Leffler { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11G */ 62068e8e04eSSam Leffler { 2, 4, 5, 188, 0 }, /* IEEE80211_MODE_FH */ 62168e8e04eSSam Leffler { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_A */ 62268e8e04eSSam Leffler { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_TURBO_G */ 62368e8e04eSSam Leffler { 2, 2, 3, 94, 0 }, /* IEEE80211_MODE_STURBO_A */ 62468e8e04eSSam Leffler { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NA */ 62568e8e04eSSam Leffler { 2, 3, 4, 94, 0 }, /* IEEE80211_MODE_11NG */ 6268a1b9b6aSSam Leffler }; 6278a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 62868e8e04eSSam Leffler { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_AUTO */ 62968e8e04eSSam Leffler { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11A */ 63068e8e04eSSam Leffler { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_11B */ 63168e8e04eSSam Leffler { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11G */ 63268e8e04eSSam Leffler { 2, 3, 4, 102, 0 }, /* IEEE80211_MODE_FH */ 63368e8e04eSSam Leffler { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_A */ 63468e8e04eSSam Leffler { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_TURBO_G */ 63568e8e04eSSam Leffler { 1, 2, 2, 47, 0 }, /* IEEE80211_MODE_STURBO_A */ 63668e8e04eSSam Leffler { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NA */ 63768e8e04eSSam Leffler { 2, 2, 3, 47, 0 }, /* IEEE80211_MODE_11NG */ 6388a1b9b6aSSam Leffler }; 6398a1b9b6aSSam Leffler 6408a1b9b6aSSam Leffler void 6418a1b9b6aSSam Leffler ieee80211_wme_initparams(struct ieee80211com *ic) 6428a1b9b6aSSam Leffler { 6438a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 6448a1b9b6aSSam Leffler const paramType *pPhyParam, *pBssPhyParam; 6458a1b9b6aSSam Leffler struct wmeParams *wmep; 64668e8e04eSSam Leffler enum ieee80211_phymode mode; 6478a1b9b6aSSam Leffler int i; 6488a1b9b6aSSam Leffler 6498a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_WME) == 0) 6508a1b9b6aSSam Leffler return; 6518a1b9b6aSSam Leffler 65268e8e04eSSam Leffler /* 65368e8e04eSSam Leffler * Select mode; we can be called early in which case we 65468e8e04eSSam Leffler * always use auto mode. We know we'll be called when 65568e8e04eSSam Leffler * entering the RUN state with bsschan setup properly 65668e8e04eSSam Leffler * so state will eventually get set correctly 65768e8e04eSSam Leffler */ 65868e8e04eSSam Leffler if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 65968e8e04eSSam Leffler mode = ieee80211_chan2mode(ic->ic_bsschan); 66068e8e04eSSam Leffler else 66168e8e04eSSam Leffler mode = IEEE80211_MODE_AUTO; 6628a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 6638a1b9b6aSSam Leffler switch (i) { 6648a1b9b6aSSam Leffler case WME_AC_BK: 66568e8e04eSSam Leffler pPhyParam = &phyParamForAC_BK[mode]; 66668e8e04eSSam Leffler pBssPhyParam = &phyParamForAC_BK[mode]; 6678a1b9b6aSSam Leffler break; 6688a1b9b6aSSam Leffler case WME_AC_VI: 66968e8e04eSSam Leffler pPhyParam = &phyParamForAC_VI[mode]; 67068e8e04eSSam Leffler pBssPhyParam = &bssPhyParamForAC_VI[mode]; 6718a1b9b6aSSam Leffler break; 6728a1b9b6aSSam Leffler case WME_AC_VO: 67368e8e04eSSam Leffler pPhyParam = &phyParamForAC_VO[mode]; 67468e8e04eSSam Leffler pBssPhyParam = &bssPhyParamForAC_VO[mode]; 6758a1b9b6aSSam Leffler break; 6768a1b9b6aSSam Leffler case WME_AC_BE: 6778a1b9b6aSSam Leffler default: 67868e8e04eSSam Leffler pPhyParam = &phyParamForAC_BE[mode]; 67968e8e04eSSam Leffler pBssPhyParam = &bssPhyParamForAC_BE[mode]; 6808a1b9b6aSSam Leffler break; 6818a1b9b6aSSam Leffler } 6828a1b9b6aSSam Leffler 6838a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 6848a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 6858a1b9b6aSSam Leffler wmep->wmep_acm = pPhyParam->acm; 6868a1b9b6aSSam Leffler wmep->wmep_aifsn = pPhyParam->aifsn; 6878a1b9b6aSSam Leffler wmep->wmep_logcwmin = pPhyParam->logcwmin; 6888a1b9b6aSSam Leffler wmep->wmep_logcwmax = pPhyParam->logcwmax; 6898a1b9b6aSSam Leffler wmep->wmep_txopLimit = pPhyParam->txopLimit; 6908a1b9b6aSSam Leffler } else { 6918a1b9b6aSSam Leffler wmep->wmep_acm = pBssPhyParam->acm; 6928a1b9b6aSSam Leffler wmep->wmep_aifsn = pBssPhyParam->aifsn; 6938a1b9b6aSSam Leffler wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 6948a1b9b6aSSam Leffler wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 6958a1b9b6aSSam Leffler wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 6968a1b9b6aSSam Leffler 6978a1b9b6aSSam Leffler } 6988a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 6998a1b9b6aSSam Leffler "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 7008a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 7018a1b9b6aSSam Leffler , ieee80211_wme_acnames[i] 7028a1b9b6aSSam Leffler , wmep->wmep_acm 7038a1b9b6aSSam Leffler , wmep->wmep_aifsn 7048a1b9b6aSSam Leffler , wmep->wmep_logcwmin 7058a1b9b6aSSam Leffler , wmep->wmep_logcwmax 7068a1b9b6aSSam Leffler , wmep->wmep_txopLimit 7078a1b9b6aSSam Leffler ); 7088a1b9b6aSSam Leffler 7098a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 7108a1b9b6aSSam Leffler wmep->wmep_acm = pBssPhyParam->acm; 7118a1b9b6aSSam Leffler wmep->wmep_aifsn = pBssPhyParam->aifsn; 7128a1b9b6aSSam Leffler wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 7138a1b9b6aSSam Leffler wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 7148a1b9b6aSSam Leffler wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 7158a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 7168a1b9b6aSSam Leffler "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 7178a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 7188a1b9b6aSSam Leffler , ieee80211_wme_acnames[i] 7198a1b9b6aSSam Leffler , wmep->wmep_acm 7208a1b9b6aSSam Leffler , wmep->wmep_aifsn 7218a1b9b6aSSam Leffler , wmep->wmep_logcwmin 7228a1b9b6aSSam Leffler , wmep->wmep_logcwmax 7238a1b9b6aSSam Leffler , wmep->wmep_txopLimit 7248a1b9b6aSSam Leffler ); 7258a1b9b6aSSam Leffler } 7268a1b9b6aSSam Leffler /* NB: check ic_bss to avoid NULL deref on initial attach */ 7278a1b9b6aSSam Leffler if (ic->ic_bss != NULL) { 7288a1b9b6aSSam Leffler /* 7298a1b9b6aSSam Leffler * Calculate agressive mode switching threshold based 7308a1b9b6aSSam Leffler * on beacon interval. This doesn't need locking since 7318a1b9b6aSSam Leffler * we're only called before entering the RUN state at 7328a1b9b6aSSam Leffler * which point we start sending beacon frames. 7338a1b9b6aSSam Leffler */ 7348a1b9b6aSSam Leffler wme->wme_hipri_switch_thresh = 7358a1b9b6aSSam Leffler (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 7368a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 7378a1b9b6aSSam Leffler } 7388a1b9b6aSSam Leffler } 7398a1b9b6aSSam Leffler 7408a1b9b6aSSam Leffler /* 7418a1b9b6aSSam Leffler * Update WME parameters for ourself and the BSS. 7428a1b9b6aSSam Leffler */ 7438a1b9b6aSSam Leffler void 7448a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(struct ieee80211com *ic) 7458a1b9b6aSSam Leffler { 7468a1b9b6aSSam Leffler static const paramType phyParam[IEEE80211_MODE_MAX] = { 74768e8e04eSSam Leffler { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_AUTO */ 74868e8e04eSSam Leffler { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11A */ 74968e8e04eSSam Leffler { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_11B */ 75068e8e04eSSam Leffler { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11G */ 75168e8e04eSSam Leffler { 2, 5, 10, 64, 0 }, /* IEEE80211_MODE_FH */ 75268e8e04eSSam Leffler { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_A */ 75368e8e04eSSam Leffler { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_TURBO_G */ 75468e8e04eSSam Leffler { 1, 3, 10, 64, 0 }, /* IEEE80211_MODE_STURBO_A */ 75568e8e04eSSam Leffler { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NA */ /*XXXcheck*/ 75668e8e04eSSam Leffler { 2, 4, 10, 64, 0 }, /* IEEE80211_MODE_11NG */ /*XXXcheck*/ 7578a1b9b6aSSam Leffler }; 7588a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 7598a1b9b6aSSam Leffler const struct wmeParams *wmep; 7608a1b9b6aSSam Leffler struct wmeParams *chanp, *bssp; 76168e8e04eSSam Leffler enum ieee80211_phymode mode; 7628a1b9b6aSSam Leffler int i; 7638a1b9b6aSSam Leffler 7648a1b9b6aSSam Leffler /* set up the channel access parameters for the physical device */ 7658a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 7668a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[i]; 7678a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 7688a1b9b6aSSam Leffler chanp->wmep_aifsn = wmep->wmep_aifsn; 7698a1b9b6aSSam Leffler chanp->wmep_logcwmin = wmep->wmep_logcwmin; 7708a1b9b6aSSam Leffler chanp->wmep_logcwmax = wmep->wmep_logcwmax; 7718a1b9b6aSSam Leffler chanp->wmep_txopLimit = wmep->wmep_txopLimit; 7728a1b9b6aSSam Leffler 7738a1b9b6aSSam Leffler chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 7748a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 7758a1b9b6aSSam Leffler chanp->wmep_aifsn = wmep->wmep_aifsn; 7768a1b9b6aSSam Leffler chanp->wmep_logcwmin = wmep->wmep_logcwmin; 7778a1b9b6aSSam Leffler chanp->wmep_logcwmax = wmep->wmep_logcwmax; 7788a1b9b6aSSam Leffler chanp->wmep_txopLimit = wmep->wmep_txopLimit; 7798a1b9b6aSSam Leffler } 7808a1b9b6aSSam Leffler 7818a1b9b6aSSam Leffler /* 78268e8e04eSSam Leffler * Select mode; we can be called early in which case we 78368e8e04eSSam Leffler * always use auto mode. We know we'll be called when 78468e8e04eSSam Leffler * entering the RUN state with bsschan setup properly 78568e8e04eSSam Leffler * so state will eventually get set correctly 78668e8e04eSSam Leffler */ 78768e8e04eSSam Leffler if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) 78868e8e04eSSam Leffler mode = ieee80211_chan2mode(ic->ic_bsschan); 78968e8e04eSSam Leffler else 79068e8e04eSSam Leffler mode = IEEE80211_MODE_AUTO; 79168e8e04eSSam Leffler 79268e8e04eSSam Leffler /* 7938a1b9b6aSSam Leffler * This implements agressive mode as found in certain 7948a1b9b6aSSam Leffler * vendors' AP's. When there is significant high 7958a1b9b6aSSam Leffler * priority (VI/VO) traffic in the BSS throttle back BE 7968a1b9b6aSSam Leffler * traffic by using conservative parameters. Otherwise 7978a1b9b6aSSam Leffler * BE uses agressive params to optimize performance of 7988a1b9b6aSSam Leffler * legacy/non-QoS traffic. 7998a1b9b6aSSam Leffler */ 8008a1b9b6aSSam Leffler if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 801ad262427SSam Leffler (wme->wme_flags & WME_F_AGGRMODE) != 0) || 80259a44035SSam Leffler (ic->ic_opmode == IEEE80211_M_STA && 8038a1b9b6aSSam Leffler (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 8048a1b9b6aSSam Leffler (ic->ic_flags & IEEE80211_F_WME) == 0) { 8058a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 8068a1b9b6aSSam Leffler bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 8078a1b9b6aSSam Leffler 80868e8e04eSSam Leffler chanp->wmep_aifsn = bssp->wmep_aifsn = phyParam[mode].aifsn; 8098a1b9b6aSSam Leffler chanp->wmep_logcwmin = bssp->wmep_logcwmin = 81068e8e04eSSam Leffler phyParam[mode].logcwmin; 8118a1b9b6aSSam Leffler chanp->wmep_logcwmax = bssp->wmep_logcwmax = 81268e8e04eSSam Leffler phyParam[mode].logcwmax; 8138a1b9b6aSSam Leffler chanp->wmep_txopLimit = bssp->wmep_txopLimit = 814c27e4e31SSam Leffler (ic->ic_flags & IEEE80211_F_BURST) ? 81568e8e04eSSam Leffler phyParam[mode].txopLimit : 0; 8168a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 8178a1b9b6aSSam Leffler "%s: %s [acm %u aifsn %u log2(cwmin) %u " 8188a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 8198a1b9b6aSSam Leffler , ieee80211_wme_acnames[WME_AC_BE] 8208a1b9b6aSSam Leffler , chanp->wmep_acm 8218a1b9b6aSSam Leffler , chanp->wmep_aifsn 8228a1b9b6aSSam Leffler , chanp->wmep_logcwmin 8238a1b9b6aSSam Leffler , chanp->wmep_logcwmax 8248a1b9b6aSSam Leffler , chanp->wmep_txopLimit 8258a1b9b6aSSam Leffler ); 8268a1b9b6aSSam Leffler } 8278a1b9b6aSSam Leffler 8288a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 829ad262427SSam Leffler ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 83068e8e04eSSam Leffler static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { 8318a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_AUTO */ 8328a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_11A */ 8338a1b9b6aSSam Leffler 4, /* IEEE80211_MODE_11B */ 8348a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_11G */ 8358a1b9b6aSSam Leffler 4, /* IEEE80211_MODE_FH */ 8368a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_TURBO_A */ 8378a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_TURBO_G */ 83868e8e04eSSam Leffler 3, /* IEEE80211_MODE_STURBO_A */ 83968e8e04eSSam Leffler 3, /* IEEE80211_MODE_11NA */ 84068e8e04eSSam Leffler 3, /* IEEE80211_MODE_11NG */ 8418a1b9b6aSSam Leffler }; 8428a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 8438a1b9b6aSSam Leffler bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 8448a1b9b6aSSam Leffler 84568e8e04eSSam Leffler chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; 8468a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 8478a1b9b6aSSam Leffler "%s: %s log2(cwmin) %u\n", __func__ 8488a1b9b6aSSam Leffler , ieee80211_wme_acnames[WME_AC_BE] 8498a1b9b6aSSam Leffler , chanp->wmep_logcwmin 8508a1b9b6aSSam Leffler ); 8518a1b9b6aSSam Leffler } 8528a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 8538a1b9b6aSSam Leffler /* 8548a1b9b6aSSam Leffler * Arrange for a beacon update and bump the parameter 8558a1b9b6aSSam Leffler * set number so associated stations load the new values. 8568a1b9b6aSSam Leffler */ 8578a1b9b6aSSam Leffler wme->wme_bssChanParams.cap_info = 8588a1b9b6aSSam Leffler (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 8598a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WMEUPDATE; 8608a1b9b6aSSam Leffler } 8618a1b9b6aSSam Leffler 8628a1b9b6aSSam Leffler wme->wme_update(ic); 8638a1b9b6aSSam Leffler 8648a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 8658a1b9b6aSSam Leffler "%s: WME params updated, cap_info 0x%x\n", __func__, 8668a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_STA ? 8678a1b9b6aSSam Leffler wme->wme_wmeChanParams.cap_info : 8688a1b9b6aSSam Leffler wme->wme_bssChanParams.cap_info); 8698a1b9b6aSSam Leffler } 8708a1b9b6aSSam Leffler 8718a1b9b6aSSam Leffler void 8728a1b9b6aSSam Leffler ieee80211_wme_updateparams(struct ieee80211com *ic) 8738a1b9b6aSSam Leffler { 8748a1b9b6aSSam Leffler 8758a1b9b6aSSam Leffler if (ic->ic_caps & IEEE80211_C_WME) { 8768a1b9b6aSSam Leffler IEEE80211_BEACON_LOCK(ic); 8778a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(ic); 8788a1b9b6aSSam Leffler IEEE80211_BEACON_UNLOCK(ic); 8798a1b9b6aSSam Leffler } 8808a1b9b6aSSam Leffler } 8818a1b9b6aSSam Leffler 88268e8e04eSSam Leffler /* 88368e8e04eSSam Leffler * Start a device. If this is the first vap running on the 88468e8e04eSSam Leffler * underlying device then we first bring it up. 88568e8e04eSSam Leffler */ 88668e8e04eSSam Leffler int 88768e8e04eSSam Leffler ieee80211_init(struct ieee80211com *ic, int forcescan) 88868e8e04eSSam Leffler { 88968e8e04eSSam Leffler 89068e8e04eSSam Leffler IEEE80211_DPRINTF(ic, 89168e8e04eSSam Leffler IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 89268e8e04eSSam Leffler "%s\n", "start running"); 89368e8e04eSSam Leffler 89468e8e04eSSam Leffler /* 89568e8e04eSSam Leffler * Kick the 802.11 state machine as appropriate. 89668e8e04eSSam Leffler */ 89768e8e04eSSam Leffler if (ic->ic_roaming != IEEE80211_ROAMING_MANUAL) { 89868e8e04eSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 89968e8e04eSSam Leffler /* 90068e8e04eSSam Leffler * Try to be intelligent about clocking the state 90168e8e04eSSam Leffler * machine. If we're currently in RUN state then 90268e8e04eSSam Leffler * we should be able to apply any new state/parameters 90368e8e04eSSam Leffler * simply by re-associating. Otherwise we need to 90468e8e04eSSam Leffler * re-scan to select an appropriate ap. 90568e8e04eSSam Leffler */ 90668e8e04eSSam Leffler if (ic->ic_state != IEEE80211_S_RUN || forcescan) 90768e8e04eSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 90868e8e04eSSam Leffler else 90968e8e04eSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); 91068e8e04eSSam Leffler } else { 91168e8e04eSSam Leffler /* 91268e8e04eSSam Leffler * For monitor+wds modes there's nothing to do but 91368e8e04eSSam Leffler * start running. Otherwise, if this is the first 91468e8e04eSSam Leffler * vap to be brought up, start a scan which may be 91568e8e04eSSam Leffler * preempted if the station is locked to a particular 91668e8e04eSSam Leffler * channel. 91768e8e04eSSam Leffler */ 91868e8e04eSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR || 91968e8e04eSSam Leffler ic->ic_opmode == IEEE80211_M_WDS) { 92068e8e04eSSam Leffler ic->ic_state = IEEE80211_S_INIT; /* XXX*/ 92168e8e04eSSam Leffler ieee80211_new_state(ic, IEEE80211_S_RUN, -1); 92268e8e04eSSam Leffler } else 92368e8e04eSSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 92468e8e04eSSam Leffler } 92568e8e04eSSam Leffler } 92668e8e04eSSam Leffler return 0; 92768e8e04eSSam Leffler } 92868e8e04eSSam Leffler 92968e8e04eSSam Leffler /* 93068e8e04eSSam Leffler * Switch between turbo and non-turbo operating modes. 93168e8e04eSSam Leffler * Use the specified channel flags to locate the new 93268e8e04eSSam Leffler * channel, update 802.11 state, and then call back into 93368e8e04eSSam Leffler * the driver to effect the change. 93468e8e04eSSam Leffler */ 93568e8e04eSSam Leffler void 93668e8e04eSSam Leffler ieee80211_dturbo_switch(struct ieee80211com *ic, int newflags) 93768e8e04eSSam Leffler { 93868e8e04eSSam Leffler struct ieee80211_channel *chan; 93968e8e04eSSam Leffler 94068e8e04eSSam Leffler chan = ieee80211_find_channel(ic, ic->ic_bsschan->ic_freq, newflags); 94168e8e04eSSam Leffler if (chan == NULL) { /* XXX should not happen */ 94268e8e04eSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 94368e8e04eSSam Leffler "%s: no channel with freq %u flags 0x%x\n", 94468e8e04eSSam Leffler __func__, ic->ic_bsschan->ic_freq, newflags); 94568e8e04eSSam Leffler return; 94668e8e04eSSam Leffler } 94768e8e04eSSam Leffler 94868e8e04eSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_SUPERG, 94968e8e04eSSam Leffler "%s: %s -> %s (freq %u flags 0x%x)\n", __func__, 95068e8e04eSSam Leffler ieee80211_phymode_name[ieee80211_chan2mode(ic->ic_bsschan)], 95168e8e04eSSam Leffler ieee80211_phymode_name[ieee80211_chan2mode(chan)], 95268e8e04eSSam Leffler chan->ic_freq, chan->ic_flags); 95368e8e04eSSam Leffler 95468e8e04eSSam Leffler ic->ic_bsschan = chan; 95568e8e04eSSam Leffler ic->ic_prevchan = ic->ic_curchan; 95668e8e04eSSam Leffler ic->ic_curchan = chan; 95768e8e04eSSam Leffler ic->ic_set_channel(ic); 95868e8e04eSSam Leffler /* NB: do not need to reset ERP state 'cuz we're in sta mode */ 95968e8e04eSSam Leffler } 96068e8e04eSSam Leffler 961e701e041SSam Leffler void 962e701e041SSam Leffler ieee80211_beacon_miss(struct ieee80211com *ic) 963e701e041SSam Leffler { 964e701e041SSam Leffler 965e701e041SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 966e701e041SSam Leffler /* XXX check ic_curchan != ic_bsschan? */ 967e701e041SSam Leffler return; 968e701e041SSam Leffler } 96968e8e04eSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 970e701e041SSam Leffler "%s\n", "beacon miss"); 971e701e041SSam Leffler 972e701e041SSam Leffler /* 973e701e041SSam Leffler * Our handling is only meaningful for stations that are 974e701e041SSam Leffler * associated; any other conditions else will be handled 975e701e041SSam Leffler * through different means (e.g. the tx timeout on mgt frames). 976e701e041SSam Leffler */ 977e701e041SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) 978e701e041SSam Leffler return; 979e701e041SSam Leffler 980e701e041SSam Leffler if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { 981e701e041SSam Leffler /* 982e701e041SSam Leffler * Send a directed probe req before falling back to a scan; 983e701e041SSam Leffler * if we receive a response ic_bmiss_count will be reset. 984e701e041SSam Leffler * Some cards mistakenly report beacon miss so this avoids 985e701e041SSam Leffler * the expensive scan if the ap is still there. 986e701e041SSam Leffler */ 987e701e041SSam Leffler ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, 988e701e041SSam Leffler ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, 989e701e041SSam Leffler ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, 990e701e041SSam Leffler ic->ic_opt_ie, ic->ic_opt_ie_len); 991e701e041SSam Leffler return; 992e701e041SSam Leffler } 993e701e041SSam Leffler ic->ic_bmiss_count = 0; 99468e8e04eSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 99568e8e04eSSam Leffler /* 99668e8e04eSSam Leffler * If we receive a beacon miss interrupt when using 99768e8e04eSSam Leffler * dynamic turbo, attempt to switch modes before 99868e8e04eSSam Leffler * reassociating. 99968e8e04eSSam Leffler */ 100068e8e04eSSam Leffler if (IEEE80211_ATH_CAP(ic, ic->ic_bss, IEEE80211_NODE_TURBOP)) 100168e8e04eSSam Leffler ieee80211_dturbo_switch(ic, 100268e8e04eSSam Leffler ic->ic_bsschan->ic_flags ^ IEEE80211_CHAN_TURBO); 100368e8e04eSSam Leffler /* 100468e8e04eSSam Leffler * Try to reassociate before scanning for a new ap. 100568e8e04eSSam Leffler */ 100668e8e04eSSam Leffler ieee80211_new_state(ic, IEEE80211_S_ASSOC, 1); 100768e8e04eSSam Leffler } else { 100868e8e04eSSam Leffler /* 100968e8e04eSSam Leffler * Somebody else is controlling state changes (e.g. 101068e8e04eSSam Leffler * a user-mode app) don't do anything that would 101168e8e04eSSam Leffler * confuse them; just drop into scan mode so they'll 101268e8e04eSSam Leffler * notified of the state change and given control. 101368e8e04eSSam Leffler */ 1014e701e041SSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 1015e701e041SSam Leffler } 101668e8e04eSSam Leffler } 1017e701e041SSam Leffler 1018e99662a6SSam Leffler /* 1019e99662a6SSam Leffler * Software beacon miss handling. Check if any beacons 1020e99662a6SSam Leffler * were received in the last period. If not post a 1021e99662a6SSam Leffler * beacon miss; otherwise reset the counter. 1022e99662a6SSam Leffler */ 1023e99662a6SSam Leffler static void 1024e99662a6SSam Leffler ieee80211_swbmiss(void *arg) 1025e99662a6SSam Leffler { 1026e99662a6SSam Leffler struct ieee80211com *ic = arg; 1027e99662a6SSam Leffler 1028e99662a6SSam Leffler if (ic->ic_swbmiss_count == 0) { 1029e99662a6SSam Leffler ieee80211_beacon_miss(ic); 1030e99662a6SSam Leffler if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ 1031e99662a6SSam Leffler return; 1032e99662a6SSam Leffler } else 1033e99662a6SSam Leffler ic->ic_swbmiss_count = 0; 1034e99662a6SSam Leffler callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1035e99662a6SSam Leffler ieee80211_swbmiss, ic); 1036e99662a6SSam Leffler } 1037e99662a6SSam Leffler 10387edb8cf9SSam Leffler static void 10397edb8cf9SSam Leffler sta_disassoc(void *arg, struct ieee80211_node *ni) 10407edb8cf9SSam Leffler { 10417edb8cf9SSam Leffler struct ieee80211com *ic = arg; 10427edb8cf9SSam Leffler 10437edb8cf9SSam Leffler if (ni->ni_associd != 0) { 10447edb8cf9SSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 10457edb8cf9SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 10467edb8cf9SSam Leffler ieee80211_node_leave(ic, ni); 10477edb8cf9SSam Leffler } 10487edb8cf9SSam Leffler } 10497edb8cf9SSam Leffler 10507edb8cf9SSam Leffler static void 10517edb8cf9SSam Leffler sta_deauth(void *arg, struct ieee80211_node *ni) 10527edb8cf9SSam Leffler { 10537edb8cf9SSam Leffler struct ieee80211com *ic = arg; 10547edb8cf9SSam Leffler 10557edb8cf9SSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 10567edb8cf9SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 10577edb8cf9SSam Leffler } 10587edb8cf9SSam Leffler 105968e8e04eSSam Leffler /* 106068e8e04eSSam Leffler * Handle deauth with reason. We retry only for 106168e8e04eSSam Leffler * the cases where we might succeed. Otherwise 106268e8e04eSSam Leffler * we downgrade the ap and scan. 106368e8e04eSSam Leffler */ 106468e8e04eSSam Leffler static void 106568e8e04eSSam Leffler sta_authretry(struct ieee80211com *ic, struct ieee80211_node *ni, int reason) 106668e8e04eSSam Leffler { 106768e8e04eSSam Leffler switch (reason) { 106868e8e04eSSam Leffler case IEEE80211_STATUS_TIMEOUT: 106968e8e04eSSam Leffler case IEEE80211_REASON_ASSOC_EXPIRE: 107068e8e04eSSam Leffler case IEEE80211_REASON_NOT_AUTHED: 107168e8e04eSSam Leffler case IEEE80211_REASON_NOT_ASSOCED: 107268e8e04eSSam Leffler case IEEE80211_REASON_ASSOC_LEAVE: 107368e8e04eSSam Leffler case IEEE80211_REASON_ASSOC_NOT_AUTHED: 107468e8e04eSSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_AUTH, 1); 107568e8e04eSSam Leffler break; 107668e8e04eSSam Leffler default: 107768e8e04eSSam Leffler ieee80211_scan_assoc_fail(ic, ic->ic_bss->ni_macaddr, reason); 107868e8e04eSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 107968e8e04eSSam Leffler ieee80211_check_scan(ic, 108068e8e04eSSam Leffler IEEE80211_SCAN_ACTIVE, 108168e8e04eSSam Leffler IEEE80211_SCAN_FOREVER, 108268e8e04eSSam Leffler ic->ic_des_nssid, ic->ic_des_ssid); 108368e8e04eSSam Leffler break; 108468e8e04eSSam Leffler } 108568e8e04eSSam Leffler } 108668e8e04eSSam Leffler 10878a1b9b6aSSam Leffler static int 10888a1b9b6aSSam Leffler ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 10898a1b9b6aSSam Leffler { 10908a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 10911a1e1d21SSam Leffler struct ieee80211_node *ni; 1092a11c9a5cSSam Leffler enum ieee80211_state ostate; 10931a1e1d21SSam Leffler 10941a1e1d21SSam Leffler ostate = ic->ic_state; 10958a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 10968a1b9b6aSSam Leffler ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 1097a11c9a5cSSam Leffler ic->ic_state = nstate; /* state transition */ 109868e8e04eSSam Leffler callout_stop(&ic->ic_mgtsend); /* XXX callout_drain */ 109968e8e04eSSam Leffler if (ostate != IEEE80211_S_SCAN) 110068e8e04eSSam Leffler ieee80211_cancel_scan(ic); /* background scan */ 11011a1e1d21SSam Leffler ni = ic->ic_bss; /* NB: no reference held */ 1102e99662a6SSam Leffler if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) 1103e99662a6SSam Leffler callout_stop(&ic->ic_swbmiss); 11041a1e1d21SSam Leffler switch (nstate) { 11051a1e1d21SSam Leffler case IEEE80211_S_INIT: 11061a1e1d21SSam Leffler switch (ostate) { 11071a1e1d21SSam Leffler case IEEE80211_S_INIT: 11081a1e1d21SSam Leffler break; 11091a1e1d21SSam Leffler case IEEE80211_S_RUN: 11101a1e1d21SSam Leffler switch (ic->ic_opmode) { 11111a1e1d21SSam Leffler case IEEE80211_M_STA: 11121a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 11131a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 11141a1e1d21SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 11158a1b9b6aSSam Leffler ieee80211_sta_leave(ic, ni); 11161a1e1d21SSam Leffler break; 11171a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 11187edb8cf9SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 11197edb8cf9SSam Leffler sta_disassoc, ic); 11201a1e1d21SSam Leffler break; 11211a1e1d21SSam Leffler default: 11221a1e1d21SSam Leffler break; 11231a1e1d21SSam Leffler } 1124915f1482SSam Leffler break; 11251a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 11261a1e1d21SSam Leffler switch (ic->ic_opmode) { 11271a1e1d21SSam Leffler case IEEE80211_M_STA: 11281a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 11291a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 11301a1e1d21SSam Leffler IEEE80211_REASON_AUTH_LEAVE); 11311a1e1d21SSam Leffler break; 11321a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 11337edb8cf9SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 11347edb8cf9SSam Leffler sta_deauth, ic); 11351a1e1d21SSam Leffler break; 11361a1e1d21SSam Leffler default: 11371a1e1d21SSam Leffler break; 11381a1e1d21SSam Leffler } 1139915f1482SSam Leffler break; 11401a1e1d21SSam Leffler case IEEE80211_S_SCAN: 1141c75ac469SSam Leffler ieee80211_cancel_scan(ic); 11421a1e1d21SSam Leffler break; 1143915f1482SSam Leffler case IEEE80211_S_AUTH: 1144915f1482SSam Leffler break; 1145915f1482SSam Leffler } 1146915f1482SSam Leffler if (ostate != IEEE80211_S_INIT) { 1147915f1482SSam Leffler /* NB: optimize INIT -> INIT case */ 1148915f1482SSam Leffler ieee80211_drain_ifq(&ic->ic_mgtq); 1149915f1482SSam Leffler ieee80211_reset_bss(ic); 115068e8e04eSSam Leffler ieee80211_scan_flush(ic); 11511a1e1d21SSam Leffler } 11528a1b9b6aSSam Leffler if (ic->ic_auth->ia_detach != NULL) 11538a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 11541a1e1d21SSam Leffler break; 11551a1e1d21SSam Leffler case IEEE80211_S_SCAN: 11561a1e1d21SSam Leffler switch (ostate) { 11571a1e1d21SSam Leffler case IEEE80211_S_INIT: 115868e8e04eSSam Leffler createibss: 11598a1b9b6aSSam Leffler if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 11608a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_IBSS || 11618a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_AHDEMO) && 11621a1e1d21SSam Leffler ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 11631a1e1d21SSam Leffler /* 116468e8e04eSSam Leffler * Already have a channel; bypass the 116568e8e04eSSam Leffler * scan and startup immediately. Because 116668e8e04eSSam Leffler * of this explicitly sync the scanner state. 11671a1e1d21SSam Leffler */ 116868e8e04eSSam Leffler ieee80211_scan_update(ic); 11691a1e1d21SSam Leffler ieee80211_create_ibss(ic, ic->ic_des_chan); 11701a1e1d21SSam Leffler } else { 117168e8e04eSSam Leffler ieee80211_check_scan(ic, 117268e8e04eSSam Leffler IEEE80211_SCAN_ACTIVE | 117368e8e04eSSam Leffler IEEE80211_SCAN_FLUSH, 117468e8e04eSSam Leffler IEEE80211_SCAN_FOREVER, 117568e8e04eSSam Leffler ic->ic_des_nssid, ic->ic_des_ssid); 11761a1e1d21SSam Leffler } 11771a1e1d21SSam Leffler break; 11781a1e1d21SSam Leffler case IEEE80211_S_SCAN: 11791a1e1d21SSam Leffler case IEEE80211_S_AUTH: 11801a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 118168e8e04eSSam Leffler /* 118268e8e04eSSam Leffler * These can happen either because of a timeout 118368e8e04eSSam Leffler * on an assoc/auth response or because of a 118468e8e04eSSam Leffler * change in state that requires a reset. For 118568e8e04eSSam Leffler * the former we're called with a non-zero arg 118668e8e04eSSam Leffler * that is the cause for the failure; pass this 118768e8e04eSSam Leffler * to the scan code so it can update state. 118868e8e04eSSam Leffler * Otherwise trigger a new scan unless we're in 118968e8e04eSSam Leffler * manual roaming mode in which case an application 119068e8e04eSSam Leffler * must issue an explicit scan request. 119168e8e04eSSam Leffler */ 119268e8e04eSSam Leffler if (arg != 0) 119368e8e04eSSam Leffler ieee80211_scan_assoc_fail(ic, 119468e8e04eSSam Leffler ic->ic_bss->ni_macaddr, arg); 1195ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 119668e8e04eSSam Leffler ieee80211_check_scan(ic, 119768e8e04eSSam Leffler IEEE80211_SCAN_ACTIVE, 119868e8e04eSSam Leffler IEEE80211_SCAN_FOREVER, 119968e8e04eSSam Leffler ic->ic_des_nssid, ic->ic_des_ssid); 120068e8e04eSSam Leffler break; 120168e8e04eSSam Leffler case IEEE80211_S_RUN: /* beacon miss */ 120268e8e04eSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 120368e8e04eSSam Leffler ieee80211_sta_leave(ic, ni); 120468e8e04eSSam Leffler ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 120568e8e04eSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 120668e8e04eSSam Leffler ieee80211_check_scan(ic, 120768e8e04eSSam Leffler IEEE80211_SCAN_ACTIVE, 120868e8e04eSSam Leffler IEEE80211_SCAN_FOREVER, 120968e8e04eSSam Leffler ic->ic_des_nssid, 121068e8e04eSSam Leffler ic->ic_des_ssid); 121168e8e04eSSam Leffler } else { 121268e8e04eSSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 121368e8e04eSSam Leffler sta_disassoc, ic); 121468e8e04eSSam Leffler goto createibss; 121568e8e04eSSam Leffler } 12161a1e1d21SSam Leffler break; 12171a1e1d21SSam Leffler } 12181a1e1d21SSam Leffler break; 12191a1e1d21SSam Leffler case IEEE80211_S_AUTH: 122068e8e04eSSam Leffler KASSERT(ic->ic_opmode == IEEE80211_M_STA, 122168e8e04eSSam Leffler ("switch to %s state when operating in mode %u", 122268e8e04eSSam Leffler ieee80211_state_name[nstate], ic->ic_opmode)); 12231a1e1d21SSam Leffler switch (ostate) { 12241a1e1d21SSam Leffler case IEEE80211_S_INIT: 12251a1e1d21SSam Leffler case IEEE80211_S_SCAN: 12261a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 12271a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1); 12281a1e1d21SSam Leffler break; 12291a1e1d21SSam Leffler case IEEE80211_S_AUTH: 12301a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 123168e8e04eSSam Leffler switch (arg & 0xff) { 12321a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 12331a1e1d21SSam Leffler /* ??? */ 12341a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 12351a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2); 12361a1e1d21SSam Leffler break; 12371a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 123868e8e04eSSam Leffler sta_authretry(ic, ni, arg>>8); 12391a1e1d21SSam Leffler break; 12401a1e1d21SSam Leffler } 12411a1e1d21SSam Leffler break; 12421a1e1d21SSam Leffler case IEEE80211_S_RUN: 124368e8e04eSSam Leffler switch (arg & 0xff) { 12441a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 12451a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 12461a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2); 12471a1e1d21SSam Leffler ic->ic_state = ostate; /* stay RUN */ 12481a1e1d21SSam Leffler break; 12491a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 1250ae8880fdSSam Leffler ieee80211_sta_leave(ic, ni); 1251ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 12521a1e1d21SSam Leffler /* try to reauth */ 12531a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 12541a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1); 1255ae8880fdSSam Leffler } 12561a1e1d21SSam Leffler break; 12571a1e1d21SSam Leffler } 12581a1e1d21SSam Leffler break; 12591a1e1d21SSam Leffler } 12601a1e1d21SSam Leffler break; 12611a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 126268e8e04eSSam Leffler KASSERT(ic->ic_opmode == IEEE80211_M_STA, 126368e8e04eSSam Leffler ("switch to %s state when operating in mode %u", 126468e8e04eSSam Leffler ieee80211_state_name[nstate], ic->ic_opmode)); 12651a1e1d21SSam Leffler switch (ostate) { 12661a1e1d21SSam Leffler case IEEE80211_S_INIT: 12671a1e1d21SSam Leffler case IEEE80211_S_SCAN: 12688a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 12698a1b9b6aSSam Leffler "%s: invalid transition\n", __func__); 12701a1e1d21SSam Leffler break; 12711a1e1d21SSam Leffler case IEEE80211_S_AUTH: 127268e8e04eSSam Leffler case IEEE80211_S_ASSOC: 12731a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 12741a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 12751a1e1d21SSam Leffler break; 12761a1e1d21SSam Leffler case IEEE80211_S_RUN: 1277ae8880fdSSam Leffler ieee80211_sta_leave(ic, ni); 1278ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 127968e8e04eSSam Leffler IEEE80211_SEND_MGMT(ic, ni, arg ? 128068e8e04eSSam Leffler IEEE80211_FC0_SUBTYPE_REASSOC_REQ : 128168e8e04eSSam Leffler IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 1282ae8880fdSSam Leffler } 12831a1e1d21SSam Leffler break; 12841a1e1d21SSam Leffler } 12851a1e1d21SSam Leffler break; 12861a1e1d21SSam Leffler case IEEE80211_S_RUN: 12878a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA) { 12888a1b9b6aSSam Leffler /* XXX validate prerequisites */ 12898a1b9b6aSSam Leffler } 12901a1e1d21SSam Leffler switch (ostate) { 12911a1e1d21SSam Leffler case IEEE80211_S_INIT: 129268e8e04eSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR || 129368e8e04eSSam Leffler ic->ic_opmode == IEEE80211_M_WDS || 129468e8e04eSSam Leffler ic->ic_opmode == IEEE80211_M_HOSTAP) { 129568e8e04eSSam Leffler /* 129668e8e04eSSam Leffler * Already have a channel; bypass the 129768e8e04eSSam Leffler * scan and startup immediately. Because 129868e8e04eSSam Leffler * of this explicitly sync the scanner state. 129968e8e04eSSam Leffler */ 130068e8e04eSSam Leffler ieee80211_scan_update(ic); 130168e8e04eSSam Leffler ieee80211_create_ibss(ic, ic->ic_curchan); 13028a1b9b6aSSam Leffler break; 130368e8e04eSSam Leffler } 13048a1b9b6aSSam Leffler /* fall thru... */ 13051a1e1d21SSam Leffler case IEEE80211_S_AUTH: 13068a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 13078a1b9b6aSSam Leffler "%s: invalid transition\n", __func__); 13082c21ffc8SSam Leffler /* fall thru... */ 13092c21ffc8SSam Leffler case IEEE80211_S_RUN: 13101a1e1d21SSam Leffler break; 13111a1e1d21SSam Leffler case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 13121a1e1d21SSam Leffler case IEEE80211_S_ASSOC: /* infra mode */ 13131a1e1d21SSam Leffler KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 13141a1e1d21SSam Leffler ("%s: bogus xmit rate %u setup\n", __func__, 13151a1e1d21SSam Leffler ni->ni_txrate)); 13168a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 13178a1b9b6aSSam Leffler if (ieee80211_msg_debug(ic)) { 13181a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 13198a1b9b6aSSam Leffler if_printf(ifp, "associated "); 13201a1e1d21SSam Leffler else 13218a1b9b6aSSam Leffler if_printf(ifp, "synchronized "); 13221a1e1d21SSam Leffler printf("with %s ssid ", 13231a1e1d21SSam Leffler ether_sprintf(ni->ni_bssid)); 13241a1e1d21SSam Leffler ieee80211_print_essid(ic->ic_bss->ni_essid, 13251a1e1d21SSam Leffler ni->ni_esslen); 13261a1e1d21SSam Leffler printf(" channel %d start %uMb\n", 1327b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 13281a1e1d21SSam Leffler IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 13291a1e1d21SSam Leffler } 13308a1b9b6aSSam Leffler #endif 133168e8e04eSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) { 133268e8e04eSSam Leffler ieee80211_scan_assoc_success(ic, 133368e8e04eSSam Leffler ni->ni_macaddr); 13348a1b9b6aSSam Leffler ieee80211_notify_node_join(ic, ni, 13358a1b9b6aSSam Leffler arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 133668e8e04eSSam Leffler } 13378a1b9b6aSSam Leffler if_start(ifp); /* XXX not authorized yet */ 13381a1e1d21SSam Leffler break; 13391a1e1d21SSam Leffler } 1340e99662a6SSam Leffler if (ostate != IEEE80211_S_RUN && 1341e99662a6SSam Leffler ic->ic_opmode == IEEE80211_M_STA && 1342e99662a6SSam Leffler (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { 1343e99662a6SSam Leffler /* 1344e99662a6SSam Leffler * Start s/w beacon miss timer for devices w/o 1345e99662a6SSam Leffler * hardware support. We fudge a bit here since 1346e99662a6SSam Leffler * we're doing this in software. 1347e99662a6SSam Leffler */ 1348e99662a6SSam Leffler ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( 1349e99662a6SSam Leffler 2 * ic->ic_bmissthreshold * ni->ni_intval); 1350e99662a6SSam Leffler ic->ic_swbmiss_count = 0; 1351e99662a6SSam Leffler callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1352e99662a6SSam Leffler ieee80211_swbmiss, ic); 1353e99662a6SSam Leffler } 13548a1b9b6aSSam Leffler /* 13558a1b9b6aSSam Leffler * Start/stop the authenticator when operating as an 13568a1b9b6aSSam Leffler * AP. We delay until here to allow configuration to 13578a1b9b6aSSam Leffler * happen out of order. 13588a1b9b6aSSam Leffler */ 13598a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 13608a1b9b6aSSam Leffler ic->ic_auth->ia_attach != NULL) { 13618a1b9b6aSSam Leffler /* XXX check failure */ 13628a1b9b6aSSam Leffler ic->ic_auth->ia_attach(ic); 13638a1b9b6aSSam Leffler } else if (ic->ic_auth->ia_detach != NULL) { 13648a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 13658a1b9b6aSSam Leffler } 13668a1b9b6aSSam Leffler /* 13678a1b9b6aSSam Leffler * When 802.1x is not in use mark the port authorized 13688a1b9b6aSSam Leffler * at this point so traffic can flow. 13698a1b9b6aSSam Leffler */ 13708a1b9b6aSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1371e4918ecdSSam Leffler ieee80211_node_authorize(ni); 13728a1b9b6aSSam Leffler /* 13738a1b9b6aSSam Leffler * Enable inactivity processing. 13748a1b9b6aSSam Leffler * XXX 13758a1b9b6aSSam Leffler */ 137668e8e04eSSam Leffler callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, 137768e8e04eSSam Leffler ieee80211_node_timeout, ic); 13781a1e1d21SSam Leffler break; 13791a1e1d21SSam Leffler } 13801a1e1d21SSam Leffler return 0; 13811a1e1d21SSam Leffler } 1382