11a1e1d21SSam Leffler /*- 27535e66aSSam Leffler * Copyright (c) 2001 Atsushi Onoe 31f1d7810SSam Leffler * Copyright (c) 2002-2005 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. 147535e66aSSam Leffler * 3. The name of the author may not be used to endorse or promote products 157535e66aSSam Leffler * derived from this software without specific prior written permission. 161a1e1d21SSam Leffler * 171a1e1d21SSam Leffler * Alternatively, this software may be distributed under the terms of the 181a1e1d21SSam Leffler * GNU General Public License ("GPL") version 2 as published by the Free 191a1e1d21SSam Leffler * Software Foundation. 201a1e1d21SSam Leffler * 217535e66aSSam Leffler * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 227535e66aSSam Leffler * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 237535e66aSSam Leffler * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 247535e66aSSam Leffler * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 257535e66aSSam Leffler * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 267535e66aSSam Leffler * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 277535e66aSSam Leffler * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 287535e66aSSam Leffler * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 297535e66aSSam Leffler * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 307535e66aSSam Leffler * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 311a1e1d21SSam Leffler */ 321a1e1d21SSam Leffler 331a1e1d21SSam Leffler #include <sys/cdefs.h> 341a1e1d21SSam Leffler __FBSDID("$FreeBSD$"); 351a1e1d21SSam Leffler 361a1e1d21SSam Leffler /* 371a1e1d21SSam Leffler * IEEE 802.11 protocol support. 381a1e1d21SSam Leffler */ 391a1e1d21SSam Leffler 401a1e1d21SSam Leffler #include "opt_inet.h" 411a1e1d21SSam Leffler 421a1e1d21SSam Leffler #include <sys/param.h> 431a1e1d21SSam Leffler #include <sys/kernel.h> 448a1b9b6aSSam Leffler #include <sys/systm.h> 451a1e1d21SSam Leffler 468a1b9b6aSSam Leffler #include <sys/socket.h> 471a1e1d21SSam Leffler 481a1e1d21SSam Leffler #include <net/if.h> 491a1e1d21SSam Leffler #include <net/if_media.h> 508a1b9b6aSSam Leffler #include <net/ethernet.h> /* XXX for ether_sprintf */ 511a1e1d21SSam Leffler 521a1e1d21SSam Leffler #include <net80211/ieee80211_var.h> 531a1e1d21SSam Leffler 548a1b9b6aSSam Leffler /* XXX tunables */ 558a1b9b6aSSam Leffler #define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ 568a1b9b6aSSam Leffler #define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ 571a1e1d21SSam Leffler 581a1e1d21SSam Leffler #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) 591a1e1d21SSam Leffler 601a1e1d21SSam Leffler const char *ieee80211_mgt_subtype_name[] = { 611a1e1d21SSam Leffler "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", 621a1e1d21SSam Leffler "probe_req", "probe_resp", "reserved#6", "reserved#7", 631a1e1d21SSam Leffler "beacon", "atim", "disassoc", "auth", 641a1e1d21SSam Leffler "deauth", "reserved#13", "reserved#14", "reserved#15" 651a1e1d21SSam Leffler }; 668a1b9b6aSSam Leffler const char *ieee80211_ctl_subtype_name[] = { 678a1b9b6aSSam Leffler "reserved#0", "reserved#1", "reserved#2", "reserved#3", 688a1b9b6aSSam Leffler "reserved#3", "reserved#5", "reserved#6", "reserved#7", 698a1b9b6aSSam Leffler "reserved#8", "reserved#9", "ps_poll", "rts", 708a1b9b6aSSam Leffler "cts", "ack", "cf_end", "cf_end_ack" 718a1b9b6aSSam Leffler }; 7249aa47d6SSam Leffler const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { 7349aa47d6SSam Leffler "IBSS", /* IEEE80211_M_IBSS */ 7449aa47d6SSam Leffler "STA", /* IEEE80211_M_STA */ 7549aa47d6SSam Leffler "#2", 7649aa47d6SSam Leffler "AHDEMO", /* IEEE80211_M_AHDEMO */ 7749aa47d6SSam Leffler "#4", "#5", 7849aa47d6SSam Leffler "HOSTAP", /* IEEE80211_M_HOSTAP */ 7949aa47d6SSam Leffler "#7", 8049aa47d6SSam Leffler "MONITOR" /* IEEE80211_M_MONITOR */ 8149aa47d6SSam Leffler }; 82a11c9a5cSSam Leffler const char *ieee80211_state_name[IEEE80211_S_MAX] = { 83a11c9a5cSSam Leffler "INIT", /* IEEE80211_S_INIT */ 84a11c9a5cSSam Leffler "SCAN", /* IEEE80211_S_SCAN */ 85a11c9a5cSSam Leffler "AUTH", /* IEEE80211_S_AUTH */ 86a11c9a5cSSam Leffler "ASSOC", /* IEEE80211_S_ASSOC */ 87a11c9a5cSSam Leffler "RUN" /* IEEE80211_S_RUN */ 88a11c9a5cSSam Leffler }; 898a1b9b6aSSam Leffler const char *ieee80211_wme_acnames[] = { 908a1b9b6aSSam Leffler "WME_AC_BE", 918a1b9b6aSSam Leffler "WME_AC_BK", 928a1b9b6aSSam Leffler "WME_AC_VI", 938a1b9b6aSSam Leffler "WME_AC_VO", 948a1b9b6aSSam Leffler "WME_UPSD", 958a1b9b6aSSam Leffler }; 96a11c9a5cSSam Leffler 97a11c9a5cSSam Leffler static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 981a1e1d21SSam Leffler 991a1e1d21SSam Leffler void 1008a1b9b6aSSam Leffler ieee80211_proto_attach(struct ieee80211com *ic) 1011a1e1d21SSam Leffler { 1028a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 1031a1e1d21SSam Leffler 1048a1b9b6aSSam Leffler /* XXX room for crypto */ 1058a1b9b6aSSam Leffler ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 1061a1e1d21SSam Leffler 1071a1e1d21SSam Leffler ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 10833acb1ceSSam Leffler ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 1092c39b32cSSam Leffler ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 110e701e041SSam Leffler ic->ic_bmiss_max = IEEE80211_BMISS_MAX; 111e99662a6SSam Leffler callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); 11264353cb0SSam Leffler ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; 1132e79ca97SSam Leffler ic->ic_protmode = IEEE80211_PROT_CTSONLY; 1148a1b9b6aSSam Leffler ic->ic_roaming = IEEE80211_ROAMING_AUTO; 1158a1b9b6aSSam Leffler 1168a1b9b6aSSam Leffler ic->ic_wme.wme_hipri_switch_hysteresis = 1178a1b9b6aSSam Leffler AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 1181a1e1d21SSam Leffler 1199bf40edeSBrooks Davis mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 1201a1e1d21SSam Leffler 121a11c9a5cSSam Leffler /* protocol state change handler */ 122a11c9a5cSSam Leffler ic->ic_newstate = ieee80211_newstate; 123a11c9a5cSSam Leffler 1241a1e1d21SSam Leffler /* initialize management frame handlers */ 1251a1e1d21SSam Leffler ic->ic_recv_mgmt = ieee80211_recv_mgmt; 1261a1e1d21SSam Leffler ic->ic_send_mgmt = ieee80211_send_mgmt; 127246b5467SSam Leffler ic->ic_raw_xmit = ieee80211_raw_xmit; 1281a1e1d21SSam Leffler } 1291a1e1d21SSam Leffler 1301a1e1d21SSam Leffler void 1318a1b9b6aSSam Leffler ieee80211_proto_detach(struct ieee80211com *ic) 1321a1e1d21SSam Leffler { 1338a1b9b6aSSam Leffler 1348a1b9b6aSSam Leffler /* 1358a1b9b6aSSam Leffler * This should not be needed as we detach when reseting 1368a1b9b6aSSam Leffler * the state but be conservative here since the 1378a1b9b6aSSam Leffler * authenticator may do things like spawn kernel threads. 1388a1b9b6aSSam Leffler */ 1398a1b9b6aSSam Leffler if (ic->ic_auth->ia_detach) 1408a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 1411a1e1d21SSam Leffler 142915f1482SSam Leffler ieee80211_drain_ifq(&ic->ic_mgtq); 1431a1e1d21SSam Leffler mtx_destroy(&ic->ic_mgtq.ifq_mtx); 1448a1b9b6aSSam Leffler 1458a1b9b6aSSam Leffler /* 1468a1b9b6aSSam Leffler * Detach any ACL'ator. 1478a1b9b6aSSam Leffler */ 1488a1b9b6aSSam Leffler if (ic->ic_acl != NULL) 1498a1b9b6aSSam Leffler ic->ic_acl->iac_detach(ic); 1508a1b9b6aSSam Leffler } 1518a1b9b6aSSam Leffler 1528a1b9b6aSSam Leffler /* 1538a1b9b6aSSam Leffler * Simple-minded authenticator module support. 1548a1b9b6aSSam Leffler */ 1558a1b9b6aSSam Leffler 1568a1b9b6aSSam Leffler #define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 1578a1b9b6aSSam Leffler /* XXX well-known names */ 1588a1b9b6aSSam Leffler static const char *auth_modnames[IEEE80211_AUTH_MAX] = { 1598a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_NONE */ 1608a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_OPEN */ 1618a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_SHARED */ 1628a1b9b6aSSam Leffler "wlan_xauth", /* IEEE80211_AUTH_8021X */ 1638a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_AUTO */ 1648a1b9b6aSSam Leffler "wlan_xauth", /* IEEE80211_AUTH_WPA */ 1658a1b9b6aSSam Leffler }; 1668a1b9b6aSSam Leffler static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 1678a1b9b6aSSam Leffler 1688a1b9b6aSSam Leffler static const struct ieee80211_authenticator auth_internal = { 1698a1b9b6aSSam Leffler .ia_name = "wlan_internal", 1708a1b9b6aSSam Leffler .ia_attach = NULL, 1718a1b9b6aSSam Leffler .ia_detach = NULL, 1728a1b9b6aSSam Leffler .ia_node_join = NULL, 1738a1b9b6aSSam Leffler .ia_node_leave = NULL, 1748a1b9b6aSSam Leffler }; 1758a1b9b6aSSam Leffler 1768a1b9b6aSSam Leffler /* 1778a1b9b6aSSam Leffler * Setup internal authenticators once; they are never unregistered. 1788a1b9b6aSSam Leffler */ 1798a1b9b6aSSam Leffler static void 1808a1b9b6aSSam Leffler ieee80211_auth_setup(void) 1818a1b9b6aSSam Leffler { 1828a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 1838a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 1848a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 1858a1b9b6aSSam Leffler } 1868a1b9b6aSSam Leffler SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 1878a1b9b6aSSam Leffler 1888a1b9b6aSSam Leffler const struct ieee80211_authenticator * 1898a1b9b6aSSam Leffler ieee80211_authenticator_get(int auth) 1908a1b9b6aSSam Leffler { 1918a1b9b6aSSam Leffler if (auth >= IEEE80211_AUTH_MAX) 1928a1b9b6aSSam Leffler return NULL; 1938a1b9b6aSSam Leffler if (authenticators[auth] == NULL) 1948a1b9b6aSSam Leffler ieee80211_load_module(auth_modnames[auth]); 1958a1b9b6aSSam Leffler return authenticators[auth]; 1961a1e1d21SSam Leffler } 1971a1e1d21SSam Leffler 1981a1e1d21SSam Leffler void 1998a1b9b6aSSam Leffler ieee80211_authenticator_register(int type, 2008a1b9b6aSSam Leffler const struct ieee80211_authenticator *auth) 2011a1e1d21SSam Leffler { 2028a1b9b6aSSam Leffler if (type >= IEEE80211_AUTH_MAX) 2038a1b9b6aSSam Leffler return; 2048a1b9b6aSSam Leffler authenticators[type] = auth; 2058a1b9b6aSSam Leffler } 2068a1b9b6aSSam Leffler 2078a1b9b6aSSam Leffler void 2088a1b9b6aSSam Leffler ieee80211_authenticator_unregister(int type) 2098a1b9b6aSSam Leffler { 2108a1b9b6aSSam Leffler 2118a1b9b6aSSam Leffler if (type >= IEEE80211_AUTH_MAX) 2128a1b9b6aSSam Leffler return; 2138a1b9b6aSSam Leffler authenticators[type] = NULL; 2148a1b9b6aSSam Leffler } 2158a1b9b6aSSam Leffler 2168a1b9b6aSSam Leffler /* 2178a1b9b6aSSam Leffler * Very simple-minded ACL module support. 2188a1b9b6aSSam Leffler */ 2198a1b9b6aSSam Leffler /* XXX just one for now */ 2208a1b9b6aSSam Leffler static const struct ieee80211_aclator *acl = NULL; 2218a1b9b6aSSam Leffler 2228a1b9b6aSSam Leffler void 2238a1b9b6aSSam Leffler ieee80211_aclator_register(const struct ieee80211_aclator *iac) 2248a1b9b6aSSam Leffler { 2258a1b9b6aSSam Leffler printf("wlan: %s acl policy registered\n", iac->iac_name); 2268a1b9b6aSSam Leffler acl = iac; 2278a1b9b6aSSam Leffler } 2288a1b9b6aSSam Leffler 2298a1b9b6aSSam Leffler void 2308a1b9b6aSSam Leffler ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 2318a1b9b6aSSam Leffler { 2328a1b9b6aSSam Leffler if (acl == iac) 2338a1b9b6aSSam Leffler acl = NULL; 2348a1b9b6aSSam Leffler printf("wlan: %s acl policy unregistered\n", iac->iac_name); 2358a1b9b6aSSam Leffler } 2368a1b9b6aSSam Leffler 2378a1b9b6aSSam Leffler const struct ieee80211_aclator * 2388a1b9b6aSSam Leffler ieee80211_aclator_get(const char *name) 2398a1b9b6aSSam Leffler { 2408a1b9b6aSSam Leffler if (acl == NULL) 2418a1b9b6aSSam Leffler ieee80211_load_module("wlan_acl"); 2428a1b9b6aSSam Leffler return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 2438a1b9b6aSSam Leffler } 2448a1b9b6aSSam Leffler 2458a1b9b6aSSam Leffler void 2468a1b9b6aSSam Leffler ieee80211_print_essid(const u_int8_t *essid, int len) 2478a1b9b6aSSam Leffler { 2488a1b9b6aSSam Leffler const u_int8_t *p; 2491a1e1d21SSam Leffler int i; 2501a1e1d21SSam Leffler 2511a1e1d21SSam Leffler if (len > IEEE80211_NWID_LEN) 2521a1e1d21SSam Leffler len = IEEE80211_NWID_LEN; 2531a1e1d21SSam Leffler /* determine printable or not */ 2541a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) { 2551a1e1d21SSam Leffler if (*p < ' ' || *p > 0x7e) 2561a1e1d21SSam Leffler break; 2571a1e1d21SSam Leffler } 2581a1e1d21SSam Leffler if (i == len) { 2591a1e1d21SSam Leffler printf("\""); 2601a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) 2611a1e1d21SSam Leffler printf("%c", *p); 2621a1e1d21SSam Leffler printf("\""); 2631a1e1d21SSam Leffler } else { 2641a1e1d21SSam Leffler printf("0x"); 2651a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) 2661a1e1d21SSam Leffler printf("%02x", *p); 2671a1e1d21SSam Leffler } 2681a1e1d21SSam Leffler } 2691a1e1d21SSam Leffler 2701a1e1d21SSam Leffler void 2718a1b9b6aSSam Leffler ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) 2721a1e1d21SSam Leffler { 2738a1b9b6aSSam Leffler const struct ieee80211_frame *wh; 2741a1e1d21SSam Leffler int i; 2751a1e1d21SSam Leffler 2768a1b9b6aSSam Leffler wh = (const struct ieee80211_frame *)buf; 2771a1e1d21SSam Leffler switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 2781a1e1d21SSam Leffler case IEEE80211_FC1_DIR_NODS: 2791a1e1d21SSam Leffler printf("NODS %s", ether_sprintf(wh->i_addr2)); 2801a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr1)); 2811a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr3)); 2821a1e1d21SSam Leffler break; 2831a1e1d21SSam Leffler case IEEE80211_FC1_DIR_TODS: 2841a1e1d21SSam Leffler printf("TODS %s", ether_sprintf(wh->i_addr2)); 2851a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr3)); 2861a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr1)); 2871a1e1d21SSam Leffler break; 2881a1e1d21SSam Leffler case IEEE80211_FC1_DIR_FROMDS: 2891a1e1d21SSam Leffler printf("FRDS %s", ether_sprintf(wh->i_addr3)); 2901a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr1)); 2911a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr2)); 2921a1e1d21SSam Leffler break; 2931a1e1d21SSam Leffler case IEEE80211_FC1_DIR_DSTODS: 2948a1b9b6aSSam Leffler printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); 2951a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr3)); 2961a1e1d21SSam Leffler printf("(%s", ether_sprintf(wh->i_addr2)); 2971a1e1d21SSam Leffler printf("->%s)", ether_sprintf(wh->i_addr1)); 2981a1e1d21SSam Leffler break; 2991a1e1d21SSam Leffler } 3001a1e1d21SSam Leffler switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 3011a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_DATA: 3021a1e1d21SSam Leffler printf(" data"); 3031a1e1d21SSam Leffler break; 3041a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_MGT: 3051a1e1d21SSam Leffler printf(" %s", ieee80211_mgt_subtype_name[ 3061a1e1d21SSam Leffler (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 3071a1e1d21SSam Leffler >> IEEE80211_FC0_SUBTYPE_SHIFT]); 3081a1e1d21SSam Leffler break; 3091a1e1d21SSam Leffler default: 3101a1e1d21SSam Leffler printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 3111a1e1d21SSam Leffler break; 3121a1e1d21SSam Leffler } 3138a1b9b6aSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3148a1b9b6aSSam Leffler int i; 3158a1b9b6aSSam Leffler printf(" WEP [IV"); 3168a1b9b6aSSam Leffler for (i = 0; i < IEEE80211_WEP_IVLEN; i++) 3178a1b9b6aSSam Leffler printf(" %.02x", buf[sizeof(*wh)+i]); 3188a1b9b6aSSam Leffler printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); 3198a1b9b6aSSam Leffler } 3201a1e1d21SSam Leffler if (rate >= 0) 3211a1e1d21SSam Leffler printf(" %dM", rate / 2); 3221a1e1d21SSam Leffler if (rssi >= 0) 3231a1e1d21SSam Leffler printf(" +%d", rssi); 3241a1e1d21SSam Leffler printf("\n"); 3251a1e1d21SSam Leffler if (len > 0) { 3261a1e1d21SSam Leffler for (i = 0; i < len; i++) { 3271a1e1d21SSam Leffler if ((i & 1) == 0) 3281a1e1d21SSam Leffler printf(" "); 3291a1e1d21SSam Leffler printf("%02x", buf[i]); 3301a1e1d21SSam Leffler } 3311a1e1d21SSam Leffler printf("\n"); 3321a1e1d21SSam Leffler } 3331a1e1d21SSam Leffler } 3341a1e1d21SSam Leffler 33579edaebfSSam Leffler static __inline int 33679edaebfSSam Leffler findrix(const struct ieee80211_rateset *rs, int r) 33779edaebfSSam Leffler { 33879edaebfSSam Leffler int i; 33979edaebfSSam Leffler 34079edaebfSSam Leffler for (i = 0; i < rs->rs_nrates; i++) 34179edaebfSSam Leffler if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 34279edaebfSSam Leffler return i; 34379edaebfSSam Leffler return -1; 34479edaebfSSam Leffler } 34579edaebfSSam Leffler 3461a1e1d21SSam Leffler int 3477d77cd53SSam Leffler ieee80211_fix_rate(struct ieee80211_node *ni, int flags) 3481a1e1d21SSam Leffler { 3491a1e1d21SSam Leffler #define RV(v) ((v) & IEEE80211_RATE_VAL) 3507d77cd53SSam Leffler struct ieee80211com *ic = ni->ni_ic; 35179edaebfSSam Leffler int i, j, rix, error; 3528a1b9b6aSSam Leffler int okrate, badrate, fixedrate; 35341b3c790SSam Leffler const struct ieee80211_rateset *srs; 35441b3c790SSam Leffler struct ieee80211_rateset *nrs; 3551a1e1d21SSam Leffler u_int8_t r; 3561a1e1d21SSam Leffler 3578a1b9b6aSSam Leffler /* 3588a1b9b6aSSam Leffler * If the fixed rate check was requested but no 3598a1b9b6aSSam Leffler * fixed has been defined then just remove it. 3608a1b9b6aSSam Leffler */ 3612c39b32cSSam Leffler if ((flags & IEEE80211_F_DOFRATE) && 3622c39b32cSSam Leffler ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) 3638a1b9b6aSSam Leffler flags &= ~IEEE80211_F_DOFRATE; 3641a1e1d21SSam Leffler error = 0; 3658a1b9b6aSSam Leffler okrate = badrate = fixedrate = 0; 36641b3c790SSam Leffler srs = ieee80211_get_suprates(ic, ni->ni_chan); 3671a1e1d21SSam Leffler nrs = &ni->ni_rates; 368ef39d4beSSam Leffler for (i = 0; i < nrs->rs_nrates; ) { 3691a1e1d21SSam Leffler if (flags & IEEE80211_F_DOSORT) { 3701a1e1d21SSam Leffler /* 3711a1e1d21SSam Leffler * Sort rates. 3721a1e1d21SSam Leffler */ 3731a1e1d21SSam Leffler for (j = i + 1; j < nrs->rs_nrates; j++) { 3741a1e1d21SSam Leffler if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 3751a1e1d21SSam Leffler r = nrs->rs_rates[i]; 3761a1e1d21SSam Leffler nrs->rs_rates[i] = nrs->rs_rates[j]; 3771a1e1d21SSam Leffler nrs->rs_rates[j] = r; 3781a1e1d21SSam Leffler } 3791a1e1d21SSam Leffler } 3801a1e1d21SSam Leffler } 3811a1e1d21SSam Leffler r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 3821a1e1d21SSam Leffler badrate = r; 3831a1e1d21SSam Leffler if (flags & IEEE80211_F_DOFRATE) { 3841a1e1d21SSam Leffler /* 3858a1b9b6aSSam Leffler * Check any fixed rate is included. 3861a1e1d21SSam Leffler */ 3878a1b9b6aSSam Leffler if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) 3888a1b9b6aSSam Leffler fixedrate = r; 3891a1e1d21SSam Leffler } 3901a1e1d21SSam Leffler /* 3911a1e1d21SSam Leffler * Check against supported rates. 3921a1e1d21SSam Leffler */ 39379edaebfSSam Leffler rix = findrix(srs, r); 39479edaebfSSam Leffler if (flags & IEEE80211_F_DONEGO) { 39579edaebfSSam Leffler if (rix < 0) { 396ef39d4beSSam Leffler /* 397ef39d4beSSam Leffler * A rate in the node's rate set is not 398ef39d4beSSam Leffler * supported. If this is a basic rate and we 39979edaebfSSam Leffler * are operating as a STA then this is an error. 400ef39d4beSSam Leffler * Otherwise we just discard/ignore the rate. 401ef39d4beSSam Leffler */ 40279edaebfSSam Leffler if ((flags & IEEE80211_F_JOIN) && 403ef39d4beSSam Leffler (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 4041a1e1d21SSam Leffler error++; 40579edaebfSSam Leffler } else if ((flags & IEEE80211_F_JOIN) == 0) { 40679edaebfSSam Leffler /* 40779edaebfSSam Leffler * Overwrite with the supported rate 40879edaebfSSam Leffler * value so any basic rate bit is set. 40979edaebfSSam Leffler */ 41079edaebfSSam Leffler nrs->rs_rates[i] = srs->rs_rates[rix]; 4111a1e1d21SSam Leffler } 4121a1e1d21SSam Leffler } 41379edaebfSSam Leffler if ((flags & IEEE80211_F_DODEL) && rix < 0) { 4141a1e1d21SSam Leffler /* 4151a1e1d21SSam Leffler * Delete unacceptable rates. 4161a1e1d21SSam Leffler */ 4171a1e1d21SSam Leffler nrs->rs_nrates--; 4181a1e1d21SSam Leffler for (j = i; j < nrs->rs_nrates; j++) 4191a1e1d21SSam Leffler nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 4201a1e1d21SSam Leffler nrs->rs_rates[j] = 0; 4211a1e1d21SSam Leffler continue; 4221a1e1d21SSam Leffler } 42379edaebfSSam Leffler if (rix >= 0) 4241a1e1d21SSam Leffler okrate = nrs->rs_rates[i]; 4251a1e1d21SSam Leffler i++; 4261a1e1d21SSam Leffler } 4278a1b9b6aSSam Leffler if (okrate == 0 || error != 0 || 4288a1b9b6aSSam Leffler ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) 4291a1e1d21SSam Leffler return badrate | IEEE80211_RATE_BASIC; 4301a1e1d21SSam Leffler else 4311a1e1d21SSam Leffler return RV(okrate); 4321a1e1d21SSam Leffler #undef RV 4331a1e1d21SSam Leffler } 4341a1e1d21SSam Leffler 4358a1b9b6aSSam Leffler /* 4368a1b9b6aSSam Leffler * Reset 11g-related state. 4378a1b9b6aSSam Leffler */ 4388a1b9b6aSSam Leffler void 4398a1b9b6aSSam Leffler ieee80211_reset_erp(struct ieee80211com *ic) 4401a1e1d21SSam Leffler { 4418a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 4428a1b9b6aSSam Leffler ic->ic_nonerpsta = 0; 4438a1b9b6aSSam Leffler ic->ic_longslotsta = 0; 4448a1b9b6aSSam Leffler /* 4458a1b9b6aSSam Leffler * Short slot time is enabled only when operating in 11g 4468a1b9b6aSSam Leffler * and not in an IBSS. We must also honor whether or not 4478a1b9b6aSSam Leffler * the driver is capable of doing it. 4488a1b9b6aSSam Leffler */ 4498a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 4508a1b9b6aSSam Leffler ic->ic_curmode == IEEE80211_MODE_11A || 4518a1b9b6aSSam Leffler (ic->ic_curmode == IEEE80211_MODE_11G && 4528a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_HOSTAP && 4538a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHSLOT))); 4548a1b9b6aSSam Leffler /* 4558a1b9b6aSSam Leffler * Set short preamble and ERP barker-preamble flags. 4568a1b9b6aSSam Leffler */ 4578a1b9b6aSSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11A || 4588a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 4598a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 4608a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEBARKER; 4618a1b9b6aSSam Leffler } else { 4628a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 4638a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEBARKER; 4648a1b9b6aSSam Leffler } 4658a1b9b6aSSam Leffler } 4668a1b9b6aSSam Leffler 4678a1b9b6aSSam Leffler /* 4688a1b9b6aSSam Leffler * Set the short slot time state and notify the driver. 4698a1b9b6aSSam Leffler */ 4708a1b9b6aSSam Leffler void 4718a1b9b6aSSam Leffler ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 4728a1b9b6aSSam Leffler { 4738a1b9b6aSSam Leffler if (onoff) 4748a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHSLOT; 4758a1b9b6aSSam Leffler else 4768a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHSLOT; 4778a1b9b6aSSam Leffler /* notify driver */ 4788a1b9b6aSSam Leffler if (ic->ic_updateslot != NULL) 4798a1b9b6aSSam Leffler ic->ic_updateslot(ic->ic_ifp); 4808a1b9b6aSSam Leffler } 4818a1b9b6aSSam Leffler 4828a1b9b6aSSam Leffler /* 4838a1b9b6aSSam Leffler * Check if the specified rate set supports ERP. 4848a1b9b6aSSam Leffler * NB: the rate set is assumed to be sorted. 4858a1b9b6aSSam Leffler */ 4868a1b9b6aSSam Leffler int 4878a1b9b6aSSam Leffler ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 4888a1b9b6aSSam Leffler { 4898a1b9b6aSSam Leffler #define N(a) (sizeof(a) / sizeof(a[0])) 4908a1b9b6aSSam Leffler static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 4918a1b9b6aSSam Leffler int i, j; 4928a1b9b6aSSam Leffler 4938a1b9b6aSSam Leffler if (rs->rs_nrates < N(rates)) 4948a1b9b6aSSam Leffler return 0; 4958a1b9b6aSSam Leffler for (i = 0; i < N(rates); i++) { 4968a1b9b6aSSam Leffler for (j = 0; j < rs->rs_nrates; j++) { 4978a1b9b6aSSam Leffler int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 4988a1b9b6aSSam Leffler if (rates[i] == r) 4998a1b9b6aSSam Leffler goto next; 5008a1b9b6aSSam Leffler if (r > rates[i]) 5018a1b9b6aSSam Leffler return 0; 5028a1b9b6aSSam Leffler } 5038a1b9b6aSSam Leffler return 0; 5048a1b9b6aSSam Leffler next: 5058a1b9b6aSSam Leffler ; 5068a1b9b6aSSam Leffler } 5078a1b9b6aSSam Leffler return 1; 5088a1b9b6aSSam Leffler #undef N 5098a1b9b6aSSam Leffler } 5108a1b9b6aSSam Leffler 5118a1b9b6aSSam Leffler /* 5128a1b9b6aSSam Leffler * Mark the basic rates for the 11g rate table based on the 5138a1b9b6aSSam Leffler * operating mode. For real 11g we mark all the 11b rates 5148a1b9b6aSSam Leffler * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 5158a1b9b6aSSam Leffler * 11b rates. There's also a pseudo 11a-mode used to mark only 5168a1b9b6aSSam Leffler * the basic OFDM rates. 5178a1b9b6aSSam Leffler */ 5188a1b9b6aSSam Leffler void 5198a1b9b6aSSam Leffler ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 5208a1b9b6aSSam Leffler { 5218a1b9b6aSSam Leffler static const struct ieee80211_rateset basic[] = { 5228a1b9b6aSSam Leffler { 0 }, /* IEEE80211_MODE_AUTO */ 5238a1b9b6aSSam Leffler { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 5248a1b9b6aSSam Leffler { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 5258a1b9b6aSSam Leffler { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 5268a1b9b6aSSam Leffler { 0 }, /* IEEE80211_MODE_FH */ 5278a1b9b6aSSam Leffler /* IEEE80211_MODE_PUREG (not yet) */ 5288a1b9b6aSSam Leffler { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 5298a1b9b6aSSam Leffler }; 5308a1b9b6aSSam Leffler int i, j; 5318a1b9b6aSSam Leffler 5328a1b9b6aSSam Leffler for (i = 0; i < rs->rs_nrates; i++) { 5338a1b9b6aSSam Leffler rs->rs_rates[i] &= IEEE80211_RATE_VAL; 5348a1b9b6aSSam Leffler for (j = 0; j < basic[mode].rs_nrates; j++) 5358a1b9b6aSSam Leffler if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 5368a1b9b6aSSam Leffler rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 5378a1b9b6aSSam Leffler break; 5388a1b9b6aSSam Leffler } 5398a1b9b6aSSam Leffler } 5408a1b9b6aSSam Leffler } 5418a1b9b6aSSam Leffler 5428a1b9b6aSSam Leffler /* 5438a1b9b6aSSam Leffler * WME protocol support. The following parameters come from the spec. 5448a1b9b6aSSam Leffler */ 5458a1b9b6aSSam Leffler typedef struct phyParamType { 5468a1b9b6aSSam Leffler u_int8_t aifsn; 5478a1b9b6aSSam Leffler u_int8_t logcwmin; 5488a1b9b6aSSam Leffler u_int8_t logcwmax; 5498a1b9b6aSSam Leffler u_int16_t txopLimit; 5508a1b9b6aSSam Leffler u_int8_t acm; 5518a1b9b6aSSam Leffler } paramType; 5528a1b9b6aSSam Leffler 5538a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 5548a1b9b6aSSam Leffler { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ 5558a1b9b6aSSam Leffler { 3, 4, 6 }, /* IEEE80211_MODE_11A */ 5568a1b9b6aSSam Leffler { 3, 5, 7 }, /* IEEE80211_MODE_11B */ 5578a1b9b6aSSam Leffler { 3, 4, 6 }, /* IEEE80211_MODE_11G */ 5588a1b9b6aSSam Leffler { 3, 5, 7 }, /* IEEE80211_MODE_FH */ 5598a1b9b6aSSam Leffler { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ 5608a1b9b6aSSam Leffler { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ 5618a1b9b6aSSam Leffler }; 5628a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 5638a1b9b6aSSam Leffler { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ 5648a1b9b6aSSam Leffler { 7, 4, 10 }, /* IEEE80211_MODE_11A */ 5658a1b9b6aSSam Leffler { 7, 5, 10 }, /* IEEE80211_MODE_11B */ 5668a1b9b6aSSam Leffler { 7, 4, 10 }, /* IEEE80211_MODE_11G */ 5678a1b9b6aSSam Leffler { 7, 5, 10 }, /* IEEE80211_MODE_FH */ 5688a1b9b6aSSam Leffler { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 5698a1b9b6aSSam Leffler { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 5708a1b9b6aSSam Leffler }; 5718a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 5728a1b9b6aSSam Leffler { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 5738a1b9b6aSSam Leffler { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 5748a1b9b6aSSam Leffler { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 5758a1b9b6aSSam Leffler { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 5768a1b9b6aSSam Leffler { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 5778a1b9b6aSSam Leffler { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 5788a1b9b6aSSam Leffler { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 5798a1b9b6aSSam Leffler }; 5808a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 5818a1b9b6aSSam Leffler { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 5828a1b9b6aSSam Leffler { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 5838a1b9b6aSSam Leffler { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 5848a1b9b6aSSam Leffler { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 5858a1b9b6aSSam Leffler { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 5868a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 5878a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 5888a1b9b6aSSam Leffler }; 5898a1b9b6aSSam Leffler 5908a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 5918a1b9b6aSSam Leffler { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ 5928a1b9b6aSSam Leffler { 3, 4, 10 }, /* IEEE80211_MODE_11A */ 5938a1b9b6aSSam Leffler { 3, 5, 10 }, /* IEEE80211_MODE_11B */ 5948a1b9b6aSSam Leffler { 3, 4, 10 }, /* IEEE80211_MODE_11G */ 5958a1b9b6aSSam Leffler { 3, 5, 10 }, /* IEEE80211_MODE_FH */ 5968a1b9b6aSSam Leffler { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 5978a1b9b6aSSam Leffler { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 5988a1b9b6aSSam Leffler }; 5998a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 6008a1b9b6aSSam Leffler { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 6018a1b9b6aSSam Leffler { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 6028a1b9b6aSSam Leffler { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 6038a1b9b6aSSam Leffler { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 6048a1b9b6aSSam Leffler { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 6058a1b9b6aSSam Leffler { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 6068a1b9b6aSSam Leffler { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 6078a1b9b6aSSam Leffler }; 6088a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 6098a1b9b6aSSam Leffler { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 6108a1b9b6aSSam Leffler { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 6118a1b9b6aSSam Leffler { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 6128a1b9b6aSSam Leffler { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 6138a1b9b6aSSam Leffler { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 6148a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 6158a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 6168a1b9b6aSSam Leffler }; 6178a1b9b6aSSam Leffler 6188a1b9b6aSSam Leffler void 6198a1b9b6aSSam Leffler ieee80211_wme_initparams(struct ieee80211com *ic) 6208a1b9b6aSSam Leffler { 6218a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 6228a1b9b6aSSam Leffler const paramType *pPhyParam, *pBssPhyParam; 6238a1b9b6aSSam Leffler struct wmeParams *wmep; 6248a1b9b6aSSam Leffler int i; 6258a1b9b6aSSam Leffler 6268a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_WME) == 0) 6278a1b9b6aSSam Leffler return; 6288a1b9b6aSSam Leffler 6298a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 6308a1b9b6aSSam Leffler switch (i) { 6318a1b9b6aSSam Leffler case WME_AC_BK: 6328a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 6338a1b9b6aSSam Leffler pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 6348a1b9b6aSSam Leffler break; 6358a1b9b6aSSam Leffler case WME_AC_VI: 6368a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; 6378a1b9b6aSSam Leffler pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; 6388a1b9b6aSSam Leffler break; 6398a1b9b6aSSam Leffler case WME_AC_VO: 6408a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; 6418a1b9b6aSSam Leffler pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; 6428a1b9b6aSSam Leffler break; 6438a1b9b6aSSam Leffler case WME_AC_BE: 6448a1b9b6aSSam Leffler default: 6458a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; 6468a1b9b6aSSam Leffler pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; 6478a1b9b6aSSam Leffler break; 6488a1b9b6aSSam Leffler } 6498a1b9b6aSSam Leffler 6508a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 6518a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 6528a1b9b6aSSam Leffler wmep->wmep_acm = pPhyParam->acm; 6538a1b9b6aSSam Leffler wmep->wmep_aifsn = pPhyParam->aifsn; 6548a1b9b6aSSam Leffler wmep->wmep_logcwmin = pPhyParam->logcwmin; 6558a1b9b6aSSam Leffler wmep->wmep_logcwmax = pPhyParam->logcwmax; 6568a1b9b6aSSam Leffler wmep->wmep_txopLimit = pPhyParam->txopLimit; 6578a1b9b6aSSam Leffler } else { 6588a1b9b6aSSam Leffler wmep->wmep_acm = pBssPhyParam->acm; 6598a1b9b6aSSam Leffler wmep->wmep_aifsn = pBssPhyParam->aifsn; 6608a1b9b6aSSam Leffler wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 6618a1b9b6aSSam Leffler wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 6628a1b9b6aSSam Leffler wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 6638a1b9b6aSSam Leffler 6648a1b9b6aSSam Leffler } 6658a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 6668a1b9b6aSSam Leffler "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 6678a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 6688a1b9b6aSSam Leffler , ieee80211_wme_acnames[i] 6698a1b9b6aSSam Leffler , wmep->wmep_acm 6708a1b9b6aSSam Leffler , wmep->wmep_aifsn 6718a1b9b6aSSam Leffler , wmep->wmep_logcwmin 6728a1b9b6aSSam Leffler , wmep->wmep_logcwmax 6738a1b9b6aSSam Leffler , wmep->wmep_txopLimit 6748a1b9b6aSSam Leffler ); 6758a1b9b6aSSam Leffler 6768a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 6778a1b9b6aSSam Leffler wmep->wmep_acm = pBssPhyParam->acm; 6788a1b9b6aSSam Leffler wmep->wmep_aifsn = pBssPhyParam->aifsn; 6798a1b9b6aSSam Leffler wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 6808a1b9b6aSSam Leffler wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 6818a1b9b6aSSam Leffler wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 6828a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 6838a1b9b6aSSam Leffler "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 6848a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 6858a1b9b6aSSam Leffler , ieee80211_wme_acnames[i] 6868a1b9b6aSSam Leffler , wmep->wmep_acm 6878a1b9b6aSSam Leffler , wmep->wmep_aifsn 6888a1b9b6aSSam Leffler , wmep->wmep_logcwmin 6898a1b9b6aSSam Leffler , wmep->wmep_logcwmax 6908a1b9b6aSSam Leffler , wmep->wmep_txopLimit 6918a1b9b6aSSam Leffler ); 6928a1b9b6aSSam Leffler } 6938a1b9b6aSSam Leffler /* NB: check ic_bss to avoid NULL deref on initial attach */ 6948a1b9b6aSSam Leffler if (ic->ic_bss != NULL) { 6958a1b9b6aSSam Leffler /* 6968a1b9b6aSSam Leffler * Calculate agressive mode switching threshold based 6978a1b9b6aSSam Leffler * on beacon interval. This doesn't need locking since 6988a1b9b6aSSam Leffler * we're only called before entering the RUN state at 6998a1b9b6aSSam Leffler * which point we start sending beacon frames. 7008a1b9b6aSSam Leffler */ 7018a1b9b6aSSam Leffler wme->wme_hipri_switch_thresh = 7028a1b9b6aSSam Leffler (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 7038a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 7048a1b9b6aSSam Leffler } 7058a1b9b6aSSam Leffler } 7068a1b9b6aSSam Leffler 7078a1b9b6aSSam Leffler /* 7088a1b9b6aSSam Leffler * Update WME parameters for ourself and the BSS. 7098a1b9b6aSSam Leffler */ 7108a1b9b6aSSam Leffler void 7118a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(struct ieee80211com *ic) 7128a1b9b6aSSam Leffler { 7138a1b9b6aSSam Leffler static const paramType phyParam[IEEE80211_MODE_MAX] = { 7148a1b9b6aSSam Leffler { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ 7158a1b9b6aSSam Leffler { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ 7168a1b9b6aSSam Leffler { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ 7178a1b9b6aSSam Leffler { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ 7188a1b9b6aSSam Leffler { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ 7198a1b9b6aSSam Leffler { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ 7208a1b9b6aSSam Leffler { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ 7218a1b9b6aSSam Leffler }; 7228a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 7238a1b9b6aSSam Leffler const struct wmeParams *wmep; 7248a1b9b6aSSam Leffler struct wmeParams *chanp, *bssp; 7258a1b9b6aSSam Leffler int i; 7268a1b9b6aSSam Leffler 7278a1b9b6aSSam Leffler /* set up the channel access parameters for the physical device */ 7288a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 7298a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[i]; 7308a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 7318a1b9b6aSSam Leffler chanp->wmep_aifsn = wmep->wmep_aifsn; 7328a1b9b6aSSam Leffler chanp->wmep_logcwmin = wmep->wmep_logcwmin; 7338a1b9b6aSSam Leffler chanp->wmep_logcwmax = wmep->wmep_logcwmax; 7348a1b9b6aSSam Leffler chanp->wmep_txopLimit = wmep->wmep_txopLimit; 7358a1b9b6aSSam Leffler 7368a1b9b6aSSam Leffler chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 7378a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 7388a1b9b6aSSam Leffler chanp->wmep_aifsn = wmep->wmep_aifsn; 7398a1b9b6aSSam Leffler chanp->wmep_logcwmin = wmep->wmep_logcwmin; 7408a1b9b6aSSam Leffler chanp->wmep_logcwmax = wmep->wmep_logcwmax; 7418a1b9b6aSSam Leffler chanp->wmep_txopLimit = wmep->wmep_txopLimit; 7428a1b9b6aSSam Leffler } 7438a1b9b6aSSam Leffler 7448a1b9b6aSSam Leffler /* 7458a1b9b6aSSam Leffler * This implements agressive mode as found in certain 7468a1b9b6aSSam Leffler * vendors' AP's. When there is significant high 7478a1b9b6aSSam Leffler * priority (VI/VO) traffic in the BSS throttle back BE 7488a1b9b6aSSam Leffler * traffic by using conservative parameters. Otherwise 7498a1b9b6aSSam Leffler * BE uses agressive params to optimize performance of 7508a1b9b6aSSam Leffler * legacy/non-QoS traffic. 7518a1b9b6aSSam Leffler */ 7528a1b9b6aSSam Leffler if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 753ad262427SSam Leffler (wme->wme_flags & WME_F_AGGRMODE) != 0) || 75459a44035SSam Leffler (ic->ic_opmode == IEEE80211_M_STA && 7558a1b9b6aSSam Leffler (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 7568a1b9b6aSSam Leffler (ic->ic_flags & IEEE80211_F_WME) == 0) { 7578a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 7588a1b9b6aSSam Leffler bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 7598a1b9b6aSSam Leffler 7608a1b9b6aSSam Leffler chanp->wmep_aifsn = bssp->wmep_aifsn = 7618a1b9b6aSSam Leffler phyParam[ic->ic_curmode].aifsn; 7628a1b9b6aSSam Leffler chanp->wmep_logcwmin = bssp->wmep_logcwmin = 7638a1b9b6aSSam Leffler phyParam[ic->ic_curmode].logcwmin; 7648a1b9b6aSSam Leffler chanp->wmep_logcwmax = bssp->wmep_logcwmax = 7658a1b9b6aSSam Leffler phyParam[ic->ic_curmode].logcwmax; 7668a1b9b6aSSam Leffler chanp->wmep_txopLimit = bssp->wmep_txopLimit = 767c27e4e31SSam Leffler (ic->ic_flags & IEEE80211_F_BURST) ? 7688a1b9b6aSSam Leffler phyParam[ic->ic_curmode].txopLimit : 0; 7698a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 7708a1b9b6aSSam Leffler "%s: %s [acm %u aifsn %u log2(cwmin) %u " 7718a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 7728a1b9b6aSSam Leffler , ieee80211_wme_acnames[WME_AC_BE] 7738a1b9b6aSSam Leffler , chanp->wmep_acm 7748a1b9b6aSSam Leffler , chanp->wmep_aifsn 7758a1b9b6aSSam Leffler , chanp->wmep_logcwmin 7768a1b9b6aSSam Leffler , chanp->wmep_logcwmax 7778a1b9b6aSSam Leffler , chanp->wmep_txopLimit 7788a1b9b6aSSam Leffler ); 7798a1b9b6aSSam Leffler } 7808a1b9b6aSSam Leffler 7818a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 782ad262427SSam Leffler ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 7838a1b9b6aSSam Leffler static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { 7848a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_AUTO */ 7858a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_11A */ 7868a1b9b6aSSam Leffler 4, /* IEEE80211_MODE_11B */ 7878a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_11G */ 7888a1b9b6aSSam Leffler 4, /* IEEE80211_MODE_FH */ 7898a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_TURBO_A */ 7908a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_TURBO_G */ 7918a1b9b6aSSam Leffler }; 7928a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 7938a1b9b6aSSam Leffler bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 7948a1b9b6aSSam Leffler 7958a1b9b6aSSam Leffler chanp->wmep_logcwmin = bssp->wmep_logcwmin = 7968a1b9b6aSSam Leffler logCwMin[ic->ic_curmode]; 7978a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 7988a1b9b6aSSam Leffler "%s: %s log2(cwmin) %u\n", __func__ 7998a1b9b6aSSam Leffler , ieee80211_wme_acnames[WME_AC_BE] 8008a1b9b6aSSam Leffler , chanp->wmep_logcwmin 8018a1b9b6aSSam Leffler ); 8028a1b9b6aSSam Leffler } 8038a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 8048a1b9b6aSSam Leffler /* 8058a1b9b6aSSam Leffler * Arrange for a beacon update and bump the parameter 8068a1b9b6aSSam Leffler * set number so associated stations load the new values. 8078a1b9b6aSSam Leffler */ 8088a1b9b6aSSam Leffler wme->wme_bssChanParams.cap_info = 8098a1b9b6aSSam Leffler (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 8108a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WMEUPDATE; 8118a1b9b6aSSam Leffler } 8128a1b9b6aSSam Leffler 8138a1b9b6aSSam Leffler wme->wme_update(ic); 8148a1b9b6aSSam Leffler 8158a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 8168a1b9b6aSSam Leffler "%s: WME params updated, cap_info 0x%x\n", __func__, 8178a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_STA ? 8188a1b9b6aSSam Leffler wme->wme_wmeChanParams.cap_info : 8198a1b9b6aSSam Leffler wme->wme_bssChanParams.cap_info); 8208a1b9b6aSSam Leffler } 8218a1b9b6aSSam Leffler 8228a1b9b6aSSam Leffler void 8238a1b9b6aSSam Leffler ieee80211_wme_updateparams(struct ieee80211com *ic) 8248a1b9b6aSSam Leffler { 8258a1b9b6aSSam Leffler 8268a1b9b6aSSam Leffler if (ic->ic_caps & IEEE80211_C_WME) { 8278a1b9b6aSSam Leffler IEEE80211_BEACON_LOCK(ic); 8288a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(ic); 8298a1b9b6aSSam Leffler IEEE80211_BEACON_UNLOCK(ic); 8308a1b9b6aSSam Leffler } 8318a1b9b6aSSam Leffler } 8328a1b9b6aSSam Leffler 833e701e041SSam Leffler void 834e701e041SSam Leffler ieee80211_beacon_miss(struct ieee80211com *ic) 835e701e041SSam Leffler { 836e701e041SSam Leffler 837e701e041SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 838e701e041SSam Leffler /* XXX check ic_curchan != ic_bsschan? */ 839e701e041SSam Leffler return; 840e701e041SSam Leffler } 841e701e041SSam Leffler IEEE80211_DPRINTF(ic, 842e701e041SSam Leffler IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 843e701e041SSam Leffler "%s\n", "beacon miss"); 844e701e041SSam Leffler 845e701e041SSam Leffler /* 846e701e041SSam Leffler * Our handling is only meaningful for stations that are 847e701e041SSam Leffler * associated; any other conditions else will be handled 848e701e041SSam Leffler * through different means (e.g. the tx timeout on mgt frames). 849e701e041SSam Leffler */ 850e701e041SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) 851e701e041SSam Leffler return; 852e701e041SSam Leffler 853e701e041SSam Leffler if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { 854e701e041SSam Leffler /* 855e701e041SSam Leffler * Send a directed probe req before falling back to a scan; 856e701e041SSam Leffler * if we receive a response ic_bmiss_count will be reset. 857e701e041SSam Leffler * Some cards mistakenly report beacon miss so this avoids 858e701e041SSam Leffler * the expensive scan if the ap is still there. 859e701e041SSam Leffler */ 860e701e041SSam Leffler ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, 861e701e041SSam Leffler ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, 862e701e041SSam Leffler ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, 863e701e041SSam Leffler ic->ic_opt_ie, ic->ic_opt_ie_len); 864e701e041SSam Leffler return; 865e701e041SSam Leffler } 866e701e041SSam Leffler ic->ic_bmiss_count = 0; 867e701e041SSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 868e701e041SSam Leffler } 869e701e041SSam Leffler 870e99662a6SSam Leffler /* 871e99662a6SSam Leffler * Software beacon miss handling. Check if any beacons 872e99662a6SSam Leffler * were received in the last period. If not post a 873e99662a6SSam Leffler * beacon miss; otherwise reset the counter. 874e99662a6SSam Leffler */ 875e99662a6SSam Leffler static void 876e99662a6SSam Leffler ieee80211_swbmiss(void *arg) 877e99662a6SSam Leffler { 878e99662a6SSam Leffler struct ieee80211com *ic = arg; 879e99662a6SSam Leffler 880e99662a6SSam Leffler if (ic->ic_swbmiss_count == 0) { 881e99662a6SSam Leffler ieee80211_beacon_miss(ic); 882e99662a6SSam Leffler if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ 883e99662a6SSam Leffler return; 884e99662a6SSam Leffler } else 885e99662a6SSam Leffler ic->ic_swbmiss_count = 0; 886e99662a6SSam Leffler callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 887e99662a6SSam Leffler ieee80211_swbmiss, ic); 888e99662a6SSam Leffler } 889e99662a6SSam Leffler 8907edb8cf9SSam Leffler static void 8917edb8cf9SSam Leffler sta_disassoc(void *arg, struct ieee80211_node *ni) 8927edb8cf9SSam Leffler { 8937edb8cf9SSam Leffler struct ieee80211com *ic = arg; 8947edb8cf9SSam Leffler 8957edb8cf9SSam Leffler if (ni->ni_associd != 0) { 8967edb8cf9SSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 8977edb8cf9SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 8987edb8cf9SSam Leffler ieee80211_node_leave(ic, ni); 8997edb8cf9SSam Leffler } 9007edb8cf9SSam Leffler } 9017edb8cf9SSam Leffler 9027edb8cf9SSam Leffler static void 9037edb8cf9SSam Leffler sta_deauth(void *arg, struct ieee80211_node *ni) 9047edb8cf9SSam Leffler { 9057edb8cf9SSam Leffler struct ieee80211com *ic = arg; 9067edb8cf9SSam Leffler 9077edb8cf9SSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 9087edb8cf9SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 9097edb8cf9SSam Leffler } 9107edb8cf9SSam Leffler 9118a1b9b6aSSam Leffler static int 9128a1b9b6aSSam Leffler ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 9138a1b9b6aSSam Leffler { 9148a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 9151a1e1d21SSam Leffler struct ieee80211_node *ni; 916a11c9a5cSSam Leffler enum ieee80211_state ostate; 9171a1e1d21SSam Leffler 9181a1e1d21SSam Leffler ostate = ic->ic_state; 9198a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 9208a1b9b6aSSam Leffler ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 921a11c9a5cSSam Leffler ic->ic_state = nstate; /* state transition */ 9221a1e1d21SSam Leffler ni = ic->ic_bss; /* NB: no reference held */ 923e99662a6SSam Leffler if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) 924e99662a6SSam Leffler callout_stop(&ic->ic_swbmiss); 9251a1e1d21SSam Leffler switch (nstate) { 9261a1e1d21SSam Leffler case IEEE80211_S_INIT: 9271a1e1d21SSam Leffler switch (ostate) { 9281a1e1d21SSam Leffler case IEEE80211_S_INIT: 9291a1e1d21SSam Leffler break; 9301a1e1d21SSam Leffler case IEEE80211_S_RUN: 9311a1e1d21SSam Leffler switch (ic->ic_opmode) { 9321a1e1d21SSam Leffler case IEEE80211_M_STA: 9331a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 9341a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 9351a1e1d21SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 9368a1b9b6aSSam Leffler ieee80211_sta_leave(ic, ni); 9371a1e1d21SSam Leffler break; 9381a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 9397edb8cf9SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 9407edb8cf9SSam Leffler sta_disassoc, ic); 9411a1e1d21SSam Leffler break; 9421a1e1d21SSam Leffler default: 9431a1e1d21SSam Leffler break; 9441a1e1d21SSam Leffler } 945915f1482SSam Leffler break; 9461a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 9471a1e1d21SSam Leffler switch (ic->ic_opmode) { 9481a1e1d21SSam Leffler case IEEE80211_M_STA: 9491a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 9501a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 9511a1e1d21SSam Leffler IEEE80211_REASON_AUTH_LEAVE); 9521a1e1d21SSam Leffler break; 9531a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 9547edb8cf9SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 9557edb8cf9SSam Leffler sta_deauth, ic); 9561a1e1d21SSam Leffler break; 9571a1e1d21SSam Leffler default: 9581a1e1d21SSam Leffler break; 9591a1e1d21SSam Leffler } 960915f1482SSam Leffler break; 9611a1e1d21SSam Leffler case IEEE80211_S_SCAN: 962c75ac469SSam Leffler ieee80211_cancel_scan(ic); 9631a1e1d21SSam Leffler break; 964915f1482SSam Leffler case IEEE80211_S_AUTH: 965915f1482SSam Leffler break; 966915f1482SSam Leffler } 967915f1482SSam Leffler if (ostate != IEEE80211_S_INIT) { 968915f1482SSam Leffler /* NB: optimize INIT -> INIT case */ 969915f1482SSam Leffler ic->ic_mgt_timer = 0; 970915f1482SSam Leffler ieee80211_drain_ifq(&ic->ic_mgtq); 971915f1482SSam Leffler ieee80211_reset_bss(ic); 9721a1e1d21SSam Leffler } 9738a1b9b6aSSam Leffler if (ic->ic_auth->ia_detach != NULL) 9748a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 9751a1e1d21SSam Leffler break; 9761a1e1d21SSam Leffler case IEEE80211_S_SCAN: 9771a1e1d21SSam Leffler switch (ostate) { 9781a1e1d21SSam Leffler case IEEE80211_S_INIT: 9798a1b9b6aSSam Leffler if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 9808a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_IBSS || 9818a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_AHDEMO) && 9821a1e1d21SSam Leffler ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 9831a1e1d21SSam Leffler /* 9841a1e1d21SSam Leffler * AP operation and we already have a channel; 9851a1e1d21SSam Leffler * bypass the scan and startup immediately. 9861a1e1d21SSam Leffler */ 9871a1e1d21SSam Leffler ieee80211_create_ibss(ic, ic->ic_des_chan); 9881a1e1d21SSam Leffler } else { 9898a1b9b6aSSam Leffler ieee80211_begin_scan(ic, arg); 9901a1e1d21SSam Leffler } 9911a1e1d21SSam Leffler break; 9921a1e1d21SSam Leffler case IEEE80211_S_SCAN: 9938a1b9b6aSSam Leffler /* 994097131ffSSam Leffler * Scan next. If doing an active scan probe 995097131ffSSam Leffler * for the requested ap (if any). 9968a1b9b6aSSam Leffler */ 997097131ffSSam Leffler if (ic->ic_flags & IEEE80211_F_ASCAN) 998097131ffSSam Leffler ieee80211_probe_curchan(ic, 0); 9991a1e1d21SSam Leffler break; 10001a1e1d21SSam Leffler case IEEE80211_S_RUN: 10011a1e1d21SSam Leffler /* beacon miss */ 10028a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, 10038a1b9b6aSSam Leffler "no recent beacons from %s; rescanning\n", 10041a1e1d21SSam Leffler ether_sprintf(ic->ic_bss->ni_bssid)); 10058a1b9b6aSSam Leffler ieee80211_sta_leave(ic, ni); 10068a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 10071a1e1d21SSam Leffler /* FALLTHRU */ 10081a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10091a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10101a1e1d21SSam Leffler /* timeout restart scan */ 10118a1b9b6aSSam Leffler ni = ieee80211_find_node(&ic->ic_scan, 10128a1b9b6aSSam Leffler ic->ic_bss->ni_macaddr); 10131a1e1d21SSam Leffler if (ni != NULL) { 10141a1e1d21SSam Leffler ni->ni_fails++; 10151a1e1d21SSam Leffler ieee80211_unref_node(&ni); 10161a1e1d21SSam Leffler } 1017ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 10188a1b9b6aSSam Leffler ieee80211_begin_scan(ic, arg); 10191a1e1d21SSam Leffler break; 10201a1e1d21SSam Leffler } 10211a1e1d21SSam Leffler break; 10221a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10231a1e1d21SSam Leffler switch (ostate) { 10241a1e1d21SSam Leffler case IEEE80211_S_INIT: 10251a1e1d21SSam Leffler case IEEE80211_S_SCAN: 10261a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10271a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1); 10281a1e1d21SSam Leffler break; 10291a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10301a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10318a1b9b6aSSam Leffler switch (arg) { 10321a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 10331a1e1d21SSam Leffler /* ??? */ 10341a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10351a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2); 10361a1e1d21SSam Leffler break; 10371a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 10381a1e1d21SSam Leffler /* ignore and retry scan on timeout */ 10391a1e1d21SSam Leffler break; 10401a1e1d21SSam Leffler } 10411a1e1d21SSam Leffler break; 10421a1e1d21SSam Leffler case IEEE80211_S_RUN: 10438a1b9b6aSSam Leffler switch (arg) { 10441a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 10451a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10461a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2); 10471a1e1d21SSam Leffler ic->ic_state = ostate; /* stay RUN */ 10481a1e1d21SSam Leffler break; 10491a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 1050ae8880fdSSam Leffler ieee80211_sta_leave(ic, ni); 1051ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 10521a1e1d21SSam Leffler /* try to reauth */ 10531a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10541a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1); 1055ae8880fdSSam Leffler } 10561a1e1d21SSam Leffler break; 10571a1e1d21SSam Leffler } 10581a1e1d21SSam Leffler break; 10591a1e1d21SSam Leffler } 10601a1e1d21SSam Leffler break; 10611a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10621a1e1d21SSam Leffler switch (ostate) { 10631a1e1d21SSam Leffler case IEEE80211_S_INIT: 10641a1e1d21SSam Leffler case IEEE80211_S_SCAN: 10651a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10668a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 10678a1b9b6aSSam Leffler "%s: invalid transition\n", __func__); 10681a1e1d21SSam Leffler break; 10691a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10701a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10711a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 10721a1e1d21SSam Leffler break; 10731a1e1d21SSam Leffler case IEEE80211_S_RUN: 1074ae8880fdSSam Leffler ieee80211_sta_leave(ic, ni); 1075ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 10761a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10771a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); 1078ae8880fdSSam Leffler } 10791a1e1d21SSam Leffler break; 10801a1e1d21SSam Leffler } 10811a1e1d21SSam Leffler break; 10821a1e1d21SSam Leffler case IEEE80211_S_RUN: 10838a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA) { 10848a1b9b6aSSam Leffler /* XXX validate prerequisites */ 10858a1b9b6aSSam Leffler } 10861a1e1d21SSam Leffler switch (ostate) { 10871a1e1d21SSam Leffler case IEEE80211_S_INIT: 10888a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 10898a1b9b6aSSam Leffler break; 10908a1b9b6aSSam Leffler /* fall thru... */ 10911a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10928a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 10938a1b9b6aSSam Leffler "%s: invalid transition\n", __func__); 10942c21ffc8SSam Leffler /* fall thru... */ 10952c21ffc8SSam Leffler case IEEE80211_S_RUN: 10961a1e1d21SSam Leffler break; 10971a1e1d21SSam Leffler case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 10981a1e1d21SSam Leffler case IEEE80211_S_ASSOC: /* infra mode */ 10991a1e1d21SSam Leffler KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 11001a1e1d21SSam Leffler ("%s: bogus xmit rate %u setup\n", __func__, 11011a1e1d21SSam Leffler ni->ni_txrate)); 11028a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 11038a1b9b6aSSam Leffler if (ieee80211_msg_debug(ic)) { 11041a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 11058a1b9b6aSSam Leffler if_printf(ifp, "associated "); 11061a1e1d21SSam Leffler else 11078a1b9b6aSSam Leffler if_printf(ifp, "synchronized "); 11081a1e1d21SSam Leffler printf("with %s ssid ", 11091a1e1d21SSam Leffler ether_sprintf(ni->ni_bssid)); 11101a1e1d21SSam Leffler ieee80211_print_essid(ic->ic_bss->ni_essid, 11111a1e1d21SSam Leffler ni->ni_esslen); 11121a1e1d21SSam Leffler printf(" channel %d start %uMb\n", 1113b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 11141a1e1d21SSam Leffler IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 11151a1e1d21SSam Leffler } 11168a1b9b6aSSam Leffler #endif 11171a1e1d21SSam Leffler ic->ic_mgt_timer = 0; 11188a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 11198a1b9b6aSSam Leffler ieee80211_notify_node_join(ic, ni, 11208a1b9b6aSSam Leffler arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 11218a1b9b6aSSam Leffler if_start(ifp); /* XXX not authorized yet */ 11221a1e1d21SSam Leffler break; 11231a1e1d21SSam Leffler } 1124e99662a6SSam Leffler if (ostate != IEEE80211_S_RUN && 1125e99662a6SSam Leffler ic->ic_opmode == IEEE80211_M_STA && 1126e99662a6SSam Leffler (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { 1127e99662a6SSam Leffler /* 1128e99662a6SSam Leffler * Start s/w beacon miss timer for devices w/o 1129e99662a6SSam Leffler * hardware support. We fudge a bit here since 1130e99662a6SSam Leffler * we're doing this in software. 1131e99662a6SSam Leffler */ 1132e99662a6SSam Leffler ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( 1133e99662a6SSam Leffler 2 * ic->ic_bmissthreshold * ni->ni_intval); 1134e99662a6SSam Leffler ic->ic_swbmiss_count = 0; 1135e99662a6SSam Leffler callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1136e99662a6SSam Leffler ieee80211_swbmiss, ic); 1137e99662a6SSam Leffler } 11388a1b9b6aSSam Leffler /* 11398a1b9b6aSSam Leffler * Start/stop the authenticator when operating as an 11408a1b9b6aSSam Leffler * AP. We delay until here to allow configuration to 11418a1b9b6aSSam Leffler * happen out of order. 11428a1b9b6aSSam Leffler */ 11438a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 11448a1b9b6aSSam Leffler ic->ic_auth->ia_attach != NULL) { 11458a1b9b6aSSam Leffler /* XXX check failure */ 11468a1b9b6aSSam Leffler ic->ic_auth->ia_attach(ic); 11478a1b9b6aSSam Leffler } else if (ic->ic_auth->ia_detach != NULL) { 11488a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 11498a1b9b6aSSam Leffler } 11508a1b9b6aSSam Leffler /* 11518a1b9b6aSSam Leffler * When 802.1x is not in use mark the port authorized 11528a1b9b6aSSam Leffler * at this point so traffic can flow. 11538a1b9b6aSSam Leffler */ 11548a1b9b6aSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1155e4918ecdSSam Leffler ieee80211_node_authorize(ni); 11568a1b9b6aSSam Leffler /* 11578a1b9b6aSSam Leffler * Enable inactivity processing. 11588a1b9b6aSSam Leffler * XXX 11598a1b9b6aSSam Leffler */ 11608a1b9b6aSSam Leffler ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; 1161acc4f7f5SSam Leffler ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; 11621a1e1d21SSam Leffler break; 11631a1e1d21SSam Leffler } 11641a1e1d21SSam Leffler return 0; 11651a1e1d21SSam Leffler } 1166