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