| Commit | Line | Data |
|---|---|---|
| 32176cfd | 1 | /*- |
| f186073c | 2 | * Copyright (c) 2001 Atsushi Onoe |
| 32176cfd | 3 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
| f186073c JS |
4 | * All rights reserved. |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| f186073c JS |
14 | * |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |
| 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |
| 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |
| 18 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |
| 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |
| 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 25 | * | |
| 32176cfd | 26 | * $FreeBSD: head/sys/net80211/ieee80211_proto.c 195618 2009-07-11 15:02:45Z rpaulo $ |
| f186073c JS |
27 | */ |
| 28 | ||
| 29 | /* | |
| 30 | * IEEE 802.11 protocol support. | |
| 31 | */ | |
| 32 | ||
| 33 | #include "opt_inet.h" | |
| 32176cfd | 34 | #include "opt_wlan.h" |
| f186073c JS |
35 | |
| 36 | #include <sys/param.h> | |
| f186073c | 37 | #include <sys/kernel.h> |
| 32176cfd RP |
38 | #include <sys/systm.h> |
| 39 | ||
| 841ab66c | 40 | #include <sys/socket.h> |
| 32176cfd | 41 | #include <sys/sockio.h> |
| 841ab66c | 42 | |
| f186073c | 43 | #include <net/if.h> |
| 841ab66c | 44 | #include <net/if_media.h> |
| 9ed293e0 | 45 | #include <net/ifq_var.h> |
| 32176cfd | 46 | #include <net/route.h> |
| f186073c JS |
47 | |
| 48 | #include <netproto/802_11/ieee80211_var.h> | |
| 32176cfd RP |
49 | #include <netproto/802_11/ieee80211_adhoc.h> |
| 50 | #include <netproto/802_11/ieee80211_sta.h> | |
| 51 | #include <netproto/802_11/ieee80211_hostap.h> | |
| 52 | #include <netproto/802_11/ieee80211_wds.h> | |
| 53 | #ifdef IEEE80211_SUPPORT_MESH | |
| 54 | #include <netproto/802_11/ieee80211_mesh.h> | |
| 55 | #endif | |
| 56 | #include <netproto/802_11/ieee80211_monitor.h> | |
| 57 | #include <netproto/802_11/ieee80211_input.h> | |
| f186073c | 58 | |
| 841ab66c SZ |
59 | /* XXX tunables */ |
| 60 | #define AGGRESSIVE_MODE_SWITCH_HYSTERESIS 3 /* pkts / 100ms */ | |
| 61 | #define HIGH_PRI_SWITCH_THRESH 10 /* pkts / 100ms */ | |
| f186073c | 62 | |
| 32176cfd RP |
63 | const char *ieee80211_mgt_subtype_name[] = { |
| 64 | "assoc_req", "assoc_resp", "reassoc_req", "reassoc_resp", | |
| 65 | "probe_req", "probe_resp", "reserved#6", "reserved#7", | |
| 66 | "beacon", "atim", "disassoc", "auth", | |
| 67 | "deauth", "action", "reserved#14", "reserved#15" | |
| 68 | }; | |
| 69 | const char *ieee80211_ctl_subtype_name[] = { | |
| 70 | "reserved#0", "reserved#1", "reserved#2", "reserved#3", | |
| 71 | "reserved#3", "reserved#5", "reserved#6", "reserved#7", | |
| 72 | "reserved#8", "reserved#9", "ps_poll", "rts", | |
| 73 | "cts", "ack", "cf_end", "cf_end_ack" | |
| 74 | }; | |
| 75 | const char *ieee80211_opmode_name[IEEE80211_OPMODE_MAX] = { | |
| 76 | "IBSS", /* IEEE80211_M_IBSS */ | |
| 77 | "STA", /* IEEE80211_M_STA */ | |
| 78 | "WDS", /* IEEE80211_M_WDS */ | |
| 79 | "AHDEMO", /* IEEE80211_M_AHDEMO */ | |
| 80 | "HOSTAP", /* IEEE80211_M_HOSTAP */ | |
| 81 | "MONITOR", /* IEEE80211_M_MONITOR */ | |
| 82 | "MBSS" /* IEEE80211_M_MBSS */ | |
| 83 | }; | |
| f186073c JS |
84 | const char *ieee80211_state_name[IEEE80211_S_MAX] = { |
| 85 | "INIT", /* IEEE80211_S_INIT */ | |
| 86 | "SCAN", /* IEEE80211_S_SCAN */ | |
| 87 | "AUTH", /* IEEE80211_S_AUTH */ | |
| 88 | "ASSOC", /* IEEE80211_S_ASSOC */ | |
| 32176cfd RP |
89 | "CAC", /* IEEE80211_S_CAC */ |
| 90 | "RUN", /* IEEE80211_S_RUN */ | |
| 91 | "CSA", /* IEEE80211_S_CSA */ | |
| 92 | "SLEEP", /* IEEE80211_S_SLEEP */ | |
| f186073c | 93 | }; |
| 841ab66c SZ |
94 | const char *ieee80211_wme_acnames[] = { |
| 95 | "WME_AC_BE", | |
| 96 | "WME_AC_BK", | |
| 97 | "WME_AC_VI", | |
| 98 | "WME_AC_VO", | |
| 99 | "WME_UPSD", | |
| 100 | }; | |
| f186073c | 101 | |
| 47156d48 MD |
102 | static void beacon_miss_task(void *, int); |
| 103 | static void beacon_swmiss_task(void *, int); | |
| 104 | static void parent_updown_task(void *, int); | |
| 105 | static void update_mcast_task(void *, int); | |
| 106 | static void update_promisc_task(void *, int); | |
| 107 | static void update_channel_task(void *, int); | |
| 108 | static void ieee80211_newstate_task(void *, int); | |
| 32176cfd RP |
109 | static int ieee80211_new_state_locked(struct ieee80211vap *, |
| 110 | enum ieee80211_state, int); | |
| 111 | ||
| 112 | static int | |
| 113 | null_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, | |
| 114 | const struct ieee80211_bpf_params *params) | |
| 115 | { | |
| 116 | struct ifnet *ifp = ni->ni_ic->ic_ifp; | |
| 117 | ||
| 118 | if_printf(ifp, "missing ic_raw_xmit callback, drop frame\n"); | |
| 119 | m_freem(m); | |
| 120 | return ENETDOWN; | |
| 121 | } | |
| f186073c JS |
122 | |
| 123 | void | |
| 841ab66c | 124 | ieee80211_proto_attach(struct ieee80211com *ic) |
| f186073c | 125 | { |
| 841ab66c | 126 | struct ifnet *ifp = ic->ic_ifp; |
| f186073c | 127 | |
| 32176cfd RP |
128 | /* override the 802.3 setting */ |
| 129 | ifp->if_hdrlen = ic->ic_headroom | |
| 130 | + sizeof(struct ieee80211_qosframe_addr4) | |
| 131 | + IEEE80211_WEP_IVLEN + IEEE80211_WEP_KIDLEN | |
| 132 | + IEEE80211_WEP_EXTIVLEN; | |
| 133 | /* XXX no way to recalculate on ifdetach */ | |
| 134 | if (ALIGN(ifp->if_hdrlen) > max_linkhdr) { | |
| 135 | /* XXX sanity check... */ | |
| 136 | max_linkhdr = ALIGN(ifp->if_hdrlen); | |
| 137 | max_hdr = max_linkhdr + max_protohdr; | |
| 138 | max_datalen = MHLEN - max_hdr; | |
| 139 | } | |
| f186073c | 140 | ic->ic_protmode = IEEE80211_PROT_CTSONLY; |
| 32176cfd | 141 | |
| 47156d48 MD |
142 | TASK_INIT(&ic->ic_parent_task, 0, parent_updown_task, ifp); |
| 143 | TASK_INIT(&ic->ic_mcast_task, 0, update_mcast_task, ic); | |
| 144 | TASK_INIT(&ic->ic_promisc_task, 0, update_promisc_task, ic); | |
| 145 | TASK_INIT(&ic->ic_chan_task, 0, update_channel_task, ic); | |
| 146 | TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss_task, ic); | |
| 841ab66c SZ |
147 | |
| 148 | ic->ic_wme.wme_hipri_switch_hysteresis = | |
| 149 | AGGRESSIVE_MODE_SWITCH_HYSTERESIS; | |
| f186073c | 150 | |
| f186073c | 151 | /* initialize management frame handlers */ |
| f186073c | 152 | ic->ic_send_mgmt = ieee80211_send_mgmt; |
| 32176cfd RP |
153 | ic->ic_raw_xmit = null_raw_xmit; |
| 154 | ||
| 155 | ieee80211_adhoc_attach(ic); | |
| 156 | ieee80211_sta_attach(ic); | |
| 157 | ieee80211_wds_attach(ic); | |
| 158 | ieee80211_hostap_attach(ic); | |
| 159 | #ifdef IEEE80211_SUPPORT_MESH | |
| 160 | ieee80211_mesh_attach(ic); | |
| 161 | #endif | |
| 162 | ieee80211_monitor_attach(ic); | |
| f186073c JS |
163 | } |
| 164 | ||
| 165 | void | |
| 841ab66c | 166 | ieee80211_proto_detach(struct ieee80211com *ic) |
| f186073c | 167 | { |
| 32176cfd RP |
168 | ieee80211_monitor_detach(ic); |
| 169 | #ifdef IEEE80211_SUPPORT_MESH | |
| 170 | ieee80211_mesh_detach(ic); | |
| 171 | #endif | |
| 172 | ieee80211_hostap_detach(ic); | |
| 173 | ieee80211_wds_detach(ic); | |
| 174 | ieee80211_adhoc_detach(ic); | |
| 175 | ieee80211_sta_detach(ic); | |
| 176 | } | |
| 177 | ||
| 178 | static void | |
| 179 | null_update_beacon(struct ieee80211vap *vap, int item) | |
| 180 | { | |
| 181 | } | |
| 182 | ||
| 183 | void | |
| 184 | ieee80211_proto_vattach(struct ieee80211vap *vap) | |
| 185 | { | |
| 186 | struct ieee80211com *ic = vap->iv_ic; | |
| 187 | struct ifnet *ifp = vap->iv_ifp; | |
| 188 | int i; | |
| 189 | ||
| 190 | /* override the 802.3 setting */ | |
| 191 | ifp->if_hdrlen = ic->ic_ifp->if_hdrlen; | |
| 841ab66c | 192 | |
| 32176cfd RP |
193 | vap->iv_rtsthreshold = IEEE80211_RTS_DEFAULT; |
| 194 | vap->iv_fragthreshold = IEEE80211_FRAG_DEFAULT; | |
| 195 | vap->iv_bmiss_max = IEEE80211_BMISS_MAX; | |
| 34a60cf6 RP |
196 | callout_init_mp(&vap->iv_swbmiss); |
| 197 | callout_init_mp(&vap->iv_mgtsend); | |
| 47156d48 MD |
198 | TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_task, vap); |
| 199 | TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss_task, vap); | |
| 32176cfd RP |
200 | /* |
| 201 | * Install default tx rate handling: no fixed rate, lowest | |
| 202 | * supported rate for mgmt and multicast frames. Default | |
| 203 | * max retry count. These settings can be changed by the | |
| 204 | * driver and/or user applications. | |
| 205 | */ | |
| 206 | for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) { | |
| 207 | const struct ieee80211_rateset *rs = &ic->ic_sup_rates[i]; | |
| 208 | ||
| 209 | vap->iv_txparms[i].ucastrate = IEEE80211_FIXED_RATE_NONE; | |
| 210 | if (i == IEEE80211_MODE_11NA || i == IEEE80211_MODE_11NG) { | |
| 211 | vap->iv_txparms[i].mgmtrate = 0 | IEEE80211_RATE_MCS; | |
| 212 | vap->iv_txparms[i].mcastrate = 0 | IEEE80211_RATE_MCS; | |
| 213 | } else { | |
| 214 | vap->iv_txparms[i].mgmtrate = | |
| 215 | rs->rs_rates[0] & IEEE80211_RATE_VAL; | |
| 216 | vap->iv_txparms[i].mcastrate = | |
| 217 | rs->rs_rates[0] & IEEE80211_RATE_VAL; | |
| 218 | } | |
| 219 | vap->iv_txparms[i].maxretry = IEEE80211_TXMAX_DEFAULT; | |
| 220 | } | |
| 221 | vap->iv_roaming = IEEE80211_ROAMING_AUTO; | |
| 222 | ||
| 223 | vap->iv_update_beacon = null_update_beacon; | |
| 224 | vap->iv_deliver_data = ieee80211_deliver_data; | |
| 225 | ||
| 226 | /* attach support for operating mode */ | |
| 227 | ic->ic_vattach[vap->iv_opmode](vap); | |
| 228 | } | |
| 229 | ||
| 230 | void | |
| 231 | ieee80211_proto_vdetach(struct ieee80211vap *vap) | |
| 232 | { | |
| 233 | #define FREEAPPIE(ie) do { \ | |
| 234 | if (ie != NULL) \ | |
| 235 | kfree(ie, M_80211_NODE_IE); \ | |
| 236 | } while (0) | |
| 237 | /* | |
| 238 | * Detach operating mode module. | |
| 239 | */ | |
| 240 | if (vap->iv_opdetach != NULL) | |
| 241 | vap->iv_opdetach(vap); | |
| 841ab66c SZ |
242 | /* |
| 243 | * This should not be needed as we detach when reseting | |
| 244 | * the state but be conservative here since the | |
| 245 | * authenticator may do things like spawn kernel threads. | |
| 246 | */ | |
| 32176cfd RP |
247 | if (vap->iv_auth->ia_detach != NULL) |
| 248 | vap->iv_auth->ia_detach(vap); | |
| 841ab66c SZ |
249 | /* |
| 250 | * Detach any ACL'ator. | |
| 251 | */ | |
| 32176cfd RP |
252 | if (vap->iv_acl != NULL) |
| 253 | vap->iv_acl->iac_detach(vap); | |
| 254 | ||
| 255 | FREEAPPIE(vap->iv_appie_beacon); | |
| 256 | FREEAPPIE(vap->iv_appie_probereq); | |
| 257 | FREEAPPIE(vap->iv_appie_proberesp); | |
| 258 | FREEAPPIE(vap->iv_appie_assocreq); | |
| 259 | FREEAPPIE(vap->iv_appie_assocresp); | |
| 260 | FREEAPPIE(vap->iv_appie_wpa); | |
| 261 | #undef FREEAPPIE | |
| 841ab66c SZ |
262 | } |
| 263 | ||
| 264 | /* | |
| 265 | * Simple-minded authenticator module support. | |
| 266 | */ | |
| 267 | ||
| 268 | #define IEEE80211_AUTH_MAX (IEEE80211_AUTH_WPA+1) | |
| 269 | /* XXX well-known names */ | |
| 270 | static const char *auth_modnames[IEEE80211_AUTH_MAX] = { | |
| 271 | "wlan_internal", /* IEEE80211_AUTH_NONE */ | |
| 272 | "wlan_internal", /* IEEE80211_AUTH_OPEN */ | |
| 273 | "wlan_internal", /* IEEE80211_AUTH_SHARED */ | |
| 274 | "wlan_xauth", /* IEEE80211_AUTH_8021X */ | |
| 275 | "wlan_internal", /* IEEE80211_AUTH_AUTO */ | |
| 276 | "wlan_xauth", /* IEEE80211_AUTH_WPA */ | |
| 277 | }; | |
| 278 | static const struct ieee80211_authenticator *authenticators[IEEE80211_AUTH_MAX]; | |
| 279 | ||
| 280 | static const struct ieee80211_authenticator auth_internal = { | |
| 281 | .ia_name = "wlan_internal", | |
| 282 | .ia_attach = NULL, | |
| 283 | .ia_detach = NULL, | |
| 284 | .ia_node_join = NULL, | |
| 285 | .ia_node_leave = NULL, | |
| 286 | }; | |
| 287 | ||
| 288 | /* | |
| 289 | * Setup internal authenticators once; they are never unregistered. | |
| 290 | */ | |
| 291 | static void | |
| 292 | ieee80211_auth_setup(void) | |
| 293 | { | |
| 294 | ieee80211_authenticator_register(IEEE80211_AUTH_OPEN, &auth_internal); | |
| 295 | ieee80211_authenticator_register(IEEE80211_AUTH_SHARED, &auth_internal); | |
| 296 | ieee80211_authenticator_register(IEEE80211_AUTH_AUTO, &auth_internal); | |
| 297 | } | |
| 298 | SYSINIT(wlan_auth, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_auth_setup, NULL); | |
| 299 | ||
| 300 | const struct ieee80211_authenticator * | |
| 301 | ieee80211_authenticator_get(int auth) | |
| 302 | { | |
| 303 | if (auth >= IEEE80211_AUTH_MAX) | |
| 304 | return NULL; | |
| 305 | if (authenticators[auth] == NULL) | |
| 306 | ieee80211_load_module(auth_modnames[auth]); | |
| 307 | return authenticators[auth]; | |
| 308 | } | |
| 309 | ||
| 310 | void | |
| 311 | ieee80211_authenticator_register(int type, | |
| 312 | const struct ieee80211_authenticator *auth) | |
| 313 | { | |
| 314 | if (type >= IEEE80211_AUTH_MAX) | |
| 315 | return; | |
| 316 | authenticators[type] = auth; | |
| 317 | } | |
| 318 | ||
| 319 | void | |
| 320 | ieee80211_authenticator_unregister(int type) | |
| 321 | { | |
| 322 | ||
| 323 | if (type >= IEEE80211_AUTH_MAX) | |
| 324 | return; | |
| 325 | authenticators[type] = NULL; | |
| 326 | } | |
| 327 | ||
| 328 | /* | |
| 329 | * Very simple-minded ACL module support. | |
| 330 | */ | |
| 331 | /* XXX just one for now */ | |
| 332 | static const struct ieee80211_aclator *acl = NULL; | |
| 333 | ||
| 334 | void | |
| 335 | ieee80211_aclator_register(const struct ieee80211_aclator *iac) | |
| 336 | { | |
| a6ec04bc | 337 | kprintf("wlan: %s acl policy registered\n", iac->iac_name); |
| 841ab66c SZ |
338 | acl = iac; |
| 339 | } | |
| 340 | ||
| 341 | void | |
| 342 | ieee80211_aclator_unregister(const struct ieee80211_aclator *iac) | |
| 343 | { | |
| 344 | if (acl == iac) | |
| 345 | acl = NULL; | |
| a6ec04bc | 346 | kprintf("wlan: %s acl policy unregistered\n", iac->iac_name); |
| 841ab66c SZ |
347 | } |
| 348 | ||
| 349 | const struct ieee80211_aclator * | |
| 350 | ieee80211_aclator_get(const char *name) | |
| 351 | { | |
| 352 | if (acl == NULL) | |
| 353 | ieee80211_load_module("wlan_acl"); | |
| 354 | return acl != NULL && strcmp(acl->iac_name, name) == 0 ? acl : NULL; | |
| f186073c JS |
355 | } |
| 356 | ||
| 357 | void | |
| 841ab66c | 358 | ieee80211_print_essid(const uint8_t *essid, int len) |
| f186073c | 359 | { |
| 32176cfd | 360 | const uint8_t *p; |
| f186073c | 361 | int i; |
| f186073c JS |
362 | |
| 363 | if (len > IEEE80211_NWID_LEN) | |
| 364 | len = IEEE80211_NWID_LEN; | |
| 365 | /* determine printable or not */ | |
| 366 | for (i = 0, p = essid; i < len; i++, p++) { | |
| 367 | if (*p < ' ' || *p > 0x7e) | |
| 368 | break; | |
| 369 | } | |
| 370 | if (i == len) { | |
| a6ec04bc | 371 | kprintf("\""); |
| f186073c | 372 | for (i = 0, p = essid; i < len; i++, p++) |
| a6ec04bc SW |
373 | kprintf("%c", *p); |
| 374 | kprintf("\""); | |
| f186073c | 375 | } else { |
| a6ec04bc | 376 | kprintf("0x"); |
| f186073c | 377 | for (i = 0, p = essid; i < len; i++, p++) |
| a6ec04bc | 378 | kprintf("%02x", *p); |
| f186073c JS |
379 | } |
| 380 | } | |
| 381 | ||
| 382 | void | |
| 32176cfd RP |
383 | ieee80211_dump_pkt(struct ieee80211com *ic, |
| 384 | const uint8_t *buf, int len, int rate, int rssi) | |
| f186073c | 385 | { |
| 841ab66c | 386 | const struct ieee80211_frame *wh; |
| f186073c | 387 | int i; |
| 1e290df3 | 388 | char ethstr[ETHER_ADDRSTRLEN + 1]; |
| f186073c | 389 | |
| 841ab66c | 390 | wh = (const struct ieee80211_frame *)buf; |
| f186073c JS |
391 | switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) { |
| 392 | case IEEE80211_FC1_DIR_NODS: | |
| 1e290df3 AHJ |
393 | kprintf("NODS %s", kether_ntoa(wh->i_addr2, ethstr)); |
| 394 | kprintf("->%s", kether_ntoa(wh->i_addr1, ethstr)); | |
| 395 | kprintf("(%s)", kether_ntoa(wh->i_addr3, ethstr)); | |
| f186073c JS |
396 | break; |
| 397 | case IEEE80211_FC1_DIR_TODS: | |
| 1e290df3 AHJ |
398 | kprintf("TODS %s", kether_ntoa(wh->i_addr2, ethstr)); |
| 399 | kprintf("->%s", kether_ntoa(wh->i_addr3, ethstr)); | |
| 400 | kprintf("(%s)", kether_ntoa(wh->i_addr1, ethstr)); | |
| f186073c JS |
401 | break; |
| 402 | case IEEE80211_FC1_DIR_FROMDS: | |
| 1e290df3 AHJ |
403 | kprintf("FRDS %s", kether_ntoa(wh->i_addr3, ethstr)); |
| 404 | kprintf("->%s", kether_ntoa(wh->i_addr1, ethstr)); | |
| 405 | kprintf("(%s)", kether_ntoa(wh->i_addr2, ethstr)); | |
| f186073c JS |
406 | break; |
| 407 | case IEEE80211_FC1_DIR_DSTODS: | |
| 1e290df3 AHJ |
408 | kprintf("DSDS %s", kether_ntoa((const uint8_t *)&wh[1], ethstr)); |
| 409 | kprintf("->%s", kether_ntoa(wh->i_addr3, ethstr)); | |
| 410 | kprintf("(%s", kether_ntoa(wh->i_addr2, ethstr)); | |
| 411 | kprintf("->%s)", kether_ntoa(wh->i_addr1, ethstr)); | |
| f186073c JS |
412 | break; |
| 413 | } | |
| 414 | switch (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) { | |
| 415 | case IEEE80211_FC0_TYPE_DATA: | |
| a6ec04bc | 416 | kprintf(" data"); |
| f186073c JS |
417 | break; |
| 418 | case IEEE80211_FC0_TYPE_MGT: | |
| a6ec04bc | 419 | kprintf(" %s", ieee80211_mgt_subtype_name[ |
| f186073c JS |
420 | (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) |
| 421 | >> IEEE80211_FC0_SUBTYPE_SHIFT]); | |
| 422 | break; | |
| 423 | default: | |
| a6ec04bc | 424 | kprintf(" type#%d", wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK); |
| f186073c JS |
425 | break; |
| 426 | } | |
| 32176cfd RP |
427 | if (IEEE80211_QOS_HAS_SEQ(wh)) { |
| 428 | const struct ieee80211_qosframe *qwh = | |
| 429 | (const struct ieee80211_qosframe *)buf; | |
| 430 | kprintf(" QoS [TID %u%s]", qwh->i_qos[0] & IEEE80211_QOS_TID, | |
| 431 | qwh->i_qos[0] & IEEE80211_QOS_ACKPOLICY ? " ACM" : ""); | |
| 432 | } | |
| 841ab66c | 433 | if (wh->i_fc[1] & IEEE80211_FC1_WEP) { |
| 32176cfd RP |
434 | int off; |
| 435 | ||
| 436 | off = ieee80211_anyhdrspace(ic, wh); | |
| 437 | kprintf(" WEP [IV %.02x %.02x %.02x", | |
| 438 | buf[off+0], buf[off+1], buf[off+2]); | |
| 439 | if (buf[off+IEEE80211_WEP_IVLEN] & IEEE80211_WEP_EXTIV) | |
| 440 | kprintf(" %.02x %.02x %.02x", | |
| 441 | buf[off+4], buf[off+5], buf[off+6]); | |
| 442 | kprintf(" KID %u]", buf[off+IEEE80211_WEP_IVLEN] >> 6); | |
| 841ab66c | 443 | } |
| f186073c | 444 | if (rate >= 0) |
| a6ec04bc | 445 | kprintf(" %dM", rate / 2); |
| f186073c | 446 | if (rssi >= 0) |
| a6ec04bc SW |
447 | kprintf(" +%d", rssi); |
| 448 | kprintf("\n"); | |
| f186073c JS |
449 | if (len > 0) { |
| 450 | for (i = 0; i < len; i++) { | |
| 451 | if ((i & 1) == 0) | |
| a6ec04bc SW |
452 | kprintf(" "); |
| 453 | kprintf("%02x", buf[i]); | |
| f186073c | 454 | } |
| a6ec04bc | 455 | kprintf("\n"); |
| f186073c JS |
456 | } |
| 457 | } | |
| 458 | ||
| 32176cfd RP |
459 | static __inline int |
| 460 | findrix(const struct ieee80211_rateset *rs, int r) | |
| 461 | { | |
| 462 | int i; | |
| 463 | ||
| 464 | for (i = 0; i < rs->rs_nrates; i++) | |
| 465 | if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == r) | |
| 466 | return i; | |
| 467 | return -1; | |
| 468 | } | |
| 469 | ||
| f186073c | 470 | int |
| 32176cfd RP |
471 | ieee80211_fix_rate(struct ieee80211_node *ni, |
| 472 | struct ieee80211_rateset *nrs, int flags) | |
| f186073c JS |
473 | { |
| 474 | #define RV(v) ((v) & IEEE80211_RATE_VAL) | |
| 32176cfd | 475 | struct ieee80211vap *vap = ni->ni_vap; |
| 841ab66c | 476 | struct ieee80211com *ic = ni->ni_ic; |
| 32176cfd RP |
477 | int i, j, rix, error; |
| 478 | int okrate, badrate, fixedrate, ucastrate; | |
| 208a1285 | 479 | const struct ieee80211_rateset *srs; |
| f186073c JS |
480 | uint8_t r; |
| 481 | ||
| 32176cfd RP |
482 | error = 0; |
| 483 | okrate = badrate = 0; | |
| 484 | ucastrate = vap->iv_txparms[ieee80211_chan2mode(ni->ni_chan)].ucastrate; | |
| 485 | if (ucastrate != IEEE80211_FIXED_RATE_NONE) { | |
| 486 | /* | |
| 487 | * Workaround awkwardness with fixed rate. We are called | |
| 488 | * to check both the legacy rate set and the HT rate set | |
| 489 | * but we must apply any legacy fixed rate check only to the | |
| 490 | * legacy rate set and vice versa. We cannot tell what type | |
| 491 | * of rate set we've been given (legacy or HT) but we can | |
| 492 | * distinguish the fixed rate type (MCS have 0x80 set). | |
| 493 | * So to deal with this the caller communicates whether to | |
| 494 | * check MCS or legacy rate using the flags and we use the | |
| 495 | * type of any fixed rate to avoid applying an MCS to a | |
| 496 | * legacy rate and vice versa. | |
| 497 | */ | |
| 498 | if (ucastrate & 0x80) { | |
| 499 | if (flags & IEEE80211_F_DOFRATE) | |
| 500 | flags &= ~IEEE80211_F_DOFRATE; | |
| 501 | } else if ((ucastrate & 0x80) == 0) { | |
| 502 | if (flags & IEEE80211_F_DOFMCS) | |
| 503 | flags &= ~IEEE80211_F_DOFMCS; | |
| 504 | } | |
| 505 | /* NB: required to make MCS match below work */ | |
| 506 | ucastrate &= IEEE80211_RATE_VAL; | |
| 507 | } | |
| 508 | fixedrate = IEEE80211_FIXED_RATE_NONE; | |
| 841ab66c | 509 | /* |
| 32176cfd RP |
510 | * XXX we are called to process both MCS and legacy rates; |
| 511 | * we must use the appropriate basic rate set or chaos will | |
| 512 | * ensue; for now callers that want MCS must supply | |
| 513 | * IEEE80211_F_DOBRS; at some point we'll need to split this | |
| 514 | * function so there are two variants, one for MCS and one | |
| 515 | * for legacy rates. | |
| 841ab66c | 516 | */ |
| 32176cfd RP |
517 | if (flags & IEEE80211_F_DOBRS) |
| 518 | srs = (const struct ieee80211_rateset *) | |
| 519 | ieee80211_get_suphtrates(ic, ni->ni_chan); | |
| 520 | else | |
| 521 | srs = ieee80211_get_suprates(ic, ni->ni_chan); | |
| f186073c | 522 | for (i = 0; i < nrs->rs_nrates; ) { |
| f186073c JS |
523 | if (flags & IEEE80211_F_DOSORT) { |
| 524 | /* | |
| 525 | * Sort rates. | |
| 526 | */ | |
| 527 | for (j = i + 1; j < nrs->rs_nrates; j++) { | |
| 528 | if (RV(nrs->rs_rates[i]) > RV(nrs->rs_rates[j])) { | |
| 529 | r = nrs->rs_rates[i]; | |
| 530 | nrs->rs_rates[i] = nrs->rs_rates[j]; | |
| 531 | nrs->rs_rates[j] = r; | |
| 532 | } | |
| 533 | } | |
| 534 | } | |
| 535 | r = nrs->rs_rates[i] & IEEE80211_RATE_VAL; | |
| 536 | badrate = r; | |
| 32176cfd RP |
537 | /* |
| 538 | * Check for fixed rate. | |
| 539 | */ | |
| 540 | if (r == ucastrate) | |
| 541 | fixedrate = r; | |
| 542 | /* | |
| 543 | * Check against supported rates. | |
| 544 | */ | |
| 545 | rix = findrix(srs, r); | |
| 546 | if (flags & IEEE80211_F_DONEGO) { | |
| 547 | if (rix < 0) { | |
| f186073c JS |
548 | /* |
| 549 | * A rate in the node's rate set is not | |
| 32176cfd RP |
550 | * supported. If this is a basic rate and we |
| 551 | * are operating as a STA then this is an error. | |
| 552 | * Otherwise we just discard/ignore the rate. | |
| f186073c | 553 | */ |
| 32176cfd | 554 | if ((flags & IEEE80211_F_JOIN) && |
| f186073c JS |
555 | (nrs->rs_rates[i] & IEEE80211_RATE_BASIC)) |
| 556 | error++; | |
| 32176cfd RP |
557 | } else if ((flags & IEEE80211_F_JOIN) == 0) { |
| 558 | /* | |
| 559 | * Overwrite with the supported rate | |
| 560 | * value so any basic rate bit is set. | |
| 561 | */ | |
| 562 | nrs->rs_rates[i] = srs->rs_rates[rix]; | |
| f186073c JS |
563 | } |
| 564 | } | |
| 32176cfd | 565 | if ((flags & IEEE80211_F_DODEL) && rix < 0) { |
| f186073c JS |
566 | /* |
| 567 | * Delete unacceptable rates. | |
| 568 | */ | |
| 32176cfd RP |
569 | nrs->rs_nrates--; |
| 570 | for (j = i; j < nrs->rs_nrates; j++) | |
| 571 | nrs->rs_rates[j] = nrs->rs_rates[j + 1]; | |
| 572 | nrs->rs_rates[j] = 0; | |
| 573 | continue; | |
| f186073c | 574 | } |
| 32176cfd | 575 | if (rix >= 0) |
| f186073c JS |
576 | okrate = nrs->rs_rates[i]; |
| 577 | i++; | |
| 578 | } | |
| 841ab66c | 579 | if (okrate == 0 || error != 0 || |
| 32176cfd RP |
580 | ((flags & (IEEE80211_F_DOFRATE|IEEE80211_F_DOFMCS)) && |
| 581 | fixedrate != ucastrate)) { | |
| 582 | IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, | |
| 583 | "%s: flags 0x%x okrate %d error %d fixedrate 0x%x " | |
| 7bfcf376 SW |
584 | "ucastrate %x\n", __func__, flags, okrate, error, |
| 585 | fixedrate, ucastrate); | |
| f186073c | 586 | return badrate | IEEE80211_RATE_BASIC; |
| 32176cfd | 587 | } else |
| f186073c JS |
588 | return RV(okrate); |
| 589 | #undef RV | |
| 590 | } | |
| 591 | ||
| 841ab66c SZ |
592 | /* |
| 593 | * Reset 11g-related state. | |
| 594 | */ | |
| 595 | void | |
| 596 | ieee80211_reset_erp(struct ieee80211com *ic) | |
| 597 | { | |
| 598 | ic->ic_flags &= ~IEEE80211_F_USEPROT; | |
| 599 | ic->ic_nonerpsta = 0; | |
| 600 | ic->ic_longslotsta = 0; | |
| 601 | /* | |
| 602 | * Short slot time is enabled only when operating in 11g | |
| 603 | * and not in an IBSS. We must also honor whether or not | |
| 604 | * the driver is capable of doing it. | |
| 605 | */ | |
| 606 | ieee80211_set_shortslottime(ic, | |
| 32176cfd RP |
607 | IEEE80211_IS_CHAN_A(ic->ic_curchan) || |
| 608 | IEEE80211_IS_CHAN_HT(ic->ic_curchan) || | |
| 609 | (IEEE80211_IS_CHAN_ANYG(ic->ic_curchan) && | |
| 841ab66c SZ |
610 | ic->ic_opmode == IEEE80211_M_HOSTAP && |
| 611 | (ic->ic_caps & IEEE80211_C_SHSLOT))); | |
| 612 | /* | |
| 613 | * Set short preamble and ERP barker-preamble flags. | |
| 614 | */ | |
| 32176cfd RP |
615 | if (IEEE80211_IS_CHAN_A(ic->ic_curchan) || |
| 616 | (ic->ic_caps & IEEE80211_C_SHPREAMBLE)) { | |
| 617 | ic->ic_flags |= IEEE80211_F_SHPREAMBLE; | |
| 618 | ic->ic_flags &= ~IEEE80211_F_USEBARKER; | |
| 619 | } else { | |
| 620 | ic->ic_flags &= ~IEEE80211_F_SHPREAMBLE; | |
| 621 | ic->ic_flags |= IEEE80211_F_USEBARKER; | |
| 622 | } | |
| 841ab66c SZ |
623 | } |
| 624 | ||
| 625 | /* | |
| 626 | * Set the short slot time state and notify the driver. | |
| 627 | */ | |
| 628 | void | |
| 629 | ieee80211_set_shortslottime(struct ieee80211com *ic, int onoff) | |
| 630 | { | |
| 631 | if (onoff) | |
| 632 | ic->ic_flags |= IEEE80211_F_SHSLOT; | |
| 633 | else | |
| 634 | ic->ic_flags &= ~IEEE80211_F_SHSLOT; | |
| 32176cfd | 635 | /* notify driver */ |
| 841ab66c SZ |
636 | if (ic->ic_updateslot != NULL) |
| 637 | ic->ic_updateslot(ic->ic_ifp); | |
| 638 | } | |
| 639 | ||
| 640 | /* | |
| 641 | * Check if the specified rate set supports ERP. | |
| 642 | * NB: the rate set is assumed to be sorted. | |
| 643 | */ | |
| 644 | int | |
| 32176cfd | 645 | ieee80211_iserp_rateset(const struct ieee80211_rateset *rs) |
| 841ab66c | 646 | { |
| 841ab66c SZ |
647 | static const int rates[] = { 2, 4, 11, 22, 12, 24, 48 }; |
| 648 | int i, j; | |
| 649 | ||
| c157ff7a | 650 | if (rs->rs_nrates < NELEM(rates)) |
| 841ab66c | 651 | return 0; |
| c157ff7a | 652 | for (i = 0; i < NELEM(rates); i++) { |
| 841ab66c SZ |
653 | for (j = 0; j < rs->rs_nrates; j++) { |
| 654 | int r = rs->rs_rates[j] & IEEE80211_RATE_VAL; | |
| 655 | if (rates[i] == r) | |
| 656 | goto next; | |
| 657 | if (r > rates[i]) | |
| 658 | return 0; | |
| 659 | } | |
| 660 | return 0; | |
| 661 | next: | |
| 662 | ; | |
| 663 | } | |
| 664 | return 1; | |
| 841ab66c SZ |
665 | } |
| 666 | ||
| 667 | /* | |
| 32176cfd | 668 | * Mark the basic rates for the rate table based on the |
| 841ab66c SZ |
669 | * operating mode. For real 11g we mark all the 11b rates |
| 670 | * and 6, 12, and 24 OFDM. For 11b compatibility we mark only | |
| 671 | * 11b rates. There's also a pseudo 11a-mode used to mark only | |
| 672 | * the basic OFDM rates. | |
| 673 | */ | |
| 32176cfd RP |
674 | static void |
| 675 | setbasicrates(struct ieee80211_rateset *rs, | |
| 676 | enum ieee80211_phymode mode, int add) | |
| 841ab66c | 677 | { |
| 32176cfd | 678 | static const struct ieee80211_rateset basic[IEEE80211_MODE_MAX] = { |
| 9639b71d SZ |
679 | [IEEE80211_MODE_11A] = { 3, { 12, 24, 48 } }, |
| 680 | [IEEE80211_MODE_11B] = { 2, { 2, 4 } }, | |
| 32176cfd | 681 | /* NB: mixed b/g */ |
| 9639b71d | 682 | [IEEE80211_MODE_11G] = { 4, { 2, 4, 11, 22 } }, |
| 9639b71d | 683 | [IEEE80211_MODE_TURBO_A] = { 3, { 12, 24, 48 } }, |
| 32176cfd RP |
684 | [IEEE80211_MODE_TURBO_G] = { 4, { 2, 4, 11, 22 } }, |
| 685 | [IEEE80211_MODE_STURBO_A] = { 3, { 12, 24, 48 } }, | |
| 686 | [IEEE80211_MODE_HALF] = { 3, { 6, 12, 24 } }, | |
| 687 | [IEEE80211_MODE_QUARTER] = { 3, { 3, 6, 12 } }, | |
| 688 | [IEEE80211_MODE_11NA] = { 3, { 12, 24, 48 } }, | |
| 689 | /* NB: mixed b/g */ | |
| 690 | [IEEE80211_MODE_11NG] = { 4, { 2, 4, 11, 22 } }, | |
| 841ab66c SZ |
691 | }; |
| 692 | int i, j; | |
| 693 | ||
| 694 | for (i = 0; i < rs->rs_nrates; i++) { | |
| 32176cfd RP |
695 | if (!add) |
| 696 | rs->rs_rates[i] &= IEEE80211_RATE_VAL; | |
| 697 | for (j = 0; j < basic[mode].rs_nrates; j++) | |
| 698 | if (basic[mode].rs_rates[j] == rs->rs_rates[i]) { | |
| 841ab66c SZ |
699 | rs->rs_rates[i] |= IEEE80211_RATE_BASIC; |
| 700 | break; | |
| 701 | } | |
| 702 | } | |
| 703 | } | |
| 704 | ||
| 32176cfd RP |
705 | /* |
| 706 | * Set the basic rates in a rate set. | |
| 707 | */ | |
| 708 | void | |
| 709 | ieee80211_setbasicrates(struct ieee80211_rateset *rs, | |
| 710 | enum ieee80211_phymode mode) | |
| 208a1285 | 711 | { |
| 32176cfd RP |
712 | setbasicrates(rs, mode, 0); |
| 713 | } | |
| 208a1285 | 714 | |
| 32176cfd RP |
715 | /* |
| 716 | * Add basic rates to a rate set. | |
| 717 | */ | |
| 718 | void | |
| 719 | ieee80211_addbasicrates(struct ieee80211_rateset *rs, | |
| 720 | enum ieee80211_phymode mode) | |
| 721 | { | |
| 722 | setbasicrates(rs, mode, 1); | |
| 208a1285 SZ |
723 | } |
| 724 | ||
| 841ab66c | 725 | /* |
| 32176cfd RP |
726 | * WME protocol support. |
| 727 | * | |
| 728 | * The default 11a/b/g/n parameters come from the WiFi Alliance WMM | |
| 729 | * System Interopability Test Plan (v1.4, Appendix F) and the 802.11n | |
| 730 | * Draft 2.0 Test Plan (Appendix D). | |
| 731 | * | |
| 732 | * Static/Dynamic Turbo mode settings come from Atheros. | |
| 841ab66c SZ |
733 | */ |
| 734 | typedef struct phyParamType { | |
| 32176cfd RP |
735 | uint8_t aifsn; |
| 736 | uint8_t logcwmin; | |
| 737 | uint8_t logcwmax; | |
| 738 | uint16_t txopLimit; | |
| 739 | uint8_t acm; | |
| 841ab66c SZ |
740 | } paramType; |
| 741 | ||
| 742 | static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
743 | [IEEE80211_MODE_AUTO] = { 3, 4, 6, 0, 0 }, |
| 744 | [IEEE80211_MODE_11A] = { 3, 4, 6, 0, 0 }, | |
| 745 | [IEEE80211_MODE_11B] = { 3, 4, 6, 0, 0 }, | |
| 746 | [IEEE80211_MODE_11G] = { 3, 4, 6, 0, 0 }, | |
| 747 | [IEEE80211_MODE_FH] = { 3, 4, 6, 0, 0 }, | |
| 748 | [IEEE80211_MODE_TURBO_A]= { 2, 3, 5, 0, 0 }, | |
| 749 | [IEEE80211_MODE_TURBO_G]= { 2, 3, 5, 0, 0 }, | |
| 750 | [IEEE80211_MODE_STURBO_A]={ 2, 3, 5, 0, 0 }, | |
| 751 | [IEEE80211_MODE_HALF] = { 3, 4, 6, 0, 0 }, | |
| 752 | [IEEE80211_MODE_QUARTER]= { 3, 4, 6, 0, 0 }, | |
| 753 | [IEEE80211_MODE_11NA] = { 3, 4, 6, 0, 0 }, | |
| 754 | [IEEE80211_MODE_11NG] = { 3, 4, 6, 0, 0 }, | |
| 841ab66c SZ |
755 | }; |
| 756 | static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
757 | [IEEE80211_MODE_AUTO] = { 7, 4, 10, 0, 0 }, |
| 758 | [IEEE80211_MODE_11A] = { 7, 4, 10, 0, 0 }, | |
| 759 | [IEEE80211_MODE_11B] = { 7, 4, 10, 0, 0 }, | |
| 760 | [IEEE80211_MODE_11G] = { 7, 4, 10, 0, 0 }, | |
| 761 | [IEEE80211_MODE_FH] = { 7, 4, 10, 0, 0 }, | |
| 762 | [IEEE80211_MODE_TURBO_A]= { 7, 3, 10, 0, 0 }, | |
| 763 | [IEEE80211_MODE_TURBO_G]= { 7, 3, 10, 0, 0 }, | |
| 764 | [IEEE80211_MODE_STURBO_A]={ 7, 3, 10, 0, 0 }, | |
| 765 | [IEEE80211_MODE_HALF] = { 7, 4, 10, 0, 0 }, | |
| 766 | [IEEE80211_MODE_QUARTER]= { 7, 4, 10, 0, 0 }, | |
| 767 | [IEEE80211_MODE_11NA] = { 7, 4, 10, 0, 0 }, | |
| 768 | [IEEE80211_MODE_11NG] = { 7, 4, 10, 0, 0 }, | |
| 841ab66c SZ |
769 | }; |
| 770 | static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
771 | [IEEE80211_MODE_AUTO] = { 1, 3, 4, 94, 0 }, |
| 772 | [IEEE80211_MODE_11A] = { 1, 3, 4, 94, 0 }, | |
| 773 | [IEEE80211_MODE_11B] = { 1, 3, 4, 188, 0 }, | |
| 774 | [IEEE80211_MODE_11G] = { 1, 3, 4, 94, 0 }, | |
| 775 | [IEEE80211_MODE_FH] = { 1, 3, 4, 188, 0 }, | |
| 776 | [IEEE80211_MODE_TURBO_A]= { 1, 2, 3, 94, 0 }, | |
| 777 | [IEEE80211_MODE_TURBO_G]= { 1, 2, 3, 94, 0 }, | |
| 778 | [IEEE80211_MODE_STURBO_A]={ 1, 2, 3, 94, 0 }, | |
| 779 | [IEEE80211_MODE_HALF] = { 1, 3, 4, 94, 0 }, | |
| 780 | [IEEE80211_MODE_QUARTER]= { 1, 3, 4, 94, 0 }, | |
| 781 | [IEEE80211_MODE_11NA] = { 1, 3, 4, 94, 0 }, | |
| 782 | [IEEE80211_MODE_11NG] = { 1, 3, 4, 94, 0 }, | |
| 841ab66c SZ |
783 | }; |
| 784 | static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
785 | [IEEE80211_MODE_AUTO] = { 1, 2, 3, 47, 0 }, |
| 786 | [IEEE80211_MODE_11A] = { 1, 2, 3, 47, 0 }, | |
| 787 | [IEEE80211_MODE_11B] = { 1, 2, 3, 102, 0 }, | |
| 788 | [IEEE80211_MODE_11G] = { 1, 2, 3, 47, 0 }, | |
| 789 | [IEEE80211_MODE_FH] = { 1, 2, 3, 102, 0 }, | |
| 790 | [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, | |
| 791 | [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, | |
| 792 | [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, | |
| 793 | [IEEE80211_MODE_HALF] = { 1, 2, 3, 47, 0 }, | |
| 794 | [IEEE80211_MODE_QUARTER]= { 1, 2, 3, 47, 0 }, | |
| 795 | [IEEE80211_MODE_11NA] = { 1, 2, 3, 47, 0 }, | |
| 796 | [IEEE80211_MODE_11NG] = { 1, 2, 3, 47, 0 }, | |
| 841ab66c SZ |
797 | }; |
| 798 | ||
| 799 | static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
800 | [IEEE80211_MODE_AUTO] = { 3, 4, 10, 0, 0 }, |
| 801 | [IEEE80211_MODE_11A] = { 3, 4, 10, 0, 0 }, | |
| 802 | [IEEE80211_MODE_11B] = { 3, 4, 10, 0, 0 }, | |
| 803 | [IEEE80211_MODE_11G] = { 3, 4, 10, 0, 0 }, | |
| 804 | [IEEE80211_MODE_FH] = { 3, 4, 10, 0, 0 }, | |
| 805 | [IEEE80211_MODE_TURBO_A]= { 2, 3, 10, 0, 0 }, | |
| 806 | [IEEE80211_MODE_TURBO_G]= { 2, 3, 10, 0, 0 }, | |
| 807 | [IEEE80211_MODE_STURBO_A]={ 2, 3, 10, 0, 0 }, | |
| 808 | [IEEE80211_MODE_HALF] = { 3, 4, 10, 0, 0 }, | |
| 809 | [IEEE80211_MODE_QUARTER]= { 3, 4, 10, 0, 0 }, | |
| 810 | [IEEE80211_MODE_11NA] = { 3, 4, 10, 0, 0 }, | |
| 811 | [IEEE80211_MODE_11NG] = { 3, 4, 10, 0, 0 }, | |
| 841ab66c SZ |
812 | }; |
| 813 | static const struct phyParamType bssPhyParamForAC_VI[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
814 | [IEEE80211_MODE_AUTO] = { 2, 3, 4, 94, 0 }, |
| 815 | [IEEE80211_MODE_11A] = { 2, 3, 4, 94, 0 }, | |
| 816 | [IEEE80211_MODE_11B] = { 2, 3, 4, 188, 0 }, | |
| 817 | [IEEE80211_MODE_11G] = { 2, 3, 4, 94, 0 }, | |
| 818 | [IEEE80211_MODE_FH] = { 2, 3, 4, 188, 0 }, | |
| 819 | [IEEE80211_MODE_TURBO_A]= { 2, 2, 3, 94, 0 }, | |
| 820 | [IEEE80211_MODE_TURBO_G]= { 2, 2, 3, 94, 0 }, | |
| 821 | [IEEE80211_MODE_STURBO_A]={ 2, 2, 3, 94, 0 }, | |
| 822 | [IEEE80211_MODE_HALF] = { 2, 3, 4, 94, 0 }, | |
| 823 | [IEEE80211_MODE_QUARTER]= { 2, 3, 4, 94, 0 }, | |
| 824 | [IEEE80211_MODE_11NA] = { 2, 3, 4, 94, 0 }, | |
| 825 | [IEEE80211_MODE_11NG] = { 2, 3, 4, 94, 0 }, | |
| 841ab66c SZ |
826 | }; |
| 827 | static const struct phyParamType bssPhyParamForAC_VO[IEEE80211_MODE_MAX] = { | |
| 32176cfd RP |
828 | [IEEE80211_MODE_AUTO] = { 2, 2, 3, 47, 0 }, |
| 829 | [IEEE80211_MODE_11A] = { 2, 2, 3, 47, 0 }, | |
| 830 | [IEEE80211_MODE_11B] = { 2, 2, 3, 102, 0 }, | |
| 831 | [IEEE80211_MODE_11G] = { 2, 2, 3, 47, 0 }, | |
| 832 | [IEEE80211_MODE_FH] = { 2, 2, 3, 102, 0 }, | |
| 833 | [IEEE80211_MODE_TURBO_A]= { 1, 2, 2, 47, 0 }, | |
| 834 | [IEEE80211_MODE_TURBO_G]= { 1, 2, 2, 47, 0 }, | |
| 835 | [IEEE80211_MODE_STURBO_A]={ 1, 2, 2, 47, 0 }, | |
| 836 | [IEEE80211_MODE_HALF] = { 2, 2, 3, 47, 0 }, | |
| 837 | [IEEE80211_MODE_QUARTER]= { 2, 2, 3, 47, 0 }, | |
| 838 | [IEEE80211_MODE_11NA] = { 2, 2, 3, 47, 0 }, | |
| 839 | [IEEE80211_MODE_11NG] = { 2, 2, 3, 47, 0 }, | |
| 841ab66c SZ |
840 | }; |
| 841 | ||
| 32176cfd RP |
842 | static void |
| 843 | _setifsparams(struct wmeParams *wmep, const paramType *phy) | |
| 844 | { | |
| 845 | wmep->wmep_aifsn = phy->aifsn; | |
| 846 | wmep->wmep_logcwmin = phy->logcwmin; | |
| 847 | wmep->wmep_logcwmax = phy->logcwmax; | |
| 848 | wmep->wmep_txopLimit = phy->txopLimit; | |
| 849 | } | |
| 850 | ||
| 851 | static void | |
| 852 | setwmeparams(struct ieee80211vap *vap, const char *type, int ac, | |
| 853 | struct wmeParams *wmep, const paramType *phy) | |
| 841ab66c | 854 | { |
| 32176cfd RP |
855 | wmep->wmep_acm = phy->acm; |
| 856 | _setifsparams(wmep, phy); | |
| 857 | ||
| 858 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, | |
| 859 | "set %s (%s) [acm %u aifsn %u logcwmin %u logcwmax %u txop %u]\n", | |
| 860 | ieee80211_wme_acnames[ac], type, | |
| 861 | wmep->wmep_acm, wmep->wmep_aifsn, wmep->wmep_logcwmin, | |
| 862 | wmep->wmep_logcwmax, wmep->wmep_txopLimit); | |
| 863 | } | |
| 864 | ||
| 865 | static void | |
| 866 | ieee80211_wme_initparams_locked(struct ieee80211vap *vap) | |
| 867 | { | |
| 868 | struct ieee80211com *ic = vap->iv_ic; | |
| 841ab66c SZ |
869 | struct ieee80211_wme_state *wme = &ic->ic_wme; |
| 870 | const paramType *pPhyParam, *pBssPhyParam; | |
| 871 | struct wmeParams *wmep; | |
| 32176cfd | 872 | enum ieee80211_phymode mode; |
| 841ab66c SZ |
873 | int i; |
| 874 | ||
| 32176cfd | 875 | if ((ic->ic_caps & IEEE80211_C_WME) == 0 || ic->ic_nrunning > 1) |
| 841ab66c SZ |
876 | return; |
| 877 | ||
| 32176cfd RP |
878 | /* |
| 879 | * Select mode; we can be called early in which case we | |
| 880 | * always use auto mode. We know we'll be called when | |
| 881 | * entering the RUN state with bsschan setup properly | |
| 882 | * so state will eventually get set correctly | |
| 883 | */ | |
| 884 | if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) | |
| 885 | mode = ieee80211_chan2mode(ic->ic_bsschan); | |
| 886 | else | |
| 887 | mode = IEEE80211_MODE_AUTO; | |
| 841ab66c SZ |
888 | for (i = 0; i < WME_NUM_AC; i++) { |
| 889 | switch (i) { | |
| 890 | case WME_AC_BK: | |
| 32176cfd RP |
891 | pPhyParam = &phyParamForAC_BK[mode]; |
| 892 | pBssPhyParam = &phyParamForAC_BK[mode]; | |
| 841ab66c SZ |
893 | break; |
| 894 | case WME_AC_VI: | |
| 32176cfd RP |
895 | pPhyParam = &phyParamForAC_VI[mode]; |
| 896 | pBssPhyParam = &bssPhyParamForAC_VI[mode]; | |
| 841ab66c SZ |
897 | break; |
| 898 | case WME_AC_VO: | |
| 32176cfd RP |
899 | pPhyParam = &phyParamForAC_VO[mode]; |
| 900 | pBssPhyParam = &bssPhyParamForAC_VO[mode]; | |
| 841ab66c SZ |
901 | break; |
| 902 | case WME_AC_BE: | |
| 903 | default: | |
| 32176cfd RP |
904 | pPhyParam = &phyParamForAC_BE[mode]; |
| 905 | pBssPhyParam = &bssPhyParamForAC_BE[mode]; | |
| 841ab66c SZ |
906 | break; |
| 907 | } | |
| 841ab66c SZ |
908 | wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; |
| 909 | if (ic->ic_opmode == IEEE80211_M_HOSTAP) { | |
| 32176cfd | 910 | setwmeparams(vap, "chan", i, wmep, pPhyParam); |
| 841ab66c | 911 | } else { |
| 32176cfd | 912 | setwmeparams(vap, "chan", i, wmep, pBssPhyParam); |
| 841ab66c | 913 | } |
| 841ab66c | 914 | wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; |
| 32176cfd | 915 | setwmeparams(vap, "bss ", i, wmep, pBssPhyParam); |
| 841ab66c SZ |
916 | } |
| 917 | /* NB: check ic_bss to avoid NULL deref on initial attach */ | |
| 32176cfd | 918 | if (vap->iv_bss != NULL) { |
| 841ab66c SZ |
919 | /* |
| 920 | * Calculate agressive mode switching threshold based | |
| 921 | * on beacon interval. This doesn't need locking since | |
| 922 | * we're only called before entering the RUN state at | |
| 923 | * which point we start sending beacon frames. | |
| 924 | */ | |
| 925 | wme->wme_hipri_switch_thresh = | |
| 32176cfd RP |
926 | (HIGH_PRI_SWITCH_THRESH * vap->iv_bss->ni_intval) / 100; |
| 927 | wme->wme_flags &= ~WME_F_AGGRMODE; | |
| 928 | ieee80211_wme_updateparams(vap); | |
| 841ab66c SZ |
929 | } |
| 930 | } | |
| 931 | ||
| 32176cfd RP |
932 | void |
| 933 | ieee80211_wme_initparams(struct ieee80211vap *vap) | |
| 934 | { | |
| 32176cfd | 935 | ieee80211_wme_initparams_locked(vap); |
| 32176cfd RP |
936 | } |
| 937 | ||
| 841ab66c SZ |
938 | /* |
| 939 | * Update WME parameters for ourself and the BSS. | |
| 940 | */ | |
| 941 | void | |
| 32176cfd | 942 | ieee80211_wme_updateparams_locked(struct ieee80211vap *vap) |
| 841ab66c | 943 | { |
| 32176cfd RP |
944 | static const paramType aggrParam[IEEE80211_MODE_MAX] = { |
| 945 | [IEEE80211_MODE_AUTO] = { 2, 4, 10, 64, 0 }, | |
| 946 | [IEEE80211_MODE_11A] = { 2, 4, 10, 64, 0 }, | |
| 947 | [IEEE80211_MODE_11B] = { 2, 5, 10, 64, 0 }, | |
| 948 | [IEEE80211_MODE_11G] = { 2, 4, 10, 64, 0 }, | |
| 949 | [IEEE80211_MODE_FH] = { 2, 5, 10, 64, 0 }, | |
| 950 | [IEEE80211_MODE_TURBO_A] = { 1, 3, 10, 64, 0 }, | |
| 951 | [IEEE80211_MODE_TURBO_G] = { 1, 3, 10, 64, 0 }, | |
| 952 | [IEEE80211_MODE_STURBO_A] = { 1, 3, 10, 64, 0 }, | |
| 953 | [IEEE80211_MODE_HALF] = { 2, 4, 10, 64, 0 }, | |
| 954 | [IEEE80211_MODE_QUARTER] = { 2, 4, 10, 64, 0 }, | |
| 955 | [IEEE80211_MODE_11NA] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/ | |
| 956 | [IEEE80211_MODE_11NG] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/ | |
| 841ab66c | 957 | }; |
| 32176cfd | 958 | struct ieee80211com *ic = vap->iv_ic; |
| 841ab66c SZ |
959 | struct ieee80211_wme_state *wme = &ic->ic_wme; |
| 960 | const struct wmeParams *wmep; | |
| 961 | struct wmeParams *chanp, *bssp; | |
| 32176cfd | 962 | enum ieee80211_phymode mode; |
| 841ab66c SZ |
963 | int i; |
| 964 | ||
| 32176cfd RP |
965 | /* |
| 966 | * Set up the channel access parameters for the physical | |
| 967 | * device. First populate the configured settings. | |
| 968 | */ | |
| 841ab66c SZ |
969 | for (i = 0; i < WME_NUM_AC; i++) { |
| 970 | chanp = &wme->wme_chanParams.cap_wmeParams[i]; | |
| 971 | wmep = &wme->wme_wmeChanParams.cap_wmeParams[i]; | |
| 972 | chanp->wmep_aifsn = wmep->wmep_aifsn; | |
| 973 | chanp->wmep_logcwmin = wmep->wmep_logcwmin; | |
| 974 | chanp->wmep_logcwmax = wmep->wmep_logcwmax; | |
| 975 | chanp->wmep_txopLimit = wmep->wmep_txopLimit; | |
| 976 | ||
| 977 | chanp = &wme->wme_bssChanParams.cap_wmeParams[i]; | |
| 978 | wmep = &wme->wme_wmeBssChanParams.cap_wmeParams[i]; | |
| 979 | chanp->wmep_aifsn = wmep->wmep_aifsn; | |
| 980 | chanp->wmep_logcwmin = wmep->wmep_logcwmin; | |
| 981 | chanp->wmep_logcwmax = wmep->wmep_logcwmax; | |
| 982 | chanp->wmep_txopLimit = wmep->wmep_txopLimit; | |
| 983 | } | |
| 984 | ||
| 985 | /* | |
| 32176cfd RP |
986 | * Select mode; we can be called early in which case we |
| 987 | * always use auto mode. We know we'll be called when | |
| 988 | * entering the RUN state with bsschan setup properly | |
| 989 | * so state will eventually get set correctly | |
| 990 | */ | |
| 991 | if (ic->ic_bsschan != IEEE80211_CHAN_ANYC) | |
| 992 | mode = ieee80211_chan2mode(ic->ic_bsschan); | |
| 993 | else | |
| 994 | mode = IEEE80211_MODE_AUTO; | |
| 995 | ||
| 996 | /* | |
| 841ab66c SZ |
997 | * This implements agressive mode as found in certain |
| 998 | * vendors' AP's. When there is significant high | |
| 999 | * priority (VI/VO) traffic in the BSS throttle back BE | |
| 1000 | * traffic by using conservative parameters. Otherwise | |
| 1001 | * BE uses agressive params to optimize performance of | |
| 1002 | * legacy/non-QoS traffic. | |
| 1003 | */ | |
| 32176cfd | 1004 | if ((vap->iv_opmode == IEEE80211_M_HOSTAP && |
| 841ab66c | 1005 | (wme->wme_flags & WME_F_AGGRMODE) != 0) || |
| 32176cfd RP |
1006 | (vap->iv_opmode == IEEE80211_M_STA && |
| 1007 | (vap->iv_bss->ni_flags & IEEE80211_NODE_QOS) == 0) || | |
| 1008 | (vap->iv_flags & IEEE80211_F_WME) == 0) { | |
| 841ab66c SZ |
1009 | chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; |
| 1010 | bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; | |
| 1011 | ||
| 32176cfd | 1012 | chanp->wmep_aifsn = bssp->wmep_aifsn = aggrParam[mode].aifsn; |
| 841ab66c | 1013 | chanp->wmep_logcwmin = bssp->wmep_logcwmin = |
| 32176cfd | 1014 | aggrParam[mode].logcwmin; |
| 841ab66c | 1015 | chanp->wmep_logcwmax = bssp->wmep_logcwmax = |
| 32176cfd | 1016 | aggrParam[mode].logcwmax; |
| 841ab66c | 1017 | chanp->wmep_txopLimit = bssp->wmep_txopLimit = |
| 32176cfd RP |
1018 | (vap->iv_flags & IEEE80211_F_BURST) ? |
| 1019 | aggrParam[mode].txopLimit : 0; | |
| 1020 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, | |
| 1021 | "update %s (chan+bss) [acm %u aifsn %u logcwmin %u " | |
| 1022 | "logcwmax %u txop %u]\n", ieee80211_wme_acnames[WME_AC_BE], | |
| 1023 | chanp->wmep_acm, chanp->wmep_aifsn, chanp->wmep_logcwmin, | |
| 1024 | chanp->wmep_logcwmax, chanp->wmep_txopLimit); | |
| 841ab66c SZ |
1025 | } |
| 1026 | ||
| 32176cfd | 1027 | if (vap->iv_opmode == IEEE80211_M_HOSTAP && |
| 841ab66c | 1028 | ic->ic_sta_assoc < 2 && (wme->wme_flags & WME_F_AGGRMODE) != 0) { |
| 32176cfd RP |
1029 | static const uint8_t logCwMin[IEEE80211_MODE_MAX] = { |
| 1030 | [IEEE80211_MODE_AUTO] = 3, | |
| 1031 | [IEEE80211_MODE_11A] = 3, | |
| 1032 | [IEEE80211_MODE_11B] = 4, | |
| 1033 | [IEEE80211_MODE_11G] = 3, | |
| 1034 | [IEEE80211_MODE_FH] = 4, | |
| 1035 | [IEEE80211_MODE_TURBO_A] = 3, | |
| 1036 | [IEEE80211_MODE_TURBO_G] = 3, | |
| 1037 | [IEEE80211_MODE_STURBO_A] = 3, | |
| 1038 | [IEEE80211_MODE_HALF] = 3, | |
| 1039 | [IEEE80211_MODE_QUARTER] = 3, | |
| 1040 | [IEEE80211_MODE_11NA] = 3, | |
| 1041 | [IEEE80211_MODE_11NG] = 3, | |
| 841ab66c SZ |
1042 | }; |
| 1043 | chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE]; | |
| 1044 | bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE]; | |
| 1045 | ||
| 32176cfd RP |
1046 | chanp->wmep_logcwmin = bssp->wmep_logcwmin = logCwMin[mode]; |
| 1047 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, | |
| 1048 | "update %s (chan+bss) logcwmin %u\n", | |
| 1049 | ieee80211_wme_acnames[WME_AC_BE], chanp->wmep_logcwmin); | |
| 841ab66c | 1050 | } |
| 32176cfd | 1051 | if (vap->iv_opmode == IEEE80211_M_HOSTAP) { /* XXX ibss? */ |
| 841ab66c SZ |
1052 | /* |
| 1053 | * Arrange for a beacon update and bump the parameter | |
| 1054 | * set number so associated stations load the new values. | |
| 1055 | */ | |
| 1056 | wme->wme_bssChanParams.cap_info = | |
| 1057 | (wme->wme_bssChanParams.cap_info+1) & WME_QOSINFO_COUNT; | |
| 32176cfd | 1058 | ieee80211_beacon_notify(vap, IEEE80211_BEACON_WME); |
| 841ab66c SZ |
1059 | } |
| 1060 | ||
| 1061 | wme->wme_update(ic); | |
| 1062 | ||
| 32176cfd RP |
1063 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME, |
| 1064 | "%s: WME params updated, cap_info 0x%x\n", __func__, | |
| 1065 | vap->iv_opmode == IEEE80211_M_STA ? | |
| 1066 | wme->wme_wmeChanParams.cap_info : | |
| 1067 | wme->wme_bssChanParams.cap_info); | |
| 841ab66c SZ |
1068 | } |
| 1069 | ||
| 1070 | void | |
| 32176cfd | 1071 | ieee80211_wme_updateparams(struct ieee80211vap *vap) |
| 841ab66c | 1072 | { |
| 32176cfd | 1073 | struct ieee80211com *ic = vap->iv_ic; |
| 841ab66c | 1074 | |
| 32176cfd | 1075 | if (ic->ic_caps & IEEE80211_C_WME) { |
| 32176cfd | 1076 | ieee80211_wme_updateparams_locked(vap); |
| 841ab66c | 1077 | } |
| 32176cfd RP |
1078 | } |
| 1079 | ||
| 1080 | static void | |
| 47156d48 | 1081 | parent_updown_task(void *arg, int npending) |
| 32176cfd RP |
1082 | { |
| 1083 | struct ifnet *parent = arg; | |
| 1084 | ||
| 47156d48 | 1085 | wlan_serialize_enter(); |
| 34a60cf6 | 1086 | parent->if_ioctl(parent, SIOCSIFFLAGS, NULL, curthread->td_ucred); |
| 47156d48 | 1087 | wlan_serialize_exit(); |
| 32176cfd RP |
1088 | } |
| 1089 | ||
| 1090 | static void | |
| 47156d48 | 1091 | update_mcast_task(void *arg, int npending) |
| 32176cfd RP |
1092 | { |
| 1093 | struct ieee80211com *ic = arg; | |
| 1094 | struct ifnet *parent = ic->ic_ifp; | |
| 1095 | ||
| 47156d48 | 1096 | wlan_serialize_enter(); |
| 32176cfd | 1097 | ic->ic_update_mcast(parent); |
| 47156d48 | 1098 | wlan_serialize_exit(); |
| 32176cfd RP |
1099 | } |
| 1100 | ||
| 1101 | static void | |
| 47156d48 | 1102 | update_promisc_task(void *arg, int npending) |
| 32176cfd RP |
1103 | { |
| 1104 | struct ieee80211com *ic = arg; | |
| 1105 | struct ifnet *parent = ic->ic_ifp; | |
| 1106 | ||
| 47156d48 | 1107 | wlan_serialize_enter(); |
| 32176cfd | 1108 | ic->ic_update_promisc(parent); |
| 47156d48 | 1109 | wlan_serialize_exit(); |
| 32176cfd RP |
1110 | } |
| 1111 | ||
| 1112 | static void | |
| 47156d48 | 1113 | update_channel_task(void *arg, int npending) |
| 32176cfd RP |
1114 | { |
| 1115 | struct ieee80211com *ic = arg; | |
| 1116 | ||
| 47156d48 | 1117 | wlan_serialize_enter(); |
| 32176cfd RP |
1118 | ic->ic_set_channel(ic); |
| 1119 | ieee80211_radiotap_chan_change(ic); | |
| 47156d48 | 1120 | wlan_serialize_exit(); |
| 32176cfd RP |
1121 | } |
| 1122 | ||
| 1123 | /* | |
| 1124 | * Block until the parent is in a known state. This is | |
| 1125 | * used after any operations that dispatch a task (e.g. | |
| 1126 | * to auto-configure the parent device up/down). | |
| 1127 | */ | |
| 1128 | void | |
| 1129 | ieee80211_waitfor_parent(struct ieee80211com *ic) | |
| 1130 | { | |
| 51237956 MD |
1131 | wlan_assert_serialized(); |
| 1132 | wlan_serialize_exit(); /* exit to block */ | |
| 32176cfd RP |
1133 | taskqueue_block(ic->ic_tq); |
| 1134 | ieee80211_draintask(ic, &ic->ic_parent_task); | |
| 1135 | ieee80211_draintask(ic, &ic->ic_mcast_task); | |
| 1136 | ieee80211_draintask(ic, &ic->ic_promisc_task); | |
| 1137 | ieee80211_draintask(ic, &ic->ic_chan_task); | |
| 1138 | ieee80211_draintask(ic, &ic->ic_bmiss_task); | |
| 1139 | taskqueue_unblock(ic->ic_tq); | |
| 51237956 | 1140 | wlan_serialize_enter(); /* then re-enter */ |
| 32176cfd RP |
1141 | } |
| 1142 | ||
| 1143 | /* | |
| 1144 | * Start a vap running. If this is the first vap to be | |
| 1145 | * set running on the underlying device then we | |
| 1146 | * automatically bring the device up. | |
| 1147 | */ | |
| 1148 | void | |
| 1149 | ieee80211_start_locked(struct ieee80211vap *vap) | |
| 1150 | { | |
| 1151 | struct ifnet *ifp = vap->iv_ifp; | |
| 1152 | struct ieee80211com *ic = vap->iv_ic; | |
| 1153 | struct ifnet *parent = ic->ic_ifp; | |
| 1154 | ||
| 32176cfd | 1155 | IEEE80211_DPRINTF(vap, |
| 841ab66c | 1156 | IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, |
| 32176cfd | 1157 | "start running, %d vaps running\n", ic->ic_nrunning); |
| 841ab66c | 1158 | |
| 34a60cf6 | 1159 | if ((ifp->if_flags & IFF_RUNNING) == 0) { |
| 32176cfd RP |
1160 | /* |
| 1161 | * Mark us running. Note that it's ok to do this first; | |
| 1162 | * if we need to bring the parent device up we defer that | |
| 1163 | * to avoid dropping the com lock. We expect the device | |
| 1164 | * to respond to being marked up by calling back into us | |
| 1165 | * through ieee80211_start_all at which point we'll come | |
| 1166 | * back in here and complete the work. | |
| 1167 | */ | |
| 34a60cf6 | 1168 | ifp->if_flags |= IFF_RUNNING; |
| 32176cfd RP |
1169 | /* |
| 1170 | * We are not running; if this we are the first vap | |
| 1171 | * to be brought up auto-up the parent if necessary. | |
| 1172 | */ | |
| 1173 | if (ic->ic_nrunning++ == 0 && | |
| 34a60cf6 | 1174 | (parent->if_flags & IFF_RUNNING) == 0) { |
| 32176cfd RP |
1175 | IEEE80211_DPRINTF(vap, |
| 1176 | IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, | |
| 1177 | "%s: up parent %s\n", __func__, parent->if_xname); | |
| 1178 | parent->if_flags |= IFF_UP; | |
| 1179 | ieee80211_runtask(ic, &ic->ic_parent_task); | |
| 1180 | return; | |
| 1181 | } | |
| 1182 | } | |
| 841ab66c | 1183 | /* |
| 32176cfd RP |
1184 | * If the parent is up and running, then kick the |
| 1185 | * 802.11 state machine as appropriate. | |
| 841ab66c | 1186 | */ |
| 34a60cf6 | 1187 | if ((parent->if_flags & IFF_RUNNING) && |
| 32176cfd RP |
1188 | vap->iv_roaming != IEEE80211_ROAMING_MANUAL) { |
| 1189 | if (vap->iv_opmode == IEEE80211_M_STA) { | |
| 1190 | #if 0 | |
| 1191 | /* XXX bypasses scan too easily; disable for now */ | |
| 1192 | /* | |
| 1193 | * Try to be intelligent about clocking the state | |
| 1194 | * machine. If we're currently in RUN state then | |
| 1195 | * we should be able to apply any new state/parameters | |
| 1196 | * simply by re-associating. Otherwise we need to | |
| 1197 | * re-scan to select an appropriate ap. | |
| 1198 | */ | |
| 1199 | if (vap->iv_state >= IEEE80211_S_RUN) | |
| 1200 | ieee80211_new_state_locked(vap, | |
| 1201 | IEEE80211_S_ASSOC, 1); | |
| 1202 | else | |
| 1203 | #endif | |
| 1204 | ieee80211_new_state_locked(vap, | |
| 1205 | IEEE80211_S_SCAN, 0); | |
| 1206 | } else { | |
| 1207 | /* | |
| 1208 | * For monitor+wds mode there's nothing to do but | |
| 1209 | * start running. Otherwise if this is the first | |
| 1210 | * vap to be brought up, start a scan which may be | |
| 1211 | * preempted if the station is locked to a particular | |
| 1212 | * channel. | |
| 1213 | */ | |
| 1214 | vap->iv_flags_ext |= IEEE80211_FEXT_REINIT; | |
| 1215 | if (vap->iv_opmode == IEEE80211_M_MONITOR || | |
| 1216 | vap->iv_opmode == IEEE80211_M_WDS) | |
| 1217 | ieee80211_new_state_locked(vap, | |
| 1218 | IEEE80211_S_RUN, -1); | |
| 1219 | else | |
| 1220 | ieee80211_new_state_locked(vap, | |
| 1221 | IEEE80211_S_SCAN, 0); | |
| 1222 | } | |
| 1223 | } | |
| 1224 | } | |
| 1225 | ||
| 1226 | /* | |
| 1227 | * Start a single vap. | |
| 1228 | */ | |
| 1229 | void | |
| 1230 | ieee80211_init(void *arg) | |
| 1231 | { | |
| 1232 | struct ieee80211vap *vap = arg; | |
| 1233 | ||
| 1234 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, | |
| 1235 | "%s\n", __func__); | |
| 1236 | ||
| 32176cfd | 1237 | ieee80211_start_locked(vap); |
| 32176cfd RP |
1238 | } |
| 1239 | ||
| 1240 | /* | |
| 1241 | * Start all runnable vap's on a device. | |
| 1242 | */ | |
| 1243 | void | |
| 1244 | ieee80211_start_all(struct ieee80211com *ic) | |
| 1245 | { | |
| 1246 | struct ieee80211vap *vap; | |
| 841ab66c | 1247 | |
| 32176cfd RP |
1248 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 1249 | struct ifnet *ifp = vap->iv_ifp; | |
| 1250 | if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ | |
| 1251 | ieee80211_start_locked(vap); | |
| 1252 | } | |
| 32176cfd RP |
1253 | } |
| 1254 | ||
| 1255 | /* | |
| 1256 | * Stop a vap. We force it down using the state machine | |
| 1257 | * then mark it's ifnet not running. If this is the last | |
| 1258 | * vap running on the underlying device then we close it | |
| 1259 | * too to insure it will be properly initialized when the | |
| 1260 | * next vap is brought up. | |
| 1261 | */ | |
| 1262 | void | |
| 1263 | ieee80211_stop_locked(struct ieee80211vap *vap) | |
| 1264 | { | |
| 1265 | struct ieee80211com *ic = vap->iv_ic; | |
| 1266 | struct ifnet *ifp = vap->iv_ifp; | |
| 1267 | struct ifnet *parent = ic->ic_ifp; | |
| 1268 | ||
| 32176cfd RP |
1269 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, |
| 1270 | "stop running, %d vaps running\n", ic->ic_nrunning); | |
| 1271 | ||
| 1272 | ieee80211_new_state_locked(vap, IEEE80211_S_INIT, -1); | |
| 34a60cf6 RP |
1273 | if (ifp->if_flags & IFF_RUNNING) { |
| 1274 | ifp->if_flags &= ~IFF_RUNNING; /* mark us stopped */ | |
| 32176cfd | 1275 | if (--ic->ic_nrunning == 0 && |
| 34a60cf6 | 1276 | (parent->if_flags & IFF_RUNNING)) { |
| 32176cfd RP |
1277 | IEEE80211_DPRINTF(vap, |
| 1278 | IEEE80211_MSG_STATE | IEEE80211_MSG_DEBUG, | |
| 1279 | "down parent %s\n", parent->if_xname); | |
| 1280 | parent->if_flags &= ~IFF_UP; | |
| 1281 | ieee80211_runtask(ic, &ic->ic_parent_task); | |
| 1282 | } | |
| 1283 | } | |
| 1284 | } | |
| 1285 | ||
| 1286 | void | |
| 1287 | ieee80211_stop(struct ieee80211vap *vap) | |
| 1288 | { | |
| 32176cfd | 1289 | ieee80211_stop_locked(vap); |
| 32176cfd RP |
1290 | } |
| 1291 | ||
| 1292 | /* | |
| 1293 | * Stop all vap's running on a device. | |
| 1294 | */ | |
| 1295 | void | |
| 1296 | ieee80211_stop_all(struct ieee80211com *ic) | |
| 1297 | { | |
| 1298 | struct ieee80211vap *vap; | |
| 1299 | ||
| 32176cfd RP |
1300 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 1301 | struct ifnet *ifp = vap->iv_ifp; | |
| 1302 | if (IFNET_IS_UP_RUNNING(ifp)) /* NB: avoid recursion */ | |
| 1303 | ieee80211_stop_locked(vap); | |
| 1304 | } | |
| 32176cfd RP |
1305 | |
| 1306 | ieee80211_waitfor_parent(ic); | |
| 1307 | } | |
| 1308 | ||
| 1309 | /* | |
| 1310 | * Stop all vap's running on a device and arrange | |
| 1311 | * for those that were running to be resumed. | |
| 1312 | */ | |
| 1313 | void | |
| 1314 | ieee80211_suspend_all(struct ieee80211com *ic) | |
| 1315 | { | |
| 1316 | struct ieee80211vap *vap; | |
| 1317 | ||
| 32176cfd RP |
1318 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 1319 | struct ifnet *ifp = vap->iv_ifp; | |
| 1320 | if (IFNET_IS_UP_RUNNING(ifp)) { /* NB: avoid recursion */ | |
| 1321 | vap->iv_flags_ext |= IEEE80211_FEXT_RESUME; | |
| 1322 | ieee80211_stop_locked(vap); | |
| 1323 | } | |
| 1324 | } | |
| 32176cfd RP |
1325 | |
| 1326 | ieee80211_waitfor_parent(ic); | |
| 1327 | } | |
| 1328 | ||
| 1329 | /* | |
| 1330 | * Start all vap's marked for resume. | |
| 1331 | */ | |
| 1332 | void | |
| 1333 | ieee80211_resume_all(struct ieee80211com *ic) | |
| 1334 | { | |
| 1335 | struct ieee80211vap *vap; | |
| 1336 | ||
| 32176cfd RP |
1337 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 1338 | struct ifnet *ifp = vap->iv_ifp; | |
| 1339 | if (!IFNET_IS_UP_RUNNING(ifp) && | |
| 1340 | (vap->iv_flags_ext & IEEE80211_FEXT_RESUME)) { | |
| 1341 | vap->iv_flags_ext &= ~IEEE80211_FEXT_RESUME; | |
| 1342 | ieee80211_start_locked(vap); | |
| 1343 | } | |
| 1344 | } | |
| 32176cfd RP |
1345 | } |
| 1346 | ||
| 1347 | void | |
| 1348 | ieee80211_beacon_miss(struct ieee80211com *ic) | |
| 1349 | { | |
| 32176cfd RP |
1350 | if ((ic->ic_flags & IEEE80211_F_SCAN) == 0) { |
| 1351 | /* Process in a taskq, the handler may reenter the driver */ | |
| 1352 | ieee80211_runtask(ic, &ic->ic_bmiss_task); | |
| 1353 | } | |
| 32176cfd RP |
1354 | } |
| 1355 | ||
| 1356 | static void | |
| 47156d48 | 1357 | beacon_miss_task(void *arg, int npending) |
| 32176cfd RP |
1358 | { |
| 1359 | struct ieee80211com *ic = arg; | |
| 1360 | struct ieee80211vap *vap; | |
| 1361 | ||
| 47156d48 | 1362 | wlan_serialize_enter(); |
| 32176cfd | 1363 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { |
| 841ab66c | 1364 | /* |
| 32176cfd RP |
1365 | * We only pass events through for sta vap's in RUN state; |
| 1366 | * may be too restrictive but for now this saves all the | |
| 1367 | * handlers duplicating these checks. | |
| 841ab66c | 1368 | */ |
| 32176cfd RP |
1369 | if (vap->iv_opmode == IEEE80211_M_STA && |
| 1370 | vap->iv_state >= IEEE80211_S_RUN && | |
| 1371 | vap->iv_bmiss != NULL) | |
| 1372 | vap->iv_bmiss(vap); | |
| 841ab66c | 1373 | } |
| 47156d48 | 1374 | wlan_serialize_exit(); |
| 32176cfd RP |
1375 | } |
| 1376 | ||
| 1377 | static void | |
| 47156d48 | 1378 | beacon_swmiss_task(void *arg, int npending) |
| 32176cfd RP |
1379 | { |
| 1380 | struct ieee80211vap *vap = arg; | |
| 1381 | ||
| 47156d48 MD |
1382 | wlan_serialize_enter(); |
| 1383 | if (vap->iv_state == IEEE80211_S_RUN) { | |
| 1384 | /* XXX Call multiple times if npending > zero? */ | |
| 1385 | vap->iv_bmiss(vap); | |
| 1386 | } | |
| 1387 | wlan_serialize_exit(); | |
| 841ab66c SZ |
1388 | } |
| 1389 | ||
| 1390 | /* | |
| 1391 | * Software beacon miss handling. Check if any beacons | |
| 1392 | * were received in the last period. If not post a | |
| 1393 | * beacon miss; otherwise reset the counter. | |
| 1394 | */ | |
| 32176cfd | 1395 | void |
| 47156d48 | 1396 | ieee80211_swbmiss_callout(void *arg) |
| 841ab66c | 1397 | { |
| 32176cfd RP |
1398 | struct ieee80211vap *vap = arg; |
| 1399 | struct ieee80211com *ic = vap->iv_ic; | |
| 841ab66c | 1400 | |
| 47156d48 | 1401 | wlan_serialize_enter(); |
| 32176cfd RP |
1402 | KASSERT(vap->iv_state == IEEE80211_S_RUN, |
| 1403 | ("wrong state %d", vap->iv_state)); | |
| 841ab66c | 1404 | |
| 32176cfd RP |
1405 | if (ic->ic_flags & IEEE80211_F_SCAN) { |
| 1406 | /* | |
| 1407 | * If scanning just ignore and reset state. If we get a | |
| 1408 | * bmiss after coming out of scan because we haven't had | |
| 1409 | * time to receive a beacon then we should probe the AP | |
| 1410 | * before posting a real bmiss (unless iv_bmiss_max has | |
| 1411 | * been artifiically lowered). A cleaner solution might | |
| 1412 | * be to disable the timer on scan start/end but to handle | |
| 1413 | * case of multiple sta vap's we'd need to disable the | |
| 1414 | * timers of all affected vap's. | |
| 1415 | */ | |
| 1416 | vap->iv_swbmiss_count = 0; | |
| 1417 | } else if (vap->iv_swbmiss_count == 0) { | |
| 1418 | if (vap->iv_bmiss != NULL) | |
| 1419 | ieee80211_runtask(ic, &vap->iv_swbmiss_task); | |
| 1420 | if (vap->iv_bmiss_count == 0) /* don't re-arm timer */ | |
| 47156d48 MD |
1421 | goto done; |
| 1422 | } else { | |
| 32176cfd | 1423 | vap->iv_swbmiss_count = 0; |
| 47156d48 | 1424 | } |
| 32176cfd | 1425 | callout_reset(&vap->iv_swbmiss, vap->iv_swbmiss_period, |
| 47156d48 MD |
1426 | ieee80211_swbmiss_callout, vap); |
| 1427 | done: | |
| 1428 | wlan_serialize_exit(); | |
| 32176cfd | 1429 | } |
| 841ab66c | 1430 | |
| 32176cfd RP |
1431 | /* |
| 1432 | * Start an 802.11h channel switch. We record the parameters, | |
| 1433 | * mark the operation pending, notify each vap through the | |
| 1434 | * beacon update mechanism so it can update the beacon frame | |
| 1435 | * contents, and then switch vap's to CSA state to block outbound | |
| 1436 | * traffic. Devices that handle CSA directly can use the state | |
| 1437 | * switch to do the right thing so long as they call | |
| 1438 | * ieee80211_csa_completeswitch when it's time to complete the | |
| 1439 | * channel change. Devices that depend on the net80211 layer can | |
| 1440 | * use ieee80211_beacon_update to handle the countdown and the | |
| 1441 | * channel switch. | |
| 1442 | */ | |
| 1443 | void | |
| 1444 | ieee80211_csa_startswitch(struct ieee80211com *ic, | |
| 1445 | struct ieee80211_channel *c, int mode, int count) | |
| 1446 | { | |
| 1447 | struct ieee80211vap *vap; | |
| 1448 | ||
| 32176cfd RP |
1449 | ic->ic_csa_newchan = c; |
| 1450 | ic->ic_csa_mode = mode; | |
| 1451 | ic->ic_csa_count = count; | |
| 1452 | ic->ic_flags |= IEEE80211_F_CSAPENDING; | |
| 1453 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | |
| 1454 | if (vap->iv_opmode == IEEE80211_M_HOSTAP || | |
| 1455 | vap->iv_opmode == IEEE80211_M_IBSS || | |
| 1456 | vap->iv_opmode == IEEE80211_M_MBSS) | |
| 1457 | ieee80211_beacon_notify(vap, IEEE80211_BEACON_CSA); | |
| 1458 | /* switch to CSA state to block outbound traffic */ | |
| 1459 | if (vap->iv_state == IEEE80211_S_RUN) | |
| 1460 | ieee80211_new_state_locked(vap, IEEE80211_S_CSA, 0); | |
| 1461 | } | |
| 1462 | ieee80211_notify_csa(ic, c, mode, count); | |
| 841ab66c SZ |
1463 | } |
| 1464 | ||
| 1465 | static void | |
| 32176cfd | 1466 | csa_completeswitch(struct ieee80211com *ic) |
| 841ab66c | 1467 | { |
| 32176cfd | 1468 | struct ieee80211vap *vap; |
| 841ab66c | 1469 | |
| 32176cfd RP |
1470 | ic->ic_csa_newchan = NULL; |
| 1471 | ic->ic_flags &= ~IEEE80211_F_CSAPENDING; | |
| 1472 | ||
| 1473 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) | |
| 1474 | if (vap->iv_state == IEEE80211_S_CSA) | |
| 1475 | ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); | |
| 841ab66c SZ |
1476 | } |
| 1477 | ||
| 32176cfd RP |
1478 | /* |
| 1479 | * Complete an 802.11h channel switch started by ieee80211_csa_startswitch. | |
| 1480 | * We clear state and move all vap's in CSA state to RUN state | |
| 1481 | * so they can again transmit. | |
| 1482 | */ | |
| 846cf0bc | 1483 | void |
| 32176cfd | 1484 | ieee80211_csa_completeswitch(struct ieee80211com *ic) |
| 841ab66c | 1485 | { |
| 32176cfd | 1486 | KASSERT(ic->ic_flags & IEEE80211_F_CSAPENDING, ("csa not pending")); |
| 846cf0bc | 1487 | |
| 32176cfd RP |
1488 | ieee80211_setcurchan(ic, ic->ic_csa_newchan); |
| 1489 | csa_completeswitch(ic); | |
| 1490 | } | |
| 846cf0bc | 1491 | |
| 32176cfd RP |
1492 | /* |
| 1493 | * Cancel an 802.11h channel switch started by ieee80211_csa_startswitch. | |
| 1494 | * We clear state and move all vap's in CSA state to RUN state | |
| 1495 | * so they can again transmit. | |
| 1496 | */ | |
| 1497 | void | |
| 1498 | ieee80211_csa_cancelswitch(struct ieee80211com *ic) | |
| 1499 | { | |
| 32176cfd RP |
1500 | csa_completeswitch(ic); |
| 1501 | } | |
| 1502 | ||
| 1503 | /* | |
| 1504 | * Complete a DFS CAC started by ieee80211_dfs_cac_start. | |
| 1505 | * We clear state and move all vap's in CAC state to RUN state. | |
| 1506 | */ | |
| 1507 | void | |
| 1508 | ieee80211_cac_completeswitch(struct ieee80211vap *vap0) | |
| 1509 | { | |
| 1510 | struct ieee80211com *ic = vap0->iv_ic; | |
| 1511 | struct ieee80211vap *vap; | |
| 1512 | ||
| 32176cfd RP |
1513 | /* |
| 1514 | * Complete CAC state change for lead vap first; then | |
| 1515 | * clock all the other vap's waiting. | |
| 1516 | */ | |
| 1517 | KASSERT(vap0->iv_state == IEEE80211_S_CAC, | |
| 1518 | ("wrong state %d", vap0->iv_state)); | |
| 1519 | ieee80211_new_state_locked(vap0, IEEE80211_S_RUN, 0); | |
| 1520 | ||
| 1521 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) | |
| 1522 | if (vap->iv_state == IEEE80211_S_CAC) | |
| 1523 | ieee80211_new_state_locked(vap, IEEE80211_S_RUN, 0); | |
| 32176cfd RP |
1524 | } |
| 1525 | ||
| 1526 | /* | |
| 1527 | * Force all vap's other than the specified vap to the INIT state | |
| 1528 | * and mark them as waiting for a scan to complete. These vaps | |
| 1529 | * will be brought up when the scan completes and the scanning vap | |
| 1530 | * reaches RUN state by wakeupwaiting. | |
| 1531 | */ | |
| 1532 | static void | |
| 1533 | markwaiting(struct ieee80211vap *vap0) | |
| 1534 | { | |
| 1535 | struct ieee80211com *ic = vap0->iv_ic; | |
| 1536 | struct ieee80211vap *vap; | |
| 1537 | ||
| 32176cfd RP |
1538 | /* |
| 1539 | * A vap list entry can not disappear since we are running on the | |
| 1540 | * taskqueue and a vap destroy will queue and drain another state | |
| 1541 | * change task. | |
| 1542 | */ | |
| 1543 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | |
| 1544 | if (vap == vap0) | |
| 1545 | continue; | |
| 1546 | if (vap->iv_state != IEEE80211_S_INIT) { | |
| 1547 | /* NB: iv_newstate may drop the lock */ | |
| 1548 | vap->iv_newstate(vap, IEEE80211_S_INIT, 0); | |
| 1549 | vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; | |
| 846cf0bc | 1550 | } |
| 32176cfd RP |
1551 | } |
| 1552 | } | |
| 1553 | ||
| 1554 | /* | |
| 1555 | * Wakeup all vap's waiting for a scan to complete. This is the | |
| 1556 | * companion to markwaiting (above) and is used to coordinate | |
| 1557 | * multiple vaps scanning. | |
| 1558 | * This is called from the state taskqueue. | |
| 1559 | */ | |
| 1560 | static void | |
| 1561 | wakeupwaiting(struct ieee80211vap *vap0) | |
| 1562 | { | |
| 1563 | struct ieee80211com *ic = vap0->iv_ic; | |
| 1564 | struct ieee80211vap *vap; | |
| 1565 | ||
| 32176cfd RP |
1566 | /* |
| 1567 | * A vap list entry can not disappear since we are running on the | |
| 1568 | * taskqueue and a vap destroy will queue and drain another state | |
| 1569 | * change task. | |
| 1570 | */ | |
| 1571 | TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { | |
| 1572 | if (vap == vap0) | |
| 1573 | continue; | |
| 1574 | if (vap->iv_flags_ext & IEEE80211_FEXT_SCANWAIT) { | |
| 1575 | vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; | |
| 1576 | /* NB: sta's cannot go INIT->RUN */ | |
| 1577 | /* NB: iv_newstate may drop the lock */ | |
| 1578 | vap->iv_newstate(vap, | |
| 1579 | vap->iv_opmode == IEEE80211_M_STA ? | |
| 1580 | IEEE80211_S_SCAN : IEEE80211_S_RUN, 0); | |
| 846cf0bc | 1581 | } |
| 846cf0bc | 1582 | } |
| 32176cfd RP |
1583 | } |
| 1584 | ||
| 1585 | /* | |
| 1586 | * Handle post state change work common to all operating modes. | |
| 1587 | */ | |
| 1588 | static void | |
| 47156d48 | 1589 | ieee80211_newstate_task(void *xvap, int npending) |
| 32176cfd RP |
1590 | { |
| 1591 | struct ieee80211vap *vap = xvap; | |
| 47156d48 | 1592 | struct ieee80211com *ic; |
| 32176cfd RP |
1593 | enum ieee80211_state nstate, ostate; |
| 1594 | int arg, rc; | |
| 841ab66c | 1595 | |
| 47156d48 MD |
1596 | wlan_serialize_enter(); |
| 1597 | ||
| 1598 | ic = vap->iv_ic; | |
| 32176cfd RP |
1599 | nstate = vap->iv_nstate; |
| 1600 | arg = vap->iv_nstate_arg; | |
| 1601 | ||
| 1602 | if (vap->iv_flags_ext & IEEE80211_FEXT_REINIT) { | |
| 1603 | /* | |
| 1604 | * We have been requested to drop back to the INIT before | |
| 1605 | * proceeding to the new state. | |
| 1606 | */ | |
| 1607 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1608 | "%s: %s -> %s arg %d\n", __func__, | |
| 1609 | ieee80211_state_name[vap->iv_state], | |
| 1610 | ieee80211_state_name[IEEE80211_S_INIT], arg); | |
| 1611 | vap->iv_newstate(vap, IEEE80211_S_INIT, arg); | |
| 1612 | vap->iv_flags_ext &= ~IEEE80211_FEXT_REINIT; | |
| 1613 | } | |
| 1614 | ||
| 1615 | ostate = vap->iv_state; | |
| 1616 | if (nstate == IEEE80211_S_SCAN && ostate != IEEE80211_S_INIT) { | |
| 1617 | /* | |
| 1618 | * SCAN was forced; e.g. on beacon miss. Force other running | |
| 1619 | * vap's to INIT state and mark them as waiting for the scan to | |
| 1620 | * complete. This insures they don't interfere with our | |
| 1621 | * scanning. Since we are single threaded the vaps can not | |
| 1622 | * transition again while we are executing. | |
| 1623 | * | |
| 1624 | * XXX not always right, assumes ap follows sta | |
| 1625 | */ | |
| 1626 | markwaiting(vap); | |
| 1627 | } | |
| 1628 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1629 | "%s: %s -> %s arg %d\n", __func__, | |
| 1630 | ieee80211_state_name[ostate], ieee80211_state_name[nstate], arg); | |
| 1631 | ||
| 1632 | rc = vap->iv_newstate(vap, nstate, arg); | |
| 1633 | vap->iv_flags_ext &= ~IEEE80211_FEXT_STATEWAIT; | |
| 1634 | if (rc != 0) { | |
| 1635 | /* State transition failed */ | |
| 1636 | KASSERT(rc != EINPROGRESS, ("iv_newstate was deferred")); | |
| 1637 | KASSERT(nstate != IEEE80211_S_INIT, | |
| 1638 | ("INIT state change failed")); | |
| 1639 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1640 | "%s: %s returned error %d\n", __func__, | |
| 1641 | ieee80211_state_name[nstate], rc); | |
| 1642 | goto done; | |
| 1643 | } | |
| 1644 | ||
| 1645 | /* No actual transition, skip post processing */ | |
| 1646 | if (ostate == nstate) | |
| 1647 | goto done; | |
| 1648 | ||
| 1649 | if (nstate == IEEE80211_S_RUN) { | |
| f0a26983 SZ |
1650 | struct ifaltq_subque *ifsq = |
| 1651 | ifq_get_subq_default(&vap->iv_ifp->if_snd); | |
| 1652 | ||
| 32176cfd RP |
1653 | /* |
| 1654 | * OACTIVE may be set on the vap if the upper layer | |
| 1655 | * tried to transmit (e.g. IPv6 NDP) before we reach | |
| 1656 | * RUN state. Clear it and restart xmit. | |
| 1657 | * | |
| 1658 | * Note this can also happen as a result of SLEEP->RUN | |
| 1659 | * (i.e. coming out of power save mode). | |
| 1660 | */ | |
| f0a26983 SZ |
1661 | ifsq_clr_oactive(ifsq); |
| 1662 | vap->iv_ifp->if_start(vap->iv_ifp, ifsq); | |
| 32176cfd RP |
1663 | |
| 1664 | /* bring up any vaps waiting on us */ | |
| 1665 | wakeupwaiting(vap); | |
| 1666 | } else if (nstate == IEEE80211_S_INIT) { | |
| 1667 | /* | |
| 1668 | * Flush the scan cache if we did the last scan (XXX?) | |
| 1669 | * and flush any frames on send queues from this vap. | |
| 1670 | * Note the mgt q is used only for legacy drivers and | |
| 1671 | * will go away shortly. | |
| 1672 | */ | |
| 1673 | ieee80211_scan_flush(vap); | |
| 1674 | ||
| 1675 | /* XXX NB: cast for altq */ | |
| c332e0e8 | 1676 | ieee80211_flush_ifq(&ic->ic_ifp->if_snd, vap); |
| 32176cfd RP |
1677 | } |
| 1678 | done: | |
| 47156d48 | 1679 | wlan_serialize_exit(); |
| 841ab66c SZ |
1680 | } |
| 1681 | ||
| 32176cfd RP |
1682 | /* |
| 1683 | * Public interface for initiating a state machine change. | |
| 1684 | * This routine single-threads the request and coordinates | |
| 1685 | * the scheduling of multiple vaps for the purpose of selecting | |
| 1686 | * an operating channel. Specifically the following scenarios | |
| 1687 | * are handled: | |
| 1688 | * o only one vap can be selecting a channel so on transition to | |
| 1689 | * SCAN state if another vap is already scanning then | |
| 1690 | * mark the caller for later processing and return without | |
| 1691 | * doing anything (XXX? expectations by caller of synchronous operation) | |
| 1692 | * o only one vap can be doing CAC of a channel so on transition to | |
| 1693 | * CAC state if another vap is already scanning for radar then | |
| 1694 | * mark the caller for later processing and return without | |
| 1695 | * doing anything (XXX? expectations by caller of synchronous operation) | |
| 1696 | * o if another vap is already running when a request is made | |
| 1697 | * to SCAN then an operating channel has been chosen; bypass | |
| 1698 | * the scan and just join the channel | |
| 1699 | * | |
| 1700 | * Note that the state change call is done through the iv_newstate | |
| 1701 | * method pointer so any driver routine gets invoked. The driver | |
| 1702 | * will normally call back into operating mode-specific | |
| 1703 | * ieee80211_newstate routines (below) unless it needs to completely | |
| 1704 | * bypass the state machine (e.g. because the firmware has it's | |
| 1705 | * own idea how things should work). Bypassing the net80211 layer | |
| 1706 | * is usually a mistake and indicates lack of proper integration | |
| 1707 | * with the net80211 layer. | |
| 1708 | */ | |
| f186073c | 1709 | static int |
| 32176cfd RP |
1710 | ieee80211_new_state_locked(struct ieee80211vap *vap, |
| 1711 | enum ieee80211_state nstate, int arg) | |
| f186073c | 1712 | { |
| 32176cfd RP |
1713 | struct ieee80211com *ic = vap->iv_ic; |
| 1714 | struct ieee80211vap *vp; | |
| f186073c | 1715 | enum ieee80211_state ostate; |
| 32176cfd | 1716 | int nrunning, nscanning; |
| f186073c | 1717 | |
| 32176cfd RP |
1718 | if (vap->iv_flags_ext & IEEE80211_FEXT_STATEWAIT) { |
| 1719 | if (vap->iv_nstate == IEEE80211_S_INIT) { | |
| 841ab66c | 1720 | /* |
| 32176cfd RP |
1721 | * XXX The vap is being stopped, do no allow any other |
| 1722 | * state changes until this is completed. | |
| 841ab66c | 1723 | */ |
| 32176cfd RP |
1724 | return -1; |
| 1725 | } else if (vap->iv_state != vap->iv_nstate) { | |
| 1726 | #if 0 | |
| 1727 | /* Warn if the previous state hasn't completed. */ | |
| 1728 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1729 | "%s: pending %s -> %s transition lost\n", __func__, | |
| 1730 | ieee80211_state_name[vap->iv_state], | |
| 1731 | ieee80211_state_name[vap->iv_nstate]); | |
| 1732 | #else | |
| 1733 | /* XXX temporarily enable to identify issues */ | |
| 1734 | if_printf(vap->iv_ifp, | |
| 1735 | "%s: pending %s -> %s transition lost\n", | |
| 1736 | __func__, ieee80211_state_name[vap->iv_state], | |
| 1737 | ieee80211_state_name[vap->iv_nstate]); | |
| 1738 | #endif | |
| f186073c | 1739 | } |
| 32176cfd RP |
1740 | } |
| 1741 | ||
| 1742 | nrunning = nscanning = 0; | |
| 1743 | /* XXX can track this state instead of calculating */ | |
| 1744 | TAILQ_FOREACH(vp, &ic->ic_vaps, iv_next) { | |
| 1745 | if (vp != vap) { | |
| 1746 | if (vp->iv_state >= IEEE80211_S_RUN) | |
| 1747 | nrunning++; | |
| 1748 | /* XXX doesn't handle bg scan */ | |
| 1749 | /* NB: CAC+AUTH+ASSOC treated like SCAN */ | |
| 1750 | else if (vp->iv_state > IEEE80211_S_INIT) | |
| 1751 | nscanning++; | |
| 1752 | } | |
| 1753 | } | |
| 1754 | ostate = vap->iv_state; | |
| 1755 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1756 | "%s: %s -> %s (nrunning %d nscanning %d)\n", __func__, | |
| 1757 | ieee80211_state_name[ostate], ieee80211_state_name[nstate], | |
| 1758 | nrunning, nscanning); | |
| 1759 | switch (nstate) { | |
| 1760 | case IEEE80211_S_SCAN: | |
| 1761 | if (ostate == IEEE80211_S_INIT) { | |
| 1762 | /* | |
| 1763 | * INIT -> SCAN happens on initial bringup. | |
| 1764 | */ | |
| 1765 | KASSERT(!(nscanning && nrunning), | |
| 1766 | ("%d scanning and %d running", nscanning, nrunning)); | |
| 1767 | if (nscanning) { | |
| 1768 | /* | |
| 1769 | * Someone is scanning, defer our state | |
| 1770 | * change until the work has completed. | |
| 1771 | */ | |
| 1772 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1773 | "%s: defer %s -> %s\n", | |
| 1774 | __func__, ieee80211_state_name[ostate], | |
| 1775 | ieee80211_state_name[nstate]); | |
| 1776 | vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; | |
| 1777 | return 0; | |
| f186073c | 1778 | } |
| 32176cfd RP |
1779 | if (nrunning) { |
| 1780 | /* | |
| 1781 | * Someone is operating; just join the channel | |
| 1782 | * they have chosen. | |
| 1783 | */ | |
| 1784 | /* XXX kill arg? */ | |
| 1785 | /* XXX check each opmode, adhoc? */ | |
| 1786 | if (vap->iv_opmode == IEEE80211_M_STA) | |
| 1787 | nstate = IEEE80211_S_SCAN; | |
| 1788 | else | |
| 1789 | nstate = IEEE80211_S_RUN; | |
| 1790 | #ifdef IEEE80211_DEBUG | |
| 1791 | if (nstate != IEEE80211_S_SCAN) { | |
| 1792 | IEEE80211_DPRINTF(vap, | |
| 1793 | IEEE80211_MSG_STATE, | |
| 1794 | "%s: override, now %s -> %s\n", | |
| 1795 | __func__, | |
| 1796 | ieee80211_state_name[ostate], | |
| 1797 | ieee80211_state_name[nstate]); | |
| 841ab66c | 1798 | } |
| 32176cfd | 1799 | #endif |
| 841ab66c | 1800 | } |
| f186073c JS |
1801 | } |
| 1802 | break; | |
| 1803 | case IEEE80211_S_RUN: | |
| 32176cfd RP |
1804 | if (vap->iv_opmode == IEEE80211_M_WDS && |
| 1805 | (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) && | |
| 1806 | nscanning) { | |
| 1807 | /* | |
| 1808 | * Legacy WDS with someone else scanning; don't | |
| 1809 | * go online until that completes as we should | |
| 1810 | * follow the other vap to the channel they choose. | |
| 1811 | */ | |
| 1812 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1813 | "%s: defer %s -> %s (legacy WDS)\n", __func__, | |
| 1814 | ieee80211_state_name[ostate], | |
| 1815 | ieee80211_state_name[nstate]); | |
| 1816 | vap->iv_flags_ext |= IEEE80211_FEXT_SCANWAIT; | |
| 1817 | return 0; | |
| f186073c | 1818 | } |
| 32176cfd RP |
1819 | if (vap->iv_opmode == IEEE80211_M_HOSTAP && |
| 1820 | IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) && | |
| 1821 | (vap->iv_flags_ext & IEEE80211_FEXT_DFS) && | |
| 1822 | !IEEE80211_IS_CHAN_CACDONE(ic->ic_bsschan)) { | |
| 841ab66c | 1823 | /* |
| 32176cfd RP |
1824 | * This is a DFS channel, transition to CAC state |
| 1825 | * instead of RUN. This allows us to initiate | |
| 1826 | * Channel Availability Check (CAC) as specified | |
| 1827 | * by 11h/DFS. | |
| 841ab66c | 1828 | */ |
| 32176cfd RP |
1829 | nstate = IEEE80211_S_CAC; |
| 1830 | IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, | |
| 1831 | "%s: override %s -> %s (DFS)\n", __func__, | |
| 1832 | ieee80211_state_name[ostate], | |
| 1833 | ieee80211_state_name[nstate]); | |
| 841ab66c | 1834 | } |
| 32176cfd RP |
1835 | break; |
| 1836 | case IEEE80211_S_INIT: | |
| 1837 | /* cancel any scan in progress */ | |
| 1838 | ieee80211_cancel_scan(vap); | |
| 1839 | if (ostate == IEEE80211_S_INIT ) { | |
| 1840 | /* XXX don't believe this */ | |
| 1841 | /* INIT -> INIT. nothing to do */ | |
| 1842 | vap->iv_flags_ext &= ~IEEE80211_FEXT_SCANWAIT; | |
| 841ab66c | 1843 | } |
| 32176cfd RP |
1844 | /* fall thru... */ |
| 1845 | default: | |
| f186073c JS |
1846 | break; |
| 1847 | } | |
| 32176cfd RP |
1848 | /* defer the state change to a thread */ |
| 1849 | vap->iv_nstate = nstate; | |
| 1850 | vap->iv_nstate_arg = arg; | |
| 1851 | vap->iv_flags_ext |= IEEE80211_FEXT_STATEWAIT; | |
| 1852 | ieee80211_runtask(ic, &vap->iv_nstate_task); | |
| 1853 | return EINPROGRESS; | |
| 1854 | } | |
| 1855 | ||
| 1856 | int | |
| 1857 | ieee80211_new_state(struct ieee80211vap *vap, | |
| 1858 | enum ieee80211_state nstate, int arg) | |
| 1859 | { | |
| 32176cfd RP |
1860 | int rc; |
| 1861 | ||
| 32176cfd | 1862 | rc = ieee80211_new_state_locked(vap, nstate, arg); |
| 32176cfd | 1863 | return rc; |
| f186073c | 1864 | } |