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 }; 72a11c9a5cSSam Leffler const char *ieee80211_state_name[IEEE80211_S_MAX] = { 73a11c9a5cSSam Leffler "INIT", /* IEEE80211_S_INIT */ 74a11c9a5cSSam Leffler "SCAN", /* IEEE80211_S_SCAN */ 75a11c9a5cSSam Leffler "AUTH", /* IEEE80211_S_AUTH */ 76a11c9a5cSSam Leffler "ASSOC", /* IEEE80211_S_ASSOC */ 77a11c9a5cSSam Leffler "RUN" /* IEEE80211_S_RUN */ 78a11c9a5cSSam Leffler }; 798a1b9b6aSSam Leffler const char *ieee80211_wme_acnames[] = { 808a1b9b6aSSam Leffler "WME_AC_BE", 818a1b9b6aSSam Leffler "WME_AC_BK", 828a1b9b6aSSam Leffler "WME_AC_VI", 838a1b9b6aSSam Leffler "WME_AC_VO", 848a1b9b6aSSam Leffler "WME_UPSD", 858a1b9b6aSSam Leffler }; 86a11c9a5cSSam Leffler 87a11c9a5cSSam Leffler static int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); 881a1e1d21SSam Leffler 891a1e1d21SSam Leffler void 908a1b9b6aSSam Leffler ieee80211_proto_attach(struct ieee80211com *ic) 911a1e1d21SSam Leffler { 928a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 931a1e1d21SSam Leffler 948a1b9b6aSSam Leffler /* XXX room for crypto */ 958a1b9b6aSSam Leffler ifp->if_hdrlen = sizeof(struct ieee80211_qosframe_addr4); 961a1e1d21SSam Leffler 971a1e1d21SSam Leffler ic->ic_rtsthreshold = IEEE80211_RTS_DEFAULT; 9833acb1ceSSam Leffler ic->ic_fragthreshold = IEEE80211_FRAG_DEFAULT; 992c39b32cSSam Leffler ic->ic_fixed_rate = IEEE80211_FIXED_RATE_NONE; 100e701e041SSam Leffler ic->ic_bmiss_max = IEEE80211_BMISS_MAX; 101e99662a6SSam Leffler callout_init(&ic->ic_swbmiss, CALLOUT_MPSAFE); 10264353cb0SSam Leffler ic->ic_mcast_rate = IEEE80211_MCAST_RATE_DEFAULT; 1032e79ca97SSam Leffler ic->ic_protmode = IEEE80211_PROT_CTSONLY; 1048a1b9b6aSSam Leffler ic->ic_roaming = IEEE80211_ROAMING_AUTO; 1058a1b9b6aSSam Leffler 1068a1b9b6aSSam Leffler ic->ic_wme.wme_hipri_switch_hysteresis = 1078a1b9b6aSSam Leffler AGGRESSIVE_MODE_SWITCH_HYSTERESIS; 1081a1e1d21SSam Leffler 1099bf40edeSBrooks Davis mtx_init(&ic->ic_mgtq.ifq_mtx, ifp->if_xname, "mgmt send q", MTX_DEF); 1101a1e1d21SSam Leffler 111a11c9a5cSSam Leffler /* protocol state change handler */ 112a11c9a5cSSam Leffler ic->ic_newstate = ieee80211_newstate; 113a11c9a5cSSam Leffler 1141a1e1d21SSam Leffler /* initialize management frame handlers */ 1151a1e1d21SSam Leffler ic->ic_recv_mgmt = ieee80211_recv_mgmt; 1161a1e1d21SSam Leffler ic->ic_send_mgmt = ieee80211_send_mgmt; 117246b5467SSam Leffler ic->ic_raw_xmit = ieee80211_raw_xmit; 1181a1e1d21SSam Leffler } 1191a1e1d21SSam Leffler 1201a1e1d21SSam Leffler void 1218a1b9b6aSSam Leffler ieee80211_proto_detach(struct ieee80211com *ic) 1221a1e1d21SSam Leffler { 1238a1b9b6aSSam Leffler 1248a1b9b6aSSam Leffler /* 1258a1b9b6aSSam Leffler * This should not be needed as we detach when reseting 1268a1b9b6aSSam Leffler * the state but be conservative here since the 1278a1b9b6aSSam Leffler * authenticator may do things like spawn kernel threads. 1288a1b9b6aSSam Leffler */ 1298a1b9b6aSSam Leffler if (ic->ic_auth->ia_detach) 1308a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 1311a1e1d21SSam Leffler 1321a1e1d21SSam Leffler IF_DRAIN(&ic->ic_mgtq); 1331a1e1d21SSam Leffler mtx_destroy(&ic->ic_mgtq.ifq_mtx); 1348a1b9b6aSSam Leffler 1358a1b9b6aSSam Leffler /* 1368a1b9b6aSSam Leffler * Detach any ACL'ator. 1378a1b9b6aSSam Leffler */ 1388a1b9b6aSSam Leffler if (ic->ic_acl != NULL) 1398a1b9b6aSSam Leffler ic->ic_acl->iac_detach(ic); 1408a1b9b6aSSam Leffler } 1418a1b9b6aSSam Leffler 1428a1b9b6aSSam Leffler /* 1438a1b9b6aSSam Leffler * Simple-minded authenticator module support. 1448a1b9b6aSSam Leffler */ 1458a1b9b6aSSam Leffler 1468a1b9b6aSSam Leffler #define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) 1478a1b9b6aSSam Leffler /* XXX well-known names */ 1488a1b9b6aSSam Leffler static const char *auth_modnames[IEEE80211_AUTH_MAX] = { 1498a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_NONE */ 1508a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_OPEN */ 1518a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_SHARED */ 1528a1b9b6aSSam Leffler "wlan_xauth", /* IEEE80211_AUTH_8021X */ 1538a1b9b6aSSam Leffler "wlan_internal", /* IEEE80211_AUTH_AUTO */ 1548a1b9b6aSSam Leffler "wlan_xauth", /* IEEE80211_AUTH_WPA */ 1558a1b9b6aSSam Leffler }; 1568a1b9b6aSSam Leffler static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; 1578a1b9b6aSSam Leffler 1588a1b9b6aSSam Leffler static const struct ieee80211_authenticator auth_internal = { 1598a1b9b6aSSam Leffler .ia_name = "wlan_internal", 1608a1b9b6aSSam Leffler .ia_attach = NULL, 1618a1b9b6aSSam Leffler .ia_detach = NULL, 1628a1b9b6aSSam Leffler .ia_node_join = NULL, 1638a1b9b6aSSam Leffler .ia_node_leave = NULL, 1648a1b9b6aSSam Leffler }; 1658a1b9b6aSSam Leffler 1668a1b9b6aSSam Leffler /* 1678a1b9b6aSSam Leffler * Setup internal authenticators once; they are never unregistered. 1688a1b9b6aSSam Leffler */ 1698a1b9b6aSSam Leffler static void 1708a1b9b6aSSam Leffler ieee80211_auth_setup(void) 1718a1b9b6aSSam Leffler { 1728a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); 1738a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); 1748a1b9b6aSSam Leffler ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); 1758a1b9b6aSSam Leffler } 1768a1b9b6aSSam Leffler SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); 1778a1b9b6aSSam Leffler 1788a1b9b6aSSam Leffler const struct ieee80211_authenticator * 1798a1b9b6aSSam Leffler ieee80211_authenticator_get(int auth) 1808a1b9b6aSSam Leffler { 1818a1b9b6aSSam Leffler if (auth >= IEEE80211_AUTH_MAX) 1828a1b9b6aSSam Leffler return NULL; 1838a1b9b6aSSam Leffler if (authenticators[auth] == NULL) 1848a1b9b6aSSam Leffler ieee80211_load_module(auth_modnames[auth]); 1858a1b9b6aSSam Leffler return authenticators[auth]; 1861a1e1d21SSam Leffler } 1871a1e1d21SSam Leffler 1881a1e1d21SSam Leffler void 1898a1b9b6aSSam Leffler ieee80211_authenticator_register(int type, 1908a1b9b6aSSam Leffler const struct ieee80211_authenticator *auth) 1911a1e1d21SSam Leffler { 1928a1b9b6aSSam Leffler if (type >= IEEE80211_AUTH_MAX) 1938a1b9b6aSSam Leffler return; 1948a1b9b6aSSam Leffler authenticators[type] = auth; 1958a1b9b6aSSam Leffler } 1968a1b9b6aSSam Leffler 1978a1b9b6aSSam Leffler void 1988a1b9b6aSSam Leffler ieee80211_authenticator_unregister(int type) 1998a1b9b6aSSam Leffler { 2008a1b9b6aSSam Leffler 2018a1b9b6aSSam Leffler if (type >= IEEE80211_AUTH_MAX) 2028a1b9b6aSSam Leffler return; 2038a1b9b6aSSam Leffler authenticators[type] = NULL; 2048a1b9b6aSSam Leffler } 2058a1b9b6aSSam Leffler 2068a1b9b6aSSam Leffler /* 2078a1b9b6aSSam Leffler * Very simple-minded ACL module support. 2088a1b9b6aSSam Leffler */ 2098a1b9b6aSSam Leffler /* XXX just one for now */ 2108a1b9b6aSSam Leffler static const struct ieee80211_aclator *acl = NULL; 2118a1b9b6aSSam Leffler 2128a1b9b6aSSam Leffler void 2138a1b9b6aSSam Leffler ieee80211_aclator_register(const struct ieee80211_aclator *iac) 2148a1b9b6aSSam Leffler { 2158a1b9b6aSSam Leffler printf("wlan: %s acl policy registered\n", iac->iac_name); 2168a1b9b6aSSam Leffler acl = iac; 2178a1b9b6aSSam Leffler } 2188a1b9b6aSSam Leffler 2198a1b9b6aSSam Leffler void 2208a1b9b6aSSam Leffler ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) 2218a1b9b6aSSam Leffler { 2228a1b9b6aSSam Leffler if (acl == iac) 2238a1b9b6aSSam Leffler acl = NULL; 2248a1b9b6aSSam Leffler printf("wlan: %s acl policy unregistered\n", iac->iac_name); 2258a1b9b6aSSam Leffler } 2268a1b9b6aSSam Leffler 2278a1b9b6aSSam Leffler const struct ieee80211_aclator * 2288a1b9b6aSSam Leffler ieee80211_aclator_get(const char *name) 2298a1b9b6aSSam Leffler { 2308a1b9b6aSSam Leffler if (acl == NULL) 2318a1b9b6aSSam Leffler ieee80211_load_module("wlan_acl"); 2328a1b9b6aSSam Leffler return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; 2338a1b9b6aSSam Leffler } 2348a1b9b6aSSam Leffler 2358a1b9b6aSSam Leffler void 2368a1b9b6aSSam Leffler ieee80211_print_essid(const u_int8_t *essid, int len) 2378a1b9b6aSSam Leffler { 2388a1b9b6aSSam Leffler const u_int8_t *p; 2391a1e1d21SSam Leffler int i; 2401a1e1d21SSam Leffler 2411a1e1d21SSam Leffler if (len > IEEE80211_NWID_LEN) 2421a1e1d21SSam Leffler len = IEEE80211_NWID_LEN; 2431a1e1d21SSam Leffler /* determine printable or not */ 2441a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) { 2451a1e1d21SSam Leffler if (*p < ' ' || *p > 0x7e) 2461a1e1d21SSam Leffler break; 2471a1e1d21SSam Leffler } 2481a1e1d21SSam Leffler if (i == len) { 2491a1e1d21SSam Leffler printf("\""); 2501a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) 2511a1e1d21SSam Leffler printf("%c", *p); 2521a1e1d21SSam Leffler printf("\""); 2531a1e1d21SSam Leffler } else { 2541a1e1d21SSam Leffler printf("0x"); 2551a1e1d21SSam Leffler for (i = 0, p = essid; i < len; i++, p++) 2561a1e1d21SSam Leffler printf("%02x", *p); 2571a1e1d21SSam Leffler } 2581a1e1d21SSam Leffler } 2591a1e1d21SSam Leffler 2601a1e1d21SSam Leffler void 2618a1b9b6aSSam Leffler ieee80211_dump_pkt(const u_int8_t *buf, int len, int rate, int rssi) 2621a1e1d21SSam Leffler { 2638a1b9b6aSSam Leffler const struct ieee80211_frame *wh; 2641a1e1d21SSam Leffler int i; 2651a1e1d21SSam Leffler 2668a1b9b6aSSam Leffler wh = (const struct ieee80211_frame *)buf; 2671a1e1d21SSam Leffler switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { 2681a1e1d21SSam Leffler case IEEE80211_FC1_DIR_NODS: 2691a1e1d21SSam Leffler printf("NODS %s", ether_sprintf(wh->i_addr2)); 2701a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr1)); 2711a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr3)); 2721a1e1d21SSam Leffler break; 2731a1e1d21SSam Leffler case IEEE80211_FC1_DIR_TODS: 2741a1e1d21SSam Leffler printf("TODS %s", ether_sprintf(wh->i_addr2)); 2751a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr3)); 2761a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr1)); 2771a1e1d21SSam Leffler break; 2781a1e1d21SSam Leffler case IEEE80211_FC1_DIR_FROMDS: 2791a1e1d21SSam Leffler printf("FRDS %s", ether_sprintf(wh->i_addr3)); 2801a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr1)); 2811a1e1d21SSam Leffler printf("(%s)", ether_sprintf(wh->i_addr2)); 2821a1e1d21SSam Leffler break; 2831a1e1d21SSam Leffler case IEEE80211_FC1_DIR_DSTODS: 2848a1b9b6aSSam Leffler printf("DSDS %s", ether_sprintf((const u_int8_t *)&wh[1])); 2851a1e1d21SSam Leffler printf("->%s", ether_sprintf(wh->i_addr3)); 2861a1e1d21SSam Leffler printf("(%s", ether_sprintf(wh->i_addr2)); 2871a1e1d21SSam Leffler printf("->%s)", ether_sprintf(wh->i_addr1)); 2881a1e1d21SSam Leffler break; 2891a1e1d21SSam Leffler } 2901a1e1d21SSam Leffler switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { 2911a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_DATA: 2921a1e1d21SSam Leffler printf(" data"); 2931a1e1d21SSam Leffler break; 2941a1e1d21SSam Leffler case IEEE80211_FC0_TYPE_MGT: 2951a1e1d21SSam Leffler printf(" %s", ieee80211_mgt_subtype_name[ 2961a1e1d21SSam Leffler (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) 2971a1e1d21SSam Leffler >> IEEE80211_FC0_SUBTYPE_SHIFT]); 2981a1e1d21SSam Leffler break; 2991a1e1d21SSam Leffler default: 3001a1e1d21SSam Leffler printf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); 3011a1e1d21SSam Leffler break; 3021a1e1d21SSam Leffler } 3038a1b9b6aSSam Leffler if (wh->i_fc[1] & IEEE80211_FC1_WEP) { 3048a1b9b6aSSam Leffler int i; 3058a1b9b6aSSam Leffler printf(" WEP [IV"); 3068a1b9b6aSSam Leffler for (i = 0; i < IEEE80211_WEP_IVLEN; i++) 3078a1b9b6aSSam Leffler printf(" %.02x", buf[sizeof(*wh)+i]); 3088a1b9b6aSSam Leffler printf(" KID %u]", buf[sizeof(*wh)+i] >> 6); 3098a1b9b6aSSam Leffler } 3101a1e1d21SSam Leffler if (rate >= 0) 3111a1e1d21SSam Leffler printf(" %dM", rate / 2); 3121a1e1d21SSam Leffler if (rssi >= 0) 3131a1e1d21SSam Leffler printf(" +%d", rssi); 3141a1e1d21SSam Leffler printf("\n"); 3151a1e1d21SSam Leffler if (len > 0) { 3161a1e1d21SSam Leffler for (i = 0; i < len; i++) { 3171a1e1d21SSam Leffler if ((i & 1) == 0) 3181a1e1d21SSam Leffler printf(" "); 3191a1e1d21SSam Leffler printf("%02x", buf[i]); 3201a1e1d21SSam Leffler } 3211a1e1d21SSam Leffler printf("\n"); 3221a1e1d21SSam Leffler } 3231a1e1d21SSam Leffler } 3241a1e1d21SSam Leffler 32579edaebfSSam Leffler static __inline int 32679edaebfSSam Leffler findrix(const struct ieee80211_rateset *rs, int r) 32779edaebfSSam Leffler { 32879edaebfSSam Leffler int i; 32979edaebfSSam Leffler 33079edaebfSSam Leffler for (i = 0; i < rs->rs_nrates; i++) 33179edaebfSSam Leffler if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) 33279edaebfSSam Leffler return i; 33379edaebfSSam Leffler return -1; 33479edaebfSSam Leffler } 33579edaebfSSam Leffler 3361a1e1d21SSam Leffler int 3377d77cd53SSam Leffler ieee80211_fix_rate(struct ieee80211_node *ni, int flags) 3381a1e1d21SSam Leffler { 3391a1e1d21SSam Leffler #define RV(v) ((v) & IEEE80211_RATE_VAL) 3407d77cd53SSam Leffler struct ieee80211com *ic = ni->ni_ic; 34179edaebfSSam Leffler int i, j, rix, error; 3428a1b9b6aSSam Leffler int okrate, badrate, fixedrate; 34341b3c790SSam Leffler const struct ieee80211_rateset *srs; 34441b3c790SSam Leffler struct ieee80211_rateset *nrs; 3451a1e1d21SSam Leffler u_int8_t r; 3461a1e1d21SSam Leffler 3478a1b9b6aSSam Leffler /* 3488a1b9b6aSSam Leffler * If the fixed rate check was requested but no 3498a1b9b6aSSam Leffler * fixed has been defined then just remove it. 3508a1b9b6aSSam Leffler */ 3512c39b32cSSam Leffler if ((flags & IEEE80211_F_DOFRATE) && 3522c39b32cSSam Leffler ic->ic_fixed_rate == IEEE80211_FIXED_RATE_NONE) 3538a1b9b6aSSam Leffler flags &= ~IEEE80211_F_DOFRATE; 3541a1e1d21SSam Leffler error = 0; 3558a1b9b6aSSam Leffler okrate = badrate = fixedrate = 0; 35641b3c790SSam Leffler srs = ieee80211_get_suprates(ic, ni->ni_chan); 3571a1e1d21SSam Leffler nrs = &ni->ni_rates; 358ef39d4beSSam Leffler for (i = 0; i < nrs->rs_nrates; ) { 3591a1e1d21SSam Leffler if (flags & IEEE80211_F_DOSORT) { 3601a1e1d21SSam Leffler /* 3611a1e1d21SSam Leffler * Sort rates. 3621a1e1d21SSam Leffler */ 3631a1e1d21SSam Leffler for (j = i + 1; j < nrs->rs_nrates; j++) { 3641a1e1d21SSam Leffler if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { 3651a1e1d21SSam Leffler r = nrs->rs_rates[i]; 3661a1e1d21SSam Leffler nrs->rs_rates[i] = nrs->rs_rates[j]; 3671a1e1d21SSam Leffler nrs->rs_rates[j] = r; 3681a1e1d21SSam Leffler } 3691a1e1d21SSam Leffler } 3701a1e1d21SSam Leffler } 3711a1e1d21SSam Leffler r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; 3721a1e1d21SSam Leffler badrate = r; 3731a1e1d21SSam Leffler if (flags & IEEE80211_F_DOFRATE) { 3741a1e1d21SSam Leffler /* 3758a1b9b6aSSam Leffler * Check any fixed rate is included. 3761a1e1d21SSam Leffler */ 3778a1b9b6aSSam Leffler if (r == RV(srs->rs_rates[ic->ic_fixed_rate])) 3788a1b9b6aSSam Leffler fixedrate = r; 3791a1e1d21SSam Leffler } 3801a1e1d21SSam Leffler /* 3811a1e1d21SSam Leffler * Check against supported rates. 3821a1e1d21SSam Leffler */ 38379edaebfSSam Leffler rix = findrix(srs, r); 38479edaebfSSam Leffler if (flags & IEEE80211_F_DONEGO) { 38579edaebfSSam Leffler if (rix < 0) { 386ef39d4beSSam Leffler /* 387ef39d4beSSam Leffler * A rate in the node's rate set is not 388ef39d4beSSam Leffler * supported. If this is a basic rate and we 38979edaebfSSam Leffler * are operating as a STA then this is an error. 390ef39d4beSSam Leffler * Otherwise we just discard/ignore the rate. 391ef39d4beSSam Leffler */ 39279edaebfSSam Leffler if ((flags & IEEE80211_F_JOIN) && 393ef39d4beSSam Leffler (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) 3941a1e1d21SSam Leffler error++; 39579edaebfSSam Leffler } else if ((flags & IEEE80211_F_JOIN) == 0) { 39679edaebfSSam Leffler /* 39779edaebfSSam Leffler * Overwrite with the supported rate 39879edaebfSSam Leffler * value so any basic rate bit is set. 39979edaebfSSam Leffler */ 40079edaebfSSam Leffler nrs->rs_rates[i] = srs->rs_rates[rix]; 4011a1e1d21SSam Leffler } 4021a1e1d21SSam Leffler } 40379edaebfSSam Leffler if ((flags & IEEE80211_F_DODEL) && rix < 0) { 4041a1e1d21SSam Leffler /* 4051a1e1d21SSam Leffler * Delete unacceptable rates. 4061a1e1d21SSam Leffler */ 4071a1e1d21SSam Leffler nrs->rs_nrates--; 4081a1e1d21SSam Leffler for (j = i; j < nrs->rs_nrates; j++) 4091a1e1d21SSam Leffler nrs->rs_rates[j] = nrs->rs_rates[j + 1]; 4101a1e1d21SSam Leffler nrs->rs_rates[j] = 0; 4111a1e1d21SSam Leffler continue; 4121a1e1d21SSam Leffler } 41379edaebfSSam Leffler if (rix >= 0) 4141a1e1d21SSam Leffler okrate = nrs->rs_rates[i]; 4151a1e1d21SSam Leffler i++; 4161a1e1d21SSam Leffler } 4178a1b9b6aSSam Leffler if (okrate == 0 || error != 0 || 4188a1b9b6aSSam Leffler ((flags & IEEE80211_F_DOFRATE) && fixedrate == 0)) 4191a1e1d21SSam Leffler return badrate | IEEE80211_RATE_BASIC; 4201a1e1d21SSam Leffler else 4211a1e1d21SSam Leffler return RV(okrate); 4221a1e1d21SSam Leffler #undef RV 4231a1e1d21SSam Leffler } 4241a1e1d21SSam Leffler 4258a1b9b6aSSam Leffler /* 4268a1b9b6aSSam Leffler * Reset 11g-related state. 4278a1b9b6aSSam Leffler */ 4288a1b9b6aSSam Leffler void 4298a1b9b6aSSam Leffler ieee80211_reset_erp(struct ieee80211com *ic) 4301a1e1d21SSam Leffler { 4318a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEPROT; 4328a1b9b6aSSam Leffler ic->ic_nonerpsta = 0; 4338a1b9b6aSSam Leffler ic->ic_longslotsta = 0; 4348a1b9b6aSSam Leffler /* 4358a1b9b6aSSam Leffler * Short slot time is enabled only when operating in 11g 4368a1b9b6aSSam Leffler * and not in an IBSS. We must also honor whether or not 4378a1b9b6aSSam Leffler * the driver is capable of doing it. 4388a1b9b6aSSam Leffler */ 4398a1b9b6aSSam Leffler ieee80211_set_shortslottime(ic, 4408a1b9b6aSSam Leffler ic->ic_curmode == IEEE80211_MODE_11A || 4418a1b9b6aSSam Leffler (ic->ic_curmode == IEEE80211_MODE_11G && 4428a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_HOSTAP && 4438a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHSLOT))); 4448a1b9b6aSSam Leffler /* 4458a1b9b6aSSam Leffler * Set short preamble and ERP barker-preamble flags. 4468a1b9b6aSSam Leffler */ 4478a1b9b6aSSam Leffler if (ic->ic_curmode == IEEE80211_MODE_11A || 4488a1b9b6aSSam Leffler (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { 4498a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHPREAMBLE; 4508a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_USEBARKER; 4518a1b9b6aSSam Leffler } else { 4528a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; 4538a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_USEBARKER; 4548a1b9b6aSSam Leffler } 4558a1b9b6aSSam Leffler } 4568a1b9b6aSSam Leffler 4578a1b9b6aSSam Leffler /* 4588a1b9b6aSSam Leffler * Set the short slot time state and notify the driver. 4598a1b9b6aSSam Leffler */ 4608a1b9b6aSSam Leffler void 4618a1b9b6aSSam Leffler ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) 4628a1b9b6aSSam Leffler { 4638a1b9b6aSSam Leffler if (onoff) 4648a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_SHSLOT; 4658a1b9b6aSSam Leffler else 4668a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SHSLOT; 4678a1b9b6aSSam Leffler /* notify driver */ 4688a1b9b6aSSam Leffler if (ic->ic_updateslot != NULL) 4698a1b9b6aSSam Leffler ic->ic_updateslot(ic->ic_ifp); 4708a1b9b6aSSam Leffler } 4718a1b9b6aSSam Leffler 4728a1b9b6aSSam Leffler /* 4738a1b9b6aSSam Leffler * Check if the specified rate set supports ERP. 4748a1b9b6aSSam Leffler * NB: the rate set is assumed to be sorted. 4758a1b9b6aSSam Leffler */ 4768a1b9b6aSSam Leffler int 4778a1b9b6aSSam Leffler ieee80211_iserp_rateset(struct ieee80211com *ic, struct ieee80211_rateset *rs) 4788a1b9b6aSSam Leffler { 4798a1b9b6aSSam Leffler #define N(a) (sizeof(a) / sizeof(a[0])) 4808a1b9b6aSSam Leffler static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; 4818a1b9b6aSSam Leffler int i, j; 4828a1b9b6aSSam Leffler 4838a1b9b6aSSam Leffler if (rs->rs_nrates < N(rates)) 4848a1b9b6aSSam Leffler return 0; 4858a1b9b6aSSam Leffler for (i = 0; i < N(rates); i++) { 4868a1b9b6aSSam Leffler for (j = 0; j < rs->rs_nrates; j++) { 4878a1b9b6aSSam Leffler int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; 4888a1b9b6aSSam Leffler if (rates[i] == r) 4898a1b9b6aSSam Leffler goto next; 4908a1b9b6aSSam Leffler if (r > rates[i]) 4918a1b9b6aSSam Leffler return 0; 4928a1b9b6aSSam Leffler } 4938a1b9b6aSSam Leffler return 0; 4948a1b9b6aSSam Leffler next: 4958a1b9b6aSSam Leffler ; 4968a1b9b6aSSam Leffler } 4978a1b9b6aSSam Leffler return 1; 4988a1b9b6aSSam Leffler #undef N 4998a1b9b6aSSam Leffler } 5008a1b9b6aSSam Leffler 5018a1b9b6aSSam Leffler /* 5028a1b9b6aSSam Leffler * Mark the basic rates for the 11g rate table based on the 5038a1b9b6aSSam Leffler * operating mode. For real 11g we mark all the 11b rates 5048a1b9b6aSSam Leffler * and 6, 12, and 24 OFDM. For 11b compatibility we mark only 5058a1b9b6aSSam Leffler * 11b rates. There's also a pseudo 11a-mode used to mark only 5068a1b9b6aSSam Leffler * the basic OFDM rates. 5078a1b9b6aSSam Leffler */ 5088a1b9b6aSSam Leffler void 5098a1b9b6aSSam Leffler ieee80211_set11gbasicrates(struct ieee80211_rateset *rs, enum ieee80211_phymode mode) 5108a1b9b6aSSam Leffler { 5118a1b9b6aSSam Leffler static const struct ieee80211_rateset basic[] = { 5128a1b9b6aSSam Leffler { 0 }, /* IEEE80211_MODE_AUTO */ 5138a1b9b6aSSam Leffler { 3, { 12, 24, 48 } }, /* IEEE80211_MODE_11A */ 5148a1b9b6aSSam Leffler { 2, { 2, 4 } }, /* IEEE80211_MODE_11B */ 5158a1b9b6aSSam Leffler { 4, { 2, 4, 11, 22 } }, /* IEEE80211_MODE_11G (mixed b/g) */ 5168a1b9b6aSSam Leffler { 0 }, /* IEEE80211_MODE_FH */ 5178a1b9b6aSSam Leffler /* IEEE80211_MODE_PUREG (not yet) */ 5188a1b9b6aSSam Leffler { 7, { 2, 4, 11, 22, 12, 24, 48 } }, 5198a1b9b6aSSam Leffler }; 5208a1b9b6aSSam Leffler int i, j; 5218a1b9b6aSSam Leffler 5228a1b9b6aSSam Leffler for (i = 0; i < rs->rs_nrates; i++) { 5238a1b9b6aSSam Leffler rs->rs_rates[i] &= IEEE80211_RATE_VAL; 5248a1b9b6aSSam Leffler for (j = 0; j < basic[mode].rs_nrates; j++) 5258a1b9b6aSSam Leffler if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { 5268a1b9b6aSSam Leffler rs->rs_rates[i] |= IEEE80211_RATE_BASIC; 5278a1b9b6aSSam Leffler break; 5288a1b9b6aSSam Leffler } 5298a1b9b6aSSam Leffler } 5308a1b9b6aSSam Leffler } 5318a1b9b6aSSam Leffler 5328a1b9b6aSSam Leffler /* 5338a1b9b6aSSam Leffler * WME protocol support. The following parameters come from the spec. 5348a1b9b6aSSam Leffler */ 5358a1b9b6aSSam Leffler typedef struct phyParamType { 5368a1b9b6aSSam Leffler u_int8_t aifsn; 5378a1b9b6aSSam Leffler u_int8_t logcwmin; 5388a1b9b6aSSam Leffler u_int8_t logcwmax; 5398a1b9b6aSSam Leffler u_int16_t txopLimit; 5408a1b9b6aSSam Leffler u_int8_t acm; 5418a1b9b6aSSam Leffler } paramType; 5428a1b9b6aSSam Leffler 5438a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { 5448a1b9b6aSSam Leffler { 3, 4, 6 }, /* IEEE80211_MODE_AUTO */ 5458a1b9b6aSSam Leffler { 3, 4, 6 }, /* IEEE80211_MODE_11A */ 5468a1b9b6aSSam Leffler { 3, 5, 7 }, /* IEEE80211_MODE_11B */ 5478a1b9b6aSSam Leffler { 3, 4, 6 }, /* IEEE80211_MODE_11G */ 5488a1b9b6aSSam Leffler { 3, 5, 7 }, /* IEEE80211_MODE_FH */ 5498a1b9b6aSSam Leffler { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_A */ 5508a1b9b6aSSam Leffler { 2, 3, 5 }, /* IEEE80211_MODE_TURBO_G */ 5518a1b9b6aSSam Leffler }; 5528a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { 5538a1b9b6aSSam Leffler { 7, 4, 10 }, /* IEEE80211_MODE_AUTO */ 5548a1b9b6aSSam Leffler { 7, 4, 10 }, /* IEEE80211_MODE_11A */ 5558a1b9b6aSSam Leffler { 7, 5, 10 }, /* IEEE80211_MODE_11B */ 5568a1b9b6aSSam Leffler { 7, 4, 10 }, /* IEEE80211_MODE_11G */ 5578a1b9b6aSSam Leffler { 7, 5, 10 }, /* IEEE80211_MODE_FH */ 5588a1b9b6aSSam Leffler { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 5598a1b9b6aSSam Leffler { 7, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 5608a1b9b6aSSam Leffler }; 5618a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { 5628a1b9b6aSSam Leffler { 1, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 5638a1b9b6aSSam Leffler { 1, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 5648a1b9b6aSSam Leffler { 1, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 5658a1b9b6aSSam Leffler { 1, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 5668a1b9b6aSSam Leffler { 1, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 5678a1b9b6aSSam Leffler { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 5688a1b9b6aSSam Leffler { 1, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 5698a1b9b6aSSam Leffler }; 5708a1b9b6aSSam Leffler static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { 5718a1b9b6aSSam Leffler { 1, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 5728a1b9b6aSSam Leffler { 1, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 5738a1b9b6aSSam Leffler { 1, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 5748a1b9b6aSSam Leffler { 1, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 5758a1b9b6aSSam Leffler { 1, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 5768a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 5778a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 5788a1b9b6aSSam Leffler }; 5798a1b9b6aSSam Leffler 5808a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { 5818a1b9b6aSSam Leffler { 3, 4, 10 }, /* IEEE80211_MODE_AUTO */ 5828a1b9b6aSSam Leffler { 3, 4, 10 }, /* IEEE80211_MODE_11A */ 5838a1b9b6aSSam Leffler { 3, 5, 10 }, /* IEEE80211_MODE_11B */ 5848a1b9b6aSSam Leffler { 3, 4, 10 }, /* IEEE80211_MODE_11G */ 5858a1b9b6aSSam Leffler { 3, 5, 10 }, /* IEEE80211_MODE_FH */ 5868a1b9b6aSSam Leffler { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_A */ 5878a1b9b6aSSam Leffler { 2, 3, 10 }, /* IEEE80211_MODE_TURBO_G */ 5888a1b9b6aSSam Leffler }; 5898a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { 5908a1b9b6aSSam Leffler { 2, 3, 4, 94 }, /* IEEE80211_MODE_AUTO */ 5918a1b9b6aSSam Leffler { 2, 3, 4, 94 }, /* IEEE80211_MODE_11A */ 5928a1b9b6aSSam Leffler { 2, 4, 5, 188 }, /* IEEE80211_MODE_11B */ 5938a1b9b6aSSam Leffler { 2, 3, 4, 94 }, /* IEEE80211_MODE_11G */ 5948a1b9b6aSSam Leffler { 2, 4, 5, 188 }, /* IEEE80211_MODE_FH */ 5958a1b9b6aSSam Leffler { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_A */ 5968a1b9b6aSSam Leffler { 2, 2, 3, 94 }, /* IEEE80211_MODE_TURBO_G */ 5978a1b9b6aSSam Leffler }; 5988a1b9b6aSSam Leffler static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { 5998a1b9b6aSSam Leffler { 2, 2, 3, 47 }, /* IEEE80211_MODE_AUTO */ 6008a1b9b6aSSam Leffler { 2, 2, 3, 47 }, /* IEEE80211_MODE_11A */ 6018a1b9b6aSSam Leffler { 2, 3, 4, 102 }, /* IEEE80211_MODE_11B */ 6028a1b9b6aSSam Leffler { 2, 2, 3, 47 }, /* IEEE80211_MODE_11G */ 6038a1b9b6aSSam Leffler { 2, 3, 4, 102 }, /* IEEE80211_MODE_FH */ 6048a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_A */ 6058a1b9b6aSSam Leffler { 1, 2, 2, 47 }, /* IEEE80211_MODE_TURBO_G */ 6068a1b9b6aSSam Leffler }; 6078a1b9b6aSSam Leffler 6088a1b9b6aSSam Leffler void 6098a1b9b6aSSam Leffler ieee80211_wme_initparams(struct ieee80211com *ic) 6108a1b9b6aSSam Leffler { 6118a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 6128a1b9b6aSSam Leffler const paramType *pPhyParam, *pBssPhyParam; 6138a1b9b6aSSam Leffler struct wmeParams *wmep; 6148a1b9b6aSSam Leffler int i; 6158a1b9b6aSSam Leffler 6168a1b9b6aSSam Leffler if ((ic->ic_caps & IEEE80211_C_WME) == 0) 6178a1b9b6aSSam Leffler return; 6188a1b9b6aSSam Leffler 6198a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 6208a1b9b6aSSam Leffler switch (i) { 6218a1b9b6aSSam Leffler case WME_AC_BK: 6228a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 6238a1b9b6aSSam Leffler pBssPhyParam = &phyParamForAC_BK[ic->ic_curmode]; 6248a1b9b6aSSam Leffler break; 6258a1b9b6aSSam Leffler case WME_AC_VI: 6268a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_VI[ic->ic_curmode]; 6278a1b9b6aSSam Leffler pBssPhyParam = &bssPhyParamForAC_VI[ic->ic_curmode]; 6288a1b9b6aSSam Leffler break; 6298a1b9b6aSSam Leffler case WME_AC_VO: 6308a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_VO[ic->ic_curmode]; 6318a1b9b6aSSam Leffler pBssPhyParam = &bssPhyParamForAC_VO[ic->ic_curmode]; 6328a1b9b6aSSam Leffler break; 6338a1b9b6aSSam Leffler case WME_AC_BE: 6348a1b9b6aSSam Leffler default: 6358a1b9b6aSSam Leffler pPhyParam = &phyParamForAC_BE[ic->ic_curmode]; 6368a1b9b6aSSam Leffler pBssPhyParam = &bssPhyParamForAC_BE[ic->ic_curmode]; 6378a1b9b6aSSam Leffler break; 6388a1b9b6aSSam Leffler } 6398a1b9b6aSSam Leffler 6408a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 6418a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { 6428a1b9b6aSSam Leffler wmep->wmep_acm = pPhyParam->acm; 6438a1b9b6aSSam Leffler wmep->wmep_aifsn = pPhyParam->aifsn; 6448a1b9b6aSSam Leffler wmep->wmep_logcwmin = pPhyParam->logcwmin; 6458a1b9b6aSSam Leffler wmep->wmep_logcwmax = pPhyParam->logcwmax; 6468a1b9b6aSSam Leffler wmep->wmep_txopLimit = pPhyParam->txopLimit; 6478a1b9b6aSSam Leffler } else { 6488a1b9b6aSSam Leffler wmep->wmep_acm = pBssPhyParam->acm; 6498a1b9b6aSSam Leffler wmep->wmep_aifsn = pBssPhyParam->aifsn; 6508a1b9b6aSSam Leffler wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 6518a1b9b6aSSam Leffler wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 6528a1b9b6aSSam Leffler wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 6538a1b9b6aSSam Leffler 6548a1b9b6aSSam Leffler } 6558a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 6568a1b9b6aSSam Leffler "%s: %s chan [acm %u aifsn %u log2(cwmin) %u " 6578a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 6588a1b9b6aSSam Leffler , ieee80211_wme_acnames[i] 6598a1b9b6aSSam Leffler , wmep->wmep_acm 6608a1b9b6aSSam Leffler , wmep->wmep_aifsn 6618a1b9b6aSSam Leffler , wmep->wmep_logcwmin 6628a1b9b6aSSam Leffler , wmep->wmep_logcwmax 6638a1b9b6aSSam Leffler , wmep->wmep_txopLimit 6648a1b9b6aSSam Leffler ); 6658a1b9b6aSSam Leffler 6668a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 6678a1b9b6aSSam Leffler wmep->wmep_acm = pBssPhyParam->acm; 6688a1b9b6aSSam Leffler wmep->wmep_aifsn = pBssPhyParam->aifsn; 6698a1b9b6aSSam Leffler wmep->wmep_logcwmin = pBssPhyParam->logcwmin; 6708a1b9b6aSSam Leffler wmep->wmep_logcwmax = pBssPhyParam->logcwmax; 6718a1b9b6aSSam Leffler wmep->wmep_txopLimit = pBssPhyParam->txopLimit; 6728a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 6738a1b9b6aSSam Leffler "%s: %s bss [acm %u aifsn %u log2(cwmin) %u " 6748a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 6758a1b9b6aSSam Leffler , ieee80211_wme_acnames[i] 6768a1b9b6aSSam Leffler , wmep->wmep_acm 6778a1b9b6aSSam Leffler , wmep->wmep_aifsn 6788a1b9b6aSSam Leffler , wmep->wmep_logcwmin 6798a1b9b6aSSam Leffler , wmep->wmep_logcwmax 6808a1b9b6aSSam Leffler , wmep->wmep_txopLimit 6818a1b9b6aSSam Leffler ); 6828a1b9b6aSSam Leffler } 6838a1b9b6aSSam Leffler /* NB: check ic_bss to avoid NULL deref on initial attach */ 6848a1b9b6aSSam Leffler if (ic->ic_bss != NULL) { 6858a1b9b6aSSam Leffler /* 6868a1b9b6aSSam Leffler * Calculate agressive mode switching threshold based 6878a1b9b6aSSam Leffler * on beacon interval. This doesn't need locking since 6888a1b9b6aSSam Leffler * we're only called before entering the RUN state at 6898a1b9b6aSSam Leffler * which point we start sending beacon frames. 6908a1b9b6aSSam Leffler */ 6918a1b9b6aSSam Leffler wme->wme_hipri_switch_thresh = 6928a1b9b6aSSam Leffler (HIGH_PRI_SWITCH_THRESH * ic->ic_bss->ni_intval) / 100; 6938a1b9b6aSSam Leffler ieee80211_wme_updateparams(ic); 6948a1b9b6aSSam Leffler } 6958a1b9b6aSSam Leffler } 6968a1b9b6aSSam Leffler 6978a1b9b6aSSam Leffler /* 6988a1b9b6aSSam Leffler * Update WME parameters for ourself and the BSS. 6998a1b9b6aSSam Leffler */ 7008a1b9b6aSSam Leffler void 7018a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(struct ieee80211com *ic) 7028a1b9b6aSSam Leffler { 7038a1b9b6aSSam Leffler static const paramType phyParam[IEEE80211_MODE_MAX] = { 7048a1b9b6aSSam Leffler { 2, 4, 10, 64 }, /* IEEE80211_MODE_AUTO */ 7058a1b9b6aSSam Leffler { 2, 4, 10, 64 }, /* IEEE80211_MODE_11A */ 7068a1b9b6aSSam Leffler { 2, 5, 10, 64 }, /* IEEE80211_MODE_11B */ 7078a1b9b6aSSam Leffler { 2, 4, 10, 64 }, /* IEEE80211_MODE_11G */ 7088a1b9b6aSSam Leffler { 2, 5, 10, 64 }, /* IEEE80211_MODE_FH */ 7098a1b9b6aSSam Leffler { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_A */ 7108a1b9b6aSSam Leffler { 1, 3, 10, 64 }, /* IEEE80211_MODE_TURBO_G */ 7118a1b9b6aSSam Leffler }; 7128a1b9b6aSSam Leffler struct ieee80211_wme_state *wme = &ic->ic_wme; 7138a1b9b6aSSam Leffler const struct wmeParams *wmep; 7148a1b9b6aSSam Leffler struct wmeParams *chanp, *bssp; 7158a1b9b6aSSam Leffler int i; 7168a1b9b6aSSam Leffler 7178a1b9b6aSSam Leffler /* set up the channel access parameters for the physical device */ 7188a1b9b6aSSam Leffler for (i = 0; i < WME_NUM_AC; i++) { 7198a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[i]; 7208a1b9b6aSSam Leffler wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; 7218a1b9b6aSSam Leffler chanp->wmep_aifsn = wmep->wmep_aifsn; 7228a1b9b6aSSam Leffler chanp->wmep_logcwmin = wmep->wmep_logcwmin; 7238a1b9b6aSSam Leffler chanp->wmep_logcwmax = wmep->wmep_logcwmax; 7248a1b9b6aSSam Leffler chanp->wmep_txopLimit = wmep->wmep_txopLimit; 7258a1b9b6aSSam Leffler 7268a1b9b6aSSam Leffler chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; 7278a1b9b6aSSam Leffler wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; 7288a1b9b6aSSam Leffler chanp->wmep_aifsn = wmep->wmep_aifsn; 7298a1b9b6aSSam Leffler chanp->wmep_logcwmin = wmep->wmep_logcwmin; 7308a1b9b6aSSam Leffler chanp->wmep_logcwmax = wmep->wmep_logcwmax; 7318a1b9b6aSSam Leffler chanp->wmep_txopLimit = wmep->wmep_txopLimit; 7328a1b9b6aSSam Leffler } 7338a1b9b6aSSam Leffler 7348a1b9b6aSSam Leffler /* 7358a1b9b6aSSam Leffler * This implements agressive mode as found in certain 7368a1b9b6aSSam Leffler * vendors' AP's. When there is significant high 7378a1b9b6aSSam Leffler * priority (VI/VO) traffic in the BSS throttle back BE 7388a1b9b6aSSam Leffler * traffic by using conservative parameters. Otherwise 7398a1b9b6aSSam Leffler * BE uses agressive params to optimize performance of 7408a1b9b6aSSam Leffler * legacy/non-QoS traffic. 7418a1b9b6aSSam Leffler */ 7428a1b9b6aSSam Leffler if ((ic->ic_opmode == IEEE80211_M_HOSTAP && 743ad262427SSam Leffler (wme->wme_flags & WME_F_AGGRMODE) != 0) || 74459a44035SSam Leffler (ic->ic_opmode == IEEE80211_M_STA && 7458a1b9b6aSSam Leffler (ic->ic_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || 7468a1b9b6aSSam Leffler (ic->ic_flags & IEEE80211_F_WME) == 0) { 7478a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 7488a1b9b6aSSam Leffler bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 7498a1b9b6aSSam Leffler 7508a1b9b6aSSam Leffler chanp->wmep_aifsn = bssp->wmep_aifsn = 7518a1b9b6aSSam Leffler phyParam[ic->ic_curmode].aifsn; 7528a1b9b6aSSam Leffler chanp->wmep_logcwmin = bssp->wmep_logcwmin = 7538a1b9b6aSSam Leffler phyParam[ic->ic_curmode].logcwmin; 7548a1b9b6aSSam Leffler chanp->wmep_logcwmax = bssp->wmep_logcwmax = 7558a1b9b6aSSam Leffler phyParam[ic->ic_curmode].logcwmax; 7568a1b9b6aSSam Leffler chanp->wmep_txopLimit = bssp->wmep_txopLimit = 757c27e4e31SSam Leffler (ic->ic_flags & IEEE80211_F_BURST) ? 7588a1b9b6aSSam Leffler phyParam[ic->ic_curmode].txopLimit : 0; 7598a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 7608a1b9b6aSSam Leffler "%s: %s [acm %u aifsn %u log2(cwmin) %u " 7618a1b9b6aSSam Leffler "log2(cwmax) %u txpoLimit %u]\n", __func__ 7628a1b9b6aSSam Leffler , ieee80211_wme_acnames[WME_AC_BE] 7638a1b9b6aSSam Leffler , chanp->wmep_acm 7648a1b9b6aSSam Leffler , chanp->wmep_aifsn 7658a1b9b6aSSam Leffler , chanp->wmep_logcwmin 7668a1b9b6aSSam Leffler , chanp->wmep_logcwmax 7678a1b9b6aSSam Leffler , chanp->wmep_txopLimit 7688a1b9b6aSSam Leffler ); 7698a1b9b6aSSam Leffler } 7708a1b9b6aSSam Leffler 7718a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && 772ad262427SSam Leffler ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { 7738a1b9b6aSSam Leffler static const u_int8_t logCwMin[IEEE80211_MODE_MAX] = { 7748a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_AUTO */ 7758a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_11A */ 7768a1b9b6aSSam Leffler 4, /* IEEE80211_MODE_11B */ 7778a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_11G */ 7788a1b9b6aSSam Leffler 4, /* IEEE80211_MODE_FH */ 7798a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_TURBO_A */ 7808a1b9b6aSSam Leffler 3, /* IEEE80211_MODE_TURBO_G */ 7818a1b9b6aSSam Leffler }; 7828a1b9b6aSSam Leffler chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; 7838a1b9b6aSSam Leffler bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; 7848a1b9b6aSSam Leffler 7858a1b9b6aSSam Leffler chanp->wmep_logcwmin = bssp->wmep_logcwmin = 7868a1b9b6aSSam Leffler logCwMin[ic->ic_curmode]; 7878a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 7888a1b9b6aSSam Leffler "%s: %s log2(cwmin) %u\n", __func__ 7898a1b9b6aSSam Leffler , ieee80211_wme_acnames[WME_AC_BE] 7908a1b9b6aSSam Leffler , chanp->wmep_logcwmin 7918a1b9b6aSSam Leffler ); 7928a1b9b6aSSam Leffler } 7938a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ 7948a1b9b6aSSam Leffler /* 7958a1b9b6aSSam Leffler * Arrange for a beacon update and bump the parameter 7968a1b9b6aSSam Leffler * set number so associated stations load the new values. 7978a1b9b6aSSam Leffler */ 7988a1b9b6aSSam Leffler wme->wme_bssChanParams.cap_info = 7998a1b9b6aSSam Leffler (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; 8008a1b9b6aSSam Leffler ic->ic_flags |= IEEE80211_F_WMEUPDATE; 8018a1b9b6aSSam Leffler } 8028a1b9b6aSSam Leffler 8038a1b9b6aSSam Leffler wme->wme_update(ic); 8048a1b9b6aSSam Leffler 8058a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_WME, 8068a1b9b6aSSam Leffler "%s: WME params updated, cap_info 0x%x\n", __func__, 8078a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_STA ? 8088a1b9b6aSSam Leffler wme->wme_wmeChanParams.cap_info : 8098a1b9b6aSSam Leffler wme->wme_bssChanParams.cap_info); 8108a1b9b6aSSam Leffler } 8118a1b9b6aSSam Leffler 8128a1b9b6aSSam Leffler void 8138a1b9b6aSSam Leffler ieee80211_wme_updateparams(struct ieee80211com *ic) 8148a1b9b6aSSam Leffler { 8158a1b9b6aSSam Leffler 8168a1b9b6aSSam Leffler if (ic->ic_caps & IEEE80211_C_WME) { 8178a1b9b6aSSam Leffler IEEE80211_BEACON_LOCK(ic); 8188a1b9b6aSSam Leffler ieee80211_wme_updateparams_locked(ic); 8198a1b9b6aSSam Leffler IEEE80211_BEACON_UNLOCK(ic); 8208a1b9b6aSSam Leffler } 8218a1b9b6aSSam Leffler } 8228a1b9b6aSSam Leffler 823e701e041SSam Leffler void 824e701e041SSam Leffler ieee80211_beacon_miss(struct ieee80211com *ic) 825e701e041SSam Leffler { 826e701e041SSam Leffler 827e701e041SSam Leffler if (ic->ic_flags & IEEE80211_F_SCAN) { 828e701e041SSam Leffler /* XXX check ic_curchan != ic_bsschan? */ 829e701e041SSam Leffler return; 830e701e041SSam Leffler } 831e701e041SSam Leffler IEEE80211_DPRINTF(ic, 832e701e041SSam Leffler IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, 833e701e041SSam Leffler "%s\n", "beacon miss"); 834e701e041SSam Leffler 835e701e041SSam Leffler /* 836e701e041SSam Leffler * Our handling is only meaningful for stations that are 837e701e041SSam Leffler * associated; any other conditions else will be handled 838e701e041SSam Leffler * through different means (e.g. the tx timeout on mgt frames). 839e701e041SSam Leffler */ 840e701e041SSam Leffler if (ic->ic_opmode != IEEE80211_M_STA || ic->ic_state != IEEE80211_S_RUN) 841e701e041SSam Leffler return; 842e701e041SSam Leffler 843e701e041SSam Leffler if (++ic->ic_bmiss_count < ic->ic_bmiss_max) { 844e701e041SSam Leffler /* 845e701e041SSam Leffler * Send a directed probe req before falling back to a scan; 846e701e041SSam Leffler * if we receive a response ic_bmiss_count will be reset. 847e701e041SSam Leffler * Some cards mistakenly report beacon miss so this avoids 848e701e041SSam Leffler * the expensive scan if the ap is still there. 849e701e041SSam Leffler */ 850e701e041SSam Leffler ieee80211_send_probereq(ic->ic_bss, ic->ic_myaddr, 851e701e041SSam Leffler ic->ic_bss->ni_bssid, ic->ic_bss->ni_bssid, 852e701e041SSam Leffler ic->ic_bss->ni_essid, ic->ic_bss->ni_esslen, 853e701e041SSam Leffler ic->ic_opt_ie, ic->ic_opt_ie_len); 854e701e041SSam Leffler return; 855e701e041SSam Leffler } 856e701e041SSam Leffler ic->ic_bmiss_count = 0; 857e701e041SSam Leffler ieee80211_new_state(ic, IEEE80211_S_SCAN, 0); 858e701e041SSam Leffler } 859e701e041SSam Leffler 860e99662a6SSam Leffler /* 861e99662a6SSam Leffler * Software beacon miss handling. Check if any beacons 862e99662a6SSam Leffler * were received in the last period. If not post a 863e99662a6SSam Leffler * beacon miss; otherwise reset the counter. 864e99662a6SSam Leffler */ 865e99662a6SSam Leffler static void 866e99662a6SSam Leffler ieee80211_swbmiss(void *arg) 867e99662a6SSam Leffler { 868e99662a6SSam Leffler struct ieee80211com *ic = arg; 869e99662a6SSam Leffler 870e99662a6SSam Leffler if (ic->ic_swbmiss_count == 0) { 871e99662a6SSam Leffler ieee80211_beacon_miss(ic); 872e99662a6SSam Leffler if (ic->ic_bmiss_count == 0) /* don't re-arm timer */ 873e99662a6SSam Leffler return; 874e99662a6SSam Leffler } else 875e99662a6SSam Leffler ic->ic_swbmiss_count = 0; 876e99662a6SSam Leffler callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 877e99662a6SSam Leffler ieee80211_swbmiss, ic); 878e99662a6SSam Leffler } 879e99662a6SSam Leffler 8807edb8cf9SSam Leffler static void 8817edb8cf9SSam Leffler sta_disassoc(void *arg, struct ieee80211_node *ni) 8827edb8cf9SSam Leffler { 8837edb8cf9SSam Leffler struct ieee80211com *ic = arg; 8847edb8cf9SSam Leffler 8857edb8cf9SSam Leffler if (ni->ni_associd != 0) { 8867edb8cf9SSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DISASSOC, 8877edb8cf9SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 8887edb8cf9SSam Leffler ieee80211_node_leave(ic, ni); 8897edb8cf9SSam Leffler } 8907edb8cf9SSam Leffler } 8917edb8cf9SSam Leffler 8927edb8cf9SSam Leffler static void 8937edb8cf9SSam Leffler sta_deauth(void *arg, struct ieee80211_node *ni) 8947edb8cf9SSam Leffler { 8957edb8cf9SSam Leffler struct ieee80211com *ic = arg; 8967edb8cf9SSam Leffler 8977edb8cf9SSam Leffler IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, 8987edb8cf9SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 8997edb8cf9SSam Leffler } 9007edb8cf9SSam Leffler 9018a1b9b6aSSam Leffler static int 9028a1b9b6aSSam Leffler ieee80211_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) 9038a1b9b6aSSam Leffler { 9048a1b9b6aSSam Leffler struct ifnet *ifp = ic->ic_ifp; 9051a1e1d21SSam Leffler struct ieee80211_node *ni; 906a11c9a5cSSam Leffler enum ieee80211_state ostate; 9071a1e1d21SSam Leffler 9081a1e1d21SSam Leffler ostate = ic->ic_state; 9098a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, "%s: %s -> %s\n", __func__, 9108a1b9b6aSSam Leffler ieee80211_state_name[ostate], ieee80211_state_name[nstate]); 911a11c9a5cSSam Leffler ic->ic_state = nstate; /* state transition */ 9121a1e1d21SSam Leffler ni = ic->ic_bss; /* NB: no reference held */ 913e99662a6SSam Leffler if (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS) 914e99662a6SSam Leffler callout_stop(&ic->ic_swbmiss); 9151a1e1d21SSam Leffler switch (nstate) { 9161a1e1d21SSam Leffler case IEEE80211_S_INIT: 9171a1e1d21SSam Leffler switch (ostate) { 9181a1e1d21SSam Leffler case IEEE80211_S_INIT: 9191a1e1d21SSam Leffler break; 9201a1e1d21SSam Leffler case IEEE80211_S_RUN: 9211a1e1d21SSam Leffler switch (ic->ic_opmode) { 9221a1e1d21SSam Leffler case IEEE80211_M_STA: 9231a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 9241a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DISASSOC, 9251a1e1d21SSam Leffler IEEE80211_REASON_ASSOC_LEAVE); 9268a1b9b6aSSam Leffler ieee80211_sta_leave(ic, ni); 9271a1e1d21SSam Leffler break; 9281a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 9297edb8cf9SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 9307edb8cf9SSam Leffler sta_disassoc, ic); 9311a1e1d21SSam Leffler break; 9321a1e1d21SSam Leffler default: 9331a1e1d21SSam Leffler break; 9341a1e1d21SSam Leffler } 9358a1b9b6aSSam Leffler goto reset; 9361a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 9371a1e1d21SSam Leffler switch (ic->ic_opmode) { 9381a1e1d21SSam Leffler case IEEE80211_M_STA: 9391a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 9401a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_DEAUTH, 9411a1e1d21SSam Leffler IEEE80211_REASON_AUTH_LEAVE); 9421a1e1d21SSam Leffler break; 9431a1e1d21SSam Leffler case IEEE80211_M_HOSTAP: 9447edb8cf9SSam Leffler ieee80211_iterate_nodes(&ic->ic_sta, 9457edb8cf9SSam Leffler sta_deauth, ic); 9461a1e1d21SSam Leffler break; 9471a1e1d21SSam Leffler default: 9481a1e1d21SSam Leffler break; 9491a1e1d21SSam Leffler } 9508a1b9b6aSSam Leffler goto reset; 9511a1e1d21SSam Leffler case IEEE80211_S_SCAN: 952c75ac469SSam Leffler ieee80211_cancel_scan(ic); 953c75ac469SSam Leffler goto reset; 954c75ac469SSam Leffler case IEEE80211_S_AUTH: 9558a1b9b6aSSam Leffler reset: 9561a1e1d21SSam Leffler ic->ic_mgt_timer = 0; 9571a1e1d21SSam Leffler IF_DRAIN(&ic->ic_mgtq); 9588a1b9b6aSSam Leffler ieee80211_reset_bss(ic); 9591a1e1d21SSam Leffler break; 9601a1e1d21SSam Leffler } 9618a1b9b6aSSam Leffler if (ic->ic_auth->ia_detach != NULL) 9628a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 9631a1e1d21SSam Leffler break; 9641a1e1d21SSam Leffler case IEEE80211_S_SCAN: 9651a1e1d21SSam Leffler switch (ostate) { 9661a1e1d21SSam Leffler case IEEE80211_S_INIT: 9678a1b9b6aSSam Leffler if ((ic->ic_opmode == IEEE80211_M_HOSTAP || 9688a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_IBSS || 9698a1b9b6aSSam Leffler ic->ic_opmode == IEEE80211_M_AHDEMO) && 9701a1e1d21SSam Leffler ic->ic_des_chan != IEEE80211_CHAN_ANYC) { 9711a1e1d21SSam Leffler /* 9721a1e1d21SSam Leffler * AP operation and we already have a channel; 9731a1e1d21SSam Leffler * bypass the scan and startup immediately. 9741a1e1d21SSam Leffler */ 9751a1e1d21SSam Leffler ieee80211_create_ibss(ic, ic->ic_des_chan); 9761a1e1d21SSam Leffler } else { 9778a1b9b6aSSam Leffler ieee80211_begin_scan(ic, arg); 9781a1e1d21SSam Leffler } 9791a1e1d21SSam Leffler break; 9801a1e1d21SSam Leffler case IEEE80211_S_SCAN: 9818a1b9b6aSSam Leffler /* 982097131ffSSam Leffler * Scan next. If doing an active scan probe 983097131ffSSam Leffler * for the requested ap (if any). 9848a1b9b6aSSam Leffler */ 985097131ffSSam Leffler if (ic->ic_flags & IEEE80211_F_ASCAN) 986097131ffSSam Leffler ieee80211_probe_curchan(ic, 0); 9871a1e1d21SSam Leffler break; 9881a1e1d21SSam Leffler case IEEE80211_S_RUN: 9891a1e1d21SSam Leffler /* beacon miss */ 9908a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_STATE, 9918a1b9b6aSSam Leffler "no recent beacons from %s; rescanning\n", 9921a1e1d21SSam Leffler ether_sprintf(ic->ic_bss->ni_bssid)); 9938a1b9b6aSSam Leffler ieee80211_sta_leave(ic, ni); 9948a1b9b6aSSam Leffler ic->ic_flags &= ~IEEE80211_F_SIBSS; /* XXX */ 9951a1e1d21SSam Leffler /* FALLTHRU */ 9961a1e1d21SSam Leffler case IEEE80211_S_AUTH: 9971a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 9981a1e1d21SSam Leffler /* timeout restart scan */ 9998a1b9b6aSSam Leffler ni = ieee80211_find_node(&ic->ic_scan, 10008a1b9b6aSSam Leffler ic->ic_bss->ni_macaddr); 10011a1e1d21SSam Leffler if (ni != NULL) { 10021a1e1d21SSam Leffler ni->ni_fails++; 10031a1e1d21SSam Leffler ieee80211_unref_node(&ni); 10041a1e1d21SSam Leffler } 1005ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) 10068a1b9b6aSSam Leffler ieee80211_begin_scan(ic, arg); 10071a1e1d21SSam Leffler break; 10081a1e1d21SSam Leffler } 10091a1e1d21SSam Leffler break; 10101a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10111a1e1d21SSam Leffler switch (ostate) { 10121a1e1d21SSam Leffler case IEEE80211_S_INIT: 10131a1e1d21SSam Leffler case IEEE80211_S_SCAN: 10141a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10151a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1); 10161a1e1d21SSam Leffler break; 10171a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10181a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10198a1b9b6aSSam Leffler switch (arg) { 10201a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 10211a1e1d21SSam Leffler /* ??? */ 10221a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10231a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2); 10241a1e1d21SSam Leffler break; 10251a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 10261a1e1d21SSam Leffler /* ignore and retry scan on timeout */ 10271a1e1d21SSam Leffler break; 10281a1e1d21SSam Leffler } 10291a1e1d21SSam Leffler break; 10301a1e1d21SSam Leffler case IEEE80211_S_RUN: 10318a1b9b6aSSam Leffler switch (arg) { 10321a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_AUTH: 10331a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10341a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 2); 10351a1e1d21SSam Leffler ic->ic_state = ostate; /* stay RUN */ 10361a1e1d21SSam Leffler break; 10371a1e1d21SSam Leffler case IEEE80211_FC0_SUBTYPE_DEAUTH: 1038ae8880fdSSam Leffler ieee80211_sta_leave(ic, ni); 1039ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 10401a1e1d21SSam Leffler /* try to reauth */ 10411a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10421a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_AUTH, 1); 1043ae8880fdSSam Leffler } 10441a1e1d21SSam Leffler break; 10451a1e1d21SSam Leffler } 10461a1e1d21SSam Leffler break; 10471a1e1d21SSam Leffler } 10481a1e1d21SSam Leffler break; 10491a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10501a1e1d21SSam Leffler switch (ostate) { 10511a1e1d21SSam Leffler case IEEE80211_S_INIT: 10521a1e1d21SSam Leffler case IEEE80211_S_SCAN: 10531a1e1d21SSam Leffler case IEEE80211_S_ASSOC: 10548a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 10558a1b9b6aSSam Leffler "%s: invalid transition\n", __func__); 10561a1e1d21SSam Leffler break; 10571a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10581a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10591a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 0); 10601a1e1d21SSam Leffler break; 10611a1e1d21SSam Leffler case IEEE80211_S_RUN: 1062ae8880fdSSam Leffler ieee80211_sta_leave(ic, ni); 1063ae8880fdSSam Leffler if (ic->ic_roaming == IEEE80211_ROAMING_AUTO) { 10641a1e1d21SSam Leffler IEEE80211_SEND_MGMT(ic, ni, 10651a1e1d21SSam Leffler IEEE80211_FC0_SUBTYPE_ASSOC_REQ, 1); 1066ae8880fdSSam Leffler } 10671a1e1d21SSam Leffler break; 10681a1e1d21SSam Leffler } 10691a1e1d21SSam Leffler break; 10701a1e1d21SSam Leffler case IEEE80211_S_RUN: 10718a1b9b6aSSam Leffler if (ic->ic_flags & IEEE80211_F_WPA) { 10728a1b9b6aSSam Leffler /* XXX validate prerequisites */ 10738a1b9b6aSSam Leffler } 10741a1e1d21SSam Leffler switch (ostate) { 10751a1e1d21SSam Leffler case IEEE80211_S_INIT: 10768a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_MONITOR) 10778a1b9b6aSSam Leffler break; 10788a1b9b6aSSam Leffler /* fall thru... */ 10791a1e1d21SSam Leffler case IEEE80211_S_AUTH: 10808a1b9b6aSSam Leffler IEEE80211_DPRINTF(ic, IEEE80211_MSG_ANY, 10818a1b9b6aSSam Leffler "%s: invalid transition\n", __func__); 10822c21ffc8SSam Leffler /* fall thru... */ 10832c21ffc8SSam Leffler case IEEE80211_S_RUN: 10841a1e1d21SSam Leffler break; 10851a1e1d21SSam Leffler case IEEE80211_S_SCAN: /* adhoc/hostap mode */ 10861a1e1d21SSam Leffler case IEEE80211_S_ASSOC: /* infra mode */ 10871a1e1d21SSam Leffler KASSERT(ni->ni_txrate < ni->ni_rates.rs_nrates, 10881a1e1d21SSam Leffler ("%s: bogus xmit rate %u setup\n", __func__, 10891a1e1d21SSam Leffler ni->ni_txrate)); 10908a1b9b6aSSam Leffler #ifdef IEEE80211_DEBUG 10918a1b9b6aSSam Leffler if (ieee80211_msg_debug(ic)) { 10921a1e1d21SSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 10938a1b9b6aSSam Leffler if_printf(ifp, "associated "); 10941a1e1d21SSam Leffler else 10958a1b9b6aSSam Leffler if_printf(ifp, "synchronized "); 10961a1e1d21SSam Leffler printf("with %s ssid ", 10971a1e1d21SSam Leffler ether_sprintf(ni->ni_bssid)); 10981a1e1d21SSam Leffler ieee80211_print_essid(ic->ic_bss->ni_essid, 10991a1e1d21SSam Leffler ni->ni_esslen); 11001a1e1d21SSam Leffler printf(" channel %d start %uMb\n", 1101b5c99415SSam Leffler ieee80211_chan2ieee(ic, ic->ic_curchan), 11021a1e1d21SSam Leffler IEEE80211_RATE2MBS(ni->ni_rates.rs_rates[ni->ni_txrate])); 11031a1e1d21SSam Leffler } 11048a1b9b6aSSam Leffler #endif 11051a1e1d21SSam Leffler ic->ic_mgt_timer = 0; 11068a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_STA) 11078a1b9b6aSSam Leffler ieee80211_notify_node_join(ic, ni, 11088a1b9b6aSSam Leffler arg == IEEE80211_FC0_SUBTYPE_ASSOC_RESP); 11098a1b9b6aSSam Leffler if_start(ifp); /* XXX not authorized yet */ 11101a1e1d21SSam Leffler break; 11111a1e1d21SSam Leffler } 1112e99662a6SSam Leffler if (ostate != IEEE80211_S_RUN && 1113e99662a6SSam Leffler ic->ic_opmode == IEEE80211_M_STA && 1114e99662a6SSam Leffler (ic->ic_flags_ext & IEEE80211_FEXT_SWBMISS)) { 1115e99662a6SSam Leffler /* 1116e99662a6SSam Leffler * Start s/w beacon miss timer for devices w/o 1117e99662a6SSam Leffler * hardware support. We fudge a bit here since 1118e99662a6SSam Leffler * we're doing this in software. 1119e99662a6SSam Leffler */ 1120e99662a6SSam Leffler ic->ic_swbmiss_period = IEEE80211_TU_TO_TICKS( 1121e99662a6SSam Leffler 2 * ic->ic_bmissthreshold * ni->ni_intval); 1122e99662a6SSam Leffler ic->ic_swbmiss_count = 0; 1123e99662a6SSam Leffler callout_reset(&ic->ic_swbmiss, ic->ic_swbmiss_period, 1124e99662a6SSam Leffler ieee80211_swbmiss, ic); 1125e99662a6SSam Leffler } 11268a1b9b6aSSam Leffler /* 11278a1b9b6aSSam Leffler * Start/stop the authenticator when operating as an 11288a1b9b6aSSam Leffler * AP. We delay until here to allow configuration to 11298a1b9b6aSSam Leffler * happen out of order. 11308a1b9b6aSSam Leffler */ 11318a1b9b6aSSam Leffler if (ic->ic_opmode == IEEE80211_M_HOSTAP && /* XXX IBSS/AHDEMO */ 11328a1b9b6aSSam Leffler ic->ic_auth->ia_attach != NULL) { 11338a1b9b6aSSam Leffler /* XXX check failure */ 11348a1b9b6aSSam Leffler ic->ic_auth->ia_attach(ic); 11358a1b9b6aSSam Leffler } else if (ic->ic_auth->ia_detach != NULL) { 11368a1b9b6aSSam Leffler ic->ic_auth->ia_detach(ic); 11378a1b9b6aSSam Leffler } 11388a1b9b6aSSam Leffler /* 11398a1b9b6aSSam Leffler * When 802.1x is not in use mark the port authorized 11408a1b9b6aSSam Leffler * at this point so traffic can flow. 11418a1b9b6aSSam Leffler */ 11428a1b9b6aSSam Leffler if (ni->ni_authmode != IEEE80211_AUTH_8021X) 1143e4918ecdSSam Leffler ieee80211_node_authorize(ni); 11448a1b9b6aSSam Leffler /* 11458a1b9b6aSSam Leffler * Enable inactivity processing. 11468a1b9b6aSSam Leffler * XXX 11478a1b9b6aSSam Leffler */ 11488a1b9b6aSSam Leffler ic->ic_scan.nt_inact_timer = IEEE80211_INACT_WAIT; 1149acc4f7f5SSam Leffler ic->ic_sta.nt_inact_timer = IEEE80211_INACT_WAIT; 11501a1e1d21SSam Leffler break; 11511a1e1d21SSam Leffler } 11521a1e1d21SSam Leffler return 0; 11531a1e1d21SSam Leffler } 1154