diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index 41b743c988f4..dcd61e8de43a 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -1,2649 +1,2649 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include /* * IEEE 802.11 generic handler */ #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_SUPERG #include #endif #include #include #include const char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = { [IEEE80211_MODE_AUTO] = "auto", [IEEE80211_MODE_11A] = "11a", [IEEE80211_MODE_11B] = "11b", [IEEE80211_MODE_11G] = "11g", [IEEE80211_MODE_FH] = "FH", [IEEE80211_MODE_TURBO_A] = "turboA", [IEEE80211_MODE_TURBO_G] = "turboG", [IEEE80211_MODE_STURBO_A] = "sturboA", [IEEE80211_MODE_HALF] = "half", [IEEE80211_MODE_QUARTER] = "quarter", [IEEE80211_MODE_11NA] = "11na", [IEEE80211_MODE_11NG] = "11ng", [IEEE80211_MODE_VHT_2GHZ] = "11acg", [IEEE80211_MODE_VHT_5GHZ] = "11ac", }; /* map ieee80211_opmode to the corresponding capability bit */ const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = { [IEEE80211_M_IBSS] = IEEE80211_C_IBSS, [IEEE80211_M_WDS] = IEEE80211_C_WDS, [IEEE80211_M_STA] = IEEE80211_C_STA, [IEEE80211_M_AHDEMO] = IEEE80211_C_AHDEMO, [IEEE80211_M_HOSTAP] = IEEE80211_C_HOSTAP, [IEEE80211_M_MONITOR] = IEEE80211_C_MONITOR, #ifdef IEEE80211_SUPPORT_MESH [IEEE80211_M_MBSS] = IEEE80211_C_MBSS, #endif }; const uint8_t ieee80211broadcastaddr[IEEE80211_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag); static void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag); static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag); static void ieee80211_syncflag_vht_locked(struct ieee80211com *ic, int flag); static int ieee80211_media_setup(struct ieee80211com *ic, struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat); static int media_status(enum ieee80211_opmode, const struct ieee80211_channel *); static uint64_t ieee80211_get_counter(struct ifnet *, ift_counter); MALLOC_DEFINE(M_80211_VAP, "80211vap", "802.11 vap state"); /* * Default supported rates for 802.11 operation (in IEEE .5Mb units). */ #define B(r) ((r) | IEEE80211_RATE_BASIC) static const struct ieee80211_rateset ieee80211_rateset_11a = { 8, { B(12), 18, B(24), 36, B(48), 72, 96, 108 } }; static const struct ieee80211_rateset ieee80211_rateset_half = { 8, { B(6), 9, B(12), 18, B(24), 36, 48, 54 } }; static const struct ieee80211_rateset ieee80211_rateset_quarter = { 8, { B(3), 4, B(6), 9, B(12), 18, 24, 27 } }; static const struct ieee80211_rateset ieee80211_rateset_11b = { 4, { B(2), B(4), B(11), B(22) } }; /* NB: OFDM rates are handled specially based on mode */ static const struct ieee80211_rateset ieee80211_rateset_11g = { 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } }; #undef B static int set_vht_extchan(struct ieee80211_channel *c); /* * Fill in 802.11 available channel set, mark * all available channels as active, and pick * a default channel if not already specified. */ void ieee80211_chan_init(struct ieee80211com *ic) { #define DEFAULTRATES(m, def) do { \ if (ic->ic_sup_rates[m].rs_nrates == 0) \ ic->ic_sup_rates[m] = def; \ } while (0) struct ieee80211_channel *c; int i; KASSERT(0 < ic->ic_nchans && ic->ic_nchans <= IEEE80211_CHAN_MAX, ("invalid number of channels specified: %u", ic->ic_nchans)); memset(ic->ic_chan_avail, 0, sizeof(ic->ic_chan_avail)); memset(ic->ic_modecaps, 0, sizeof(ic->ic_modecaps)); setbit(ic->ic_modecaps, IEEE80211_MODE_AUTO); for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; KASSERT(c->ic_flags != 0, ("channel with no flags")); /* * Help drivers that work only with frequencies by filling * in IEEE channel #'s if not already calculated. Note this * mimics similar work done in ieee80211_setregdomain when * changing regulatory state. */ if (c->ic_ieee == 0) c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); /* * Setup the HT40/VHT40 upper/lower bits. * The VHT80/... math is done elsewhere. */ if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0) c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq + (IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20), c->ic_flags); /* Update VHT math */ /* * XXX VHT again, note that this assumes VHT80/... channels * are legit already. */ set_vht_extchan(c); /* default max tx power to max regulatory */ if (c->ic_maxpower == 0) c->ic_maxpower = 2*c->ic_maxregpower; setbit(ic->ic_chan_avail, c->ic_ieee); /* * Identify mode capabilities. */ if (IEEE80211_IS_CHAN_A(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11A); if (IEEE80211_IS_CHAN_B(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11B); if (IEEE80211_IS_CHAN_ANYG(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11G); if (IEEE80211_IS_CHAN_FHSS(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_FH); if (IEEE80211_IS_CHAN_108A(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_A); if (IEEE80211_IS_CHAN_108G(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_TURBO_G); if (IEEE80211_IS_CHAN_ST(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_STURBO_A); if (IEEE80211_IS_CHAN_HALF(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_HALF); if (IEEE80211_IS_CHAN_QUARTER(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_QUARTER); if (IEEE80211_IS_CHAN_HTA(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11NA); if (IEEE80211_IS_CHAN_HTG(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_11NG); if (IEEE80211_IS_CHAN_VHTA(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_VHT_5GHZ); if (IEEE80211_IS_CHAN_VHTG(c)) setbit(ic->ic_modecaps, IEEE80211_MODE_VHT_2GHZ); } /* initialize candidate channels to all available */ memcpy(ic->ic_chan_active, ic->ic_chan_avail, sizeof(ic->ic_chan_avail)); /* sort channel table to allow lookup optimizations */ ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); /* invalidate any previous state */ ic->ic_bsschan = IEEE80211_CHAN_ANYC; ic->ic_prevchan = NULL; ic->ic_csa_newchan = NULL; /* arbitrarily pick the first channel */ ic->ic_curchan = &ic->ic_channels[0]; ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); /* fillin well-known rate sets if driver has not specified */ DEFAULTRATES(IEEE80211_MODE_11B, ieee80211_rateset_11b); DEFAULTRATES(IEEE80211_MODE_11G, ieee80211_rateset_11g); DEFAULTRATES(IEEE80211_MODE_11A, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_TURBO_A, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_TURBO_G, ieee80211_rateset_11g); DEFAULTRATES(IEEE80211_MODE_STURBO_A, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_HALF, ieee80211_rateset_half); DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter); DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a); DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g); DEFAULTRATES(IEEE80211_MODE_VHT_2GHZ, ieee80211_rateset_11g); DEFAULTRATES(IEEE80211_MODE_VHT_5GHZ, ieee80211_rateset_11a); /* * Setup required information to fill the mcsset field, if driver did * not. Assume a 2T2R setup for historic reasons. */ if (ic->ic_rxstream == 0) ic->ic_rxstream = 2; if (ic->ic_txstream == 0) ic->ic_txstream = 2; ieee80211_init_suphtrates(ic); /* * Set auto mode to reset active channel state and any desired channel. */ (void) ieee80211_setmode(ic, IEEE80211_MODE_AUTO); #undef DEFAULTRATES } static void null_update_mcast(struct ieee80211com *ic) { ic_printf(ic, "need multicast update callback\n"); } static void null_update_promisc(struct ieee80211com *ic) { ic_printf(ic, "need promiscuous mode update callback\n"); } static void null_update_chw(struct ieee80211com *ic) { ic_printf(ic, "%s: need callback\n", __func__); } int ic_printf(struct ieee80211com *ic, const char * fmt, ...) { va_list ap; int retval; retval = printf("%s: ", ic->ic_name); va_start(ap, fmt); retval += vprintf(fmt, ap); va_end(ap); return (retval); } static LIST_HEAD(, ieee80211com) ic_head = LIST_HEAD_INITIALIZER(ic_head); static struct mtx ic_list_mtx; MTX_SYSINIT(ic_list, &ic_list_mtx, "ieee80211com list", MTX_DEF); static int sysctl_ieee80211coms(SYSCTL_HANDLER_ARGS) { struct ieee80211com *ic; struct sbuf sb; char *sp; int error; error = sysctl_wire_old_buffer(req, 0); if (error) return (error); sbuf_new_for_sysctl(&sb, NULL, 8, req); sbuf_clear_flags(&sb, SBUF_INCLUDENUL); sp = ""; mtx_lock(&ic_list_mtx); LIST_FOREACH(ic, &ic_head, ic_next) { sbuf_printf(&sb, "%s%s", sp, ic->ic_name); sp = " "; } mtx_unlock(&ic_list_mtx); error = sbuf_finish(&sb); sbuf_delete(&sb); return (error); } SYSCTL_PROC(_net_wlan, OID_AUTO, devices, CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, 0, sysctl_ieee80211coms, "A", "names of available 802.11 devices"); /* * Attach/setup the common net80211 state. Called by * the driver on attach to prior to creating any vap's. */ void ieee80211_ifattach(struct ieee80211com *ic) { IEEE80211_LOCK_INIT(ic, ic->ic_name); IEEE80211_TX_LOCK_INIT(ic, ic->ic_name); TAILQ_INIT(&ic->ic_vaps); /* Create a taskqueue for all state changes */ ic->ic_tq = taskqueue_create("ic_taskq", IEEE80211_M_WAITOK | IEEE80211_M_ZERO, taskqueue_thread_enqueue, &ic->ic_tq); taskqueue_start_threads(&ic->ic_tq, 1, PI_NET, "%s net80211 taskq", ic->ic_name); ic->ic_ierrors = counter_u64_alloc(IEEE80211_M_WAITOK); ic->ic_oerrors = counter_u64_alloc(IEEE80211_M_WAITOK); /* * Fill in 802.11 available channel set, mark all * available channels as active, and pick a default * channel if not already specified. */ ieee80211_chan_init(ic); ic->ic_update_mcast = null_update_mcast; ic->ic_update_promisc = null_update_promisc; ic->ic_update_chw = null_update_chw; ic->ic_hash_key = arc4random(); ic->ic_bintval = IEEE80211_BINTVAL_DEFAULT; ic->ic_lintval = ic->ic_bintval; ic->ic_txpowlimit = IEEE80211_TXPOWER_MAX; ieee80211_crypto_attach(ic); ieee80211_node_attach(ic); ieee80211_power_attach(ic); ieee80211_proto_attach(ic); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_attach(ic); #endif ieee80211_ht_attach(ic); ieee80211_vht_attach(ic); ieee80211_scan_attach(ic); ieee80211_regdomain_attach(ic); ieee80211_dfs_attach(ic); ieee80211_sysctl_attach(ic); mtx_lock(&ic_list_mtx); LIST_INSERT_HEAD(&ic_head, ic, ic_next); mtx_unlock(&ic_list_mtx); } /* * Detach net80211 state on device detach. Tear down * all vap's and reclaim all common state prior to the * device state going away. Note we may call back into * driver; it must be prepared for this. */ void ieee80211_ifdetach(struct ieee80211com *ic) { struct ieee80211vap *vap; /* * We use this as an indicator that ifattach never had a chance to be * called, e.g. early driver attach failed and ifdetach was called * during subsequent detach. Never fear, for we have nothing to do * here. */ if (ic->ic_tq == NULL) return; mtx_lock(&ic_list_mtx); LIST_REMOVE(ic, ic_next); mtx_unlock(&ic_list_mtx); taskqueue_drain(taskqueue_thread, &ic->ic_restart_task); /* * The VAP is responsible for setting and clearing * the VIMAGE context. */ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) { ieee80211_com_vdetach(vap); ieee80211_vap_destroy(vap); } ieee80211_waitfor_parent(ic); ieee80211_sysctl_detach(ic); ieee80211_dfs_detach(ic); ieee80211_regdomain_detach(ic); ieee80211_scan_detach(ic); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_detach(ic); #endif ieee80211_vht_detach(ic); ieee80211_ht_detach(ic); /* NB: must be called before ieee80211_node_detach */ ieee80211_proto_detach(ic); ieee80211_crypto_detach(ic); ieee80211_power_detach(ic); ieee80211_node_detach(ic); counter_u64_free(ic->ic_ierrors); counter_u64_free(ic->ic_oerrors); taskqueue_free(ic->ic_tq); IEEE80211_TX_LOCK_DESTROY(ic); IEEE80211_LOCK_DESTROY(ic); } struct ieee80211com * ieee80211_find_com(const char *name) { struct ieee80211com *ic; mtx_lock(&ic_list_mtx); LIST_FOREACH(ic, &ic_head, ic_next) if (strcmp(ic->ic_name, name) == 0) break; mtx_unlock(&ic_list_mtx); return (ic); } void ieee80211_iterate_coms(ieee80211_com_iter_func *f, void *arg) { struct ieee80211com *ic; mtx_lock(&ic_list_mtx); LIST_FOREACH(ic, &ic_head, ic_next) (*f)(arg, ic); mtx_unlock(&ic_list_mtx); } /* * Default reset method for use with the ioctl support. This * method is invoked after any state change in the 802.11 * layer that should be propagated to the hardware but not * require re-initialization of the 802.11 state machine (e.g * rescanning for an ap). We always return ENETRESET which * should cause the driver to re-initialize the device. Drivers * can override this method to implement more optimized support. */ static int default_reset(struct ieee80211vap *vap, u_long cmd) { return ENETRESET; } /* * Default for updating the VAP default TX key index. * * Drivers that support TX offload as well as hardware encryption offload * may need to be informed of key index changes separate from the key * update. */ static void default_update_deftxkey(struct ieee80211vap *vap, ieee80211_keyix kid) { /* XXX assert validity */ /* XXX assert we're in a key update block */ vap->iv_def_txkey = kid; } /* * Add underlying device errors to vap errors. */ static uint64_t ieee80211_get_counter(struct ifnet *ifp, ift_counter cnt) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; uint64_t rv; rv = if_get_counter_default(ifp, cnt); switch (cnt) { case IFCOUNTER_OERRORS: rv += counter_u64_fetch(ic->ic_oerrors); break; case IFCOUNTER_IERRORS: rv += counter_u64_fetch(ic->ic_ierrors); break; default: break; } return (rv); } /* * Prepare a vap for use. Drivers use this call to * setup net80211 state in new vap's prior attaching * them with ieee80211_vap_attach (below). */ int ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN]) { struct ifnet *ifp; ifp = if_alloc(IFT_ETHER); if (ifp == NULL) { ic_printf(ic, "%s: unable to allocate ifnet\n", __func__); return ENOMEM; } if_initname(ifp, name, unit); ifp->if_softc = vap; /* back pointer */ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; ifp->if_transmit = ieee80211_vap_transmit; ifp->if_qflush = ieee80211_vap_qflush; ifp->if_ioctl = ieee80211_ioctl; ifp->if_init = ieee80211_init; ifp->if_get_counter = ieee80211_get_counter; vap->iv_ifp = ifp; vap->iv_ic = ic; vap->iv_flags = ic->ic_flags; /* propagate common flags */ vap->iv_flags_ext = ic->ic_flags_ext; vap->iv_flags_ven = ic->ic_flags_ven; vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE; /* 11n capabilities - XXX methodize */ vap->iv_htcaps = ic->ic_htcaps; vap->iv_htextcaps = ic->ic_htextcaps; /* 11ac capabilities - XXX methodize */ - vap->iv_vhtcaps = ic->ic_vhtcaps; + vap->iv_vht_cap.vht_cap_info = ic->ic_vhtcaps; vap->iv_vhtextcaps = ic->ic_vhtextcaps; vap->iv_opmode = opmode; vap->iv_caps |= ieee80211_opcap[opmode]; IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr); switch (opmode) { case IEEE80211_M_WDS: /* * WDS links must specify the bssid of the far end. * For legacy operation this is a static relationship. * For non-legacy operation the station must associate * and be authorized to pass traffic. Plumbing the * vap to the proper node happens when the vap * transitions to RUN state. */ IEEE80211_ADDR_COPY(vap->iv_des_bssid, bssid); vap->iv_flags |= IEEE80211_F_DESBSSID; if (flags & IEEE80211_CLONE_WDSLEGACY) vap->iv_flags_ext |= IEEE80211_FEXT_WDSLEGACY; break; #ifdef IEEE80211_SUPPORT_TDMA case IEEE80211_M_AHDEMO: if (flags & IEEE80211_CLONE_TDMA) { /* NB: checked before clone operation allowed */ KASSERT(ic->ic_caps & IEEE80211_C_TDMA, ("not TDMA capable, ic_caps 0x%x", ic->ic_caps)); /* * Propagate TDMA capability to mark vap; this * cannot be removed and is used to distinguish * regular ahdemo operation from ahdemo+tdma. */ vap->iv_caps |= IEEE80211_C_TDMA; } break; #endif default: break; } /* auto-enable s/w beacon miss support */ if (flags & IEEE80211_CLONE_NOBEACONS) vap->iv_flags_ext |= IEEE80211_FEXT_SWBMISS; /* auto-generated or user supplied MAC address */ if (flags & (IEEE80211_CLONE_BSSID|IEEE80211_CLONE_MACADDR)) vap->iv_flags_ext |= IEEE80211_FEXT_UNIQMAC; /* * Enable various functionality by default if we're * capable; the driver can override us if it knows better. */ if (vap->iv_caps & IEEE80211_C_WME) vap->iv_flags |= IEEE80211_F_WME; if (vap->iv_caps & IEEE80211_C_BURST) vap->iv_flags |= IEEE80211_F_BURST; /* NB: bg scanning only makes sense for station mode right now */ if (vap->iv_opmode == IEEE80211_M_STA && (vap->iv_caps & IEEE80211_C_BGSCAN)) vap->iv_flags |= IEEE80211_F_BGSCAN; vap->iv_flags |= IEEE80211_F_DOTH; /* XXX no cap, just ena */ /* NB: DFS support only makes sense for ap mode right now */ if (vap->iv_opmode == IEEE80211_M_HOSTAP && (vap->iv_caps & IEEE80211_C_DFS)) vap->iv_flags_ext |= IEEE80211_FEXT_DFS; /* NB: only flip on U-APSD for hostap/sta for now */ if ((vap->iv_opmode == IEEE80211_M_STA) || (vap->iv_opmode == IEEE80211_M_HOSTAP)) { if (vap->iv_caps & IEEE80211_C_UAPSD) vap->iv_flags_ext |= IEEE80211_FEXT_UAPSD; } vap->iv_des_chan = IEEE80211_CHAN_ANYC; /* any channel is ok */ vap->iv_bmissthreshold = IEEE80211_HWBMISS_DEFAULT; vap->iv_dtim_period = IEEE80211_DTIM_DEFAULT; /* * Install a default reset method for the ioctl support; * the driver can override this. */ vap->iv_reset = default_reset; /* * Install a default crypto key update method, the driver * can override this. */ vap->iv_update_deftxkey = default_update_deftxkey; ieee80211_sysctl_vattach(vap); ieee80211_crypto_vattach(vap); ieee80211_node_vattach(vap); ieee80211_power_vattach(vap); ieee80211_proto_vattach(vap); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_vattach(vap); #endif ieee80211_ht_vattach(vap); ieee80211_vht_vattach(vap); ieee80211_scan_vattach(vap); ieee80211_regdomain_vattach(vap); ieee80211_radiotap_vattach(vap); ieee80211_vap_reset_erp(vap); ieee80211_ratectl_set(vap, IEEE80211_RATECTL_NONE); return 0; } /* * Activate a vap. State should have been prepared with a * call to ieee80211_vap_setup and by the driver. On return * from this call the vap is ready for use. */ int ieee80211_vap_attach(struct ieee80211vap *vap, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat, const uint8_t macaddr[IEEE80211_ADDR_LEN]) { struct ifnet *ifp = vap->iv_ifp; struct ieee80211com *ic = vap->iv_ic; struct ifmediareq imr; int maxrate; IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s flags 0x%x flags_ext 0x%x\n", __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_name, vap->iv_flags, vap->iv_flags_ext); /* * Do late attach work that cannot happen until after * the driver has had a chance to override defaults. */ ieee80211_node_latevattach(vap); ieee80211_power_latevattach(vap); maxrate = ieee80211_media_setup(ic, &vap->iv_media, vap->iv_caps, vap->iv_opmode == IEEE80211_M_STA, media_change, media_stat); ieee80211_media_status(ifp, &imr); /* NB: strip explicit mode; we're actually in autoselect */ ifmedia_set(&vap->iv_media, imr.ifm_active &~ (IFM_MMASK | IFM_IEEE80211_TURBO)); if (maxrate) ifp->if_baudrate = IF_Mbps(maxrate); ether_ifattach(ifp, macaddr); IEEE80211_ADDR_COPY(vap->iv_myaddr, IF_LLADDR(ifp)); /* hook output method setup by ether_ifattach */ vap->iv_output = ifp->if_output; ifp->if_output = ieee80211_output; /* NB: if_mtu set by ether_ifattach to ETHERMTU */ IEEE80211_LOCK(ic); TAILQ_INSERT_TAIL(&ic->ic_vaps, vap, iv_next); ieee80211_syncflag_locked(ic, IEEE80211_F_WME); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); #endif ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_VHT); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT40); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80); IEEE80211_UNLOCK(ic); return 1; } /* * Tear down vap state and reclaim the ifnet. * The driver is assumed to have prepared for * this; e.g. by turning off interrupts for the * underlying device. */ void ieee80211_vap_detach(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; struct ifnet *ifp = vap->iv_ifp; CURVNET_SET(ifp->if_vnet); IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n", __func__, ieee80211_opmode_name[vap->iv_opmode], ic->ic_name); /* NB: bpfdetach is called by ether_ifdetach and claims all taps */ ether_ifdetach(ifp); ieee80211_stop(vap); /* * Flush any deferred vap tasks. */ ieee80211_draintask(ic, &vap->iv_nstate_task); ieee80211_draintask(ic, &vap->iv_swbmiss_task); ieee80211_draintask(ic, &vap->iv_wme_task); ieee80211_draintask(ic, &ic->ic_parent_task); /* XXX band-aid until ifnet handles this for us */ taskqueue_drain(taskqueue_swi, &ifp->if_linktask); IEEE80211_LOCK(ic); KASSERT(vap->iv_state == IEEE80211_S_INIT , ("vap still running")); TAILQ_REMOVE(&ic->ic_vaps, vap, iv_next); ieee80211_syncflag_locked(ic, IEEE80211_F_WME); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_syncflag_locked(ic, IEEE80211_F_TURBOP); #endif ieee80211_syncflag_locked(ic, IEEE80211_F_PCF); ieee80211_syncflag_locked(ic, IEEE80211_F_BURST); ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT); ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_VHT); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT40); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT160); ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_USEVHT80P80); /* NB: this handles the bpfdetach done below */ ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF); if (vap->iv_ifflags & IFF_PROMISC) ieee80211_promisc(vap, false); if (vap->iv_ifflags & IFF_ALLMULTI) ieee80211_allmulti(vap, false); IEEE80211_UNLOCK(ic); ifmedia_removeall(&vap->iv_media); ieee80211_radiotap_vdetach(vap); ieee80211_regdomain_vdetach(vap); ieee80211_scan_vdetach(vap); #ifdef IEEE80211_SUPPORT_SUPERG ieee80211_superg_vdetach(vap); #endif ieee80211_vht_vdetach(vap); ieee80211_ht_vdetach(vap); /* NB: must be before ieee80211_node_vdetach */ ieee80211_proto_vdetach(vap); ieee80211_crypto_vdetach(vap); ieee80211_power_vdetach(vap); ieee80211_node_vdetach(vap); ieee80211_sysctl_vdetach(vap); if_free(ifp); CURVNET_RESTORE(); } /* * Count number of vaps in promisc, and issue promisc on * parent respectively. */ void ieee80211_promisc(struct ieee80211vap *vap, bool on) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK_ASSERT(ic); if (on) { if (++ic->ic_promisc == 1) ieee80211_runtask(ic, &ic->ic_promisc_task); } else { KASSERT(ic->ic_promisc > 0, ("%s: ic %p not promisc", __func__, ic)); if (--ic->ic_promisc == 0) ieee80211_runtask(ic, &ic->ic_promisc_task); } } /* * Count number of vaps in allmulti, and issue allmulti on * parent respectively. */ void ieee80211_allmulti(struct ieee80211vap *vap, bool on) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK_ASSERT(ic); if (on) { if (++ic->ic_allmulti == 1) ieee80211_runtask(ic, &ic->ic_mcast_task); } else { KASSERT(ic->ic_allmulti > 0, ("%s: ic %p not allmulti", __func__, ic)); if (--ic->ic_allmulti == 0) ieee80211_runtask(ic, &ic->ic_mcast_task); } } /* * Synchronize flag bit state in the com structure * according to the state of all vap's. This is used, * for example, to handle state changes via ioctls. */ static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag) { struct ieee80211vap *vap; int bit; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_flags & flag) { bit = 1; break; } if (bit) ic->ic_flags |= flag; else ic->ic_flags &= ~flag; } void ieee80211_syncflag(struct ieee80211vap *vap, int flag) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); if (flag < 0) { flag = -flag; vap->iv_flags &= ~flag; } else vap->iv_flags |= flag; ieee80211_syncflag_locked(ic, flag); IEEE80211_UNLOCK(ic); } /* * Synchronize flags_ht bit state in the com structure * according to the state of all vap's. This is used, * for example, to handle state changes via ioctls. */ static void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag) { struct ieee80211vap *vap; int bit; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_flags_ht & flag) { bit = 1; break; } if (bit) ic->ic_flags_ht |= flag; else ic->ic_flags_ht &= ~flag; } void ieee80211_syncflag_ht(struct ieee80211vap *vap, int flag) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); if (flag < 0) { flag = -flag; vap->iv_flags_ht &= ~flag; } else vap->iv_flags_ht |= flag; ieee80211_syncflag_ht_locked(ic, flag); IEEE80211_UNLOCK(ic); } /* * Synchronize flags_vht bit state in the com structure * according to the state of all vap's. This is used, * for example, to handle state changes via ioctls. */ static void ieee80211_syncflag_vht_locked(struct ieee80211com *ic, int flag) { struct ieee80211vap *vap; int bit; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_vht_flags & flag) { bit = 1; break; } if (bit) ic->ic_flags_vht |= flag; else ic->ic_flags_vht &= ~flag; } void ieee80211_syncflag_vht(struct ieee80211vap *vap, int flag) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); if (flag < 0) { flag = -flag; vap->iv_vht_flags &= ~flag; } else vap->iv_vht_flags |= flag; ieee80211_syncflag_vht_locked(ic, flag); IEEE80211_UNLOCK(ic); } /* * Synchronize flags_ext bit state in the com structure * according to the state of all vap's. This is used, * for example, to handle state changes via ioctls. */ static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag) { struct ieee80211vap *vap; int bit; IEEE80211_LOCK_ASSERT(ic); bit = 0; TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) if (vap->iv_flags_ext & flag) { bit = 1; break; } if (bit) ic->ic_flags_ext |= flag; else ic->ic_flags_ext &= ~flag; } void ieee80211_syncflag_ext(struct ieee80211vap *vap, int flag) { struct ieee80211com *ic = vap->iv_ic; IEEE80211_LOCK(ic); if (flag < 0) { flag = -flag; vap->iv_flags_ext &= ~flag; } else vap->iv_flags_ext |= flag; ieee80211_syncflag_ext_locked(ic, flag); IEEE80211_UNLOCK(ic); } static __inline int mapgsm(u_int freq, u_int flags) { freq *= 10; if (flags & IEEE80211_CHAN_QUARTER) freq += 5; else if (flags & IEEE80211_CHAN_HALF) freq += 10; else freq += 20; /* NB: there is no 907/20 wide but leave room */ return (freq - 906*10) / 5; } static __inline int mappsb(u_int freq, u_int flags) { return 37 + ((freq * 10) + ((freq % 5) == 2 ? 5 : 0) - 49400) / 5; } /* * Convert MHz frequency to IEEE channel number. */ int ieee80211_mhz2ieee(u_int freq, u_int flags) { #define IS_FREQ_IN_PSB(_freq) ((_freq) > 4940 && (_freq) < 4990) if (flags & IEEE80211_CHAN_GSM) return mapgsm(freq, flags); if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (freq == 2484) return 14; if (freq < 2484) return ((int) freq - 2407) / 5; else return 15 + ((freq - 2512) / 20); } else if (flags & IEEE80211_CHAN_5GHZ) { /* 5Ghz band */ if (freq <= 5000) { /* XXX check regdomain? */ if (IS_FREQ_IN_PSB(freq)) return mappsb(freq, flags); return (freq - 4000) / 5; } else return (freq - 5000) / 5; } else { /* either, guess */ if (freq == 2484) return 14; if (freq < 2484) { if (907 <= freq && freq <= 922) return mapgsm(freq, flags); return ((int) freq - 2407) / 5; } if (freq < 5000) { if (IS_FREQ_IN_PSB(freq)) return mappsb(freq, flags); else if (freq > 4900) return (freq - 4000) / 5; else return 15 + ((freq - 2512) / 20); } return (freq - 5000) / 5; } #undef IS_FREQ_IN_PSB } /* * Convert channel to IEEE channel number. */ int ieee80211_chan2ieee(struct ieee80211com *ic, const struct ieee80211_channel *c) { if (c == NULL) { ic_printf(ic, "invalid channel (NULL)\n"); return 0; /* XXX */ } return (c == IEEE80211_CHAN_ANYC ? IEEE80211_CHAN_ANY : c->ic_ieee); } /* * Convert IEEE channel number to MHz frequency. */ u_int ieee80211_ieee2mhz(u_int chan, u_int flags) { if (flags & IEEE80211_CHAN_GSM) return 907 + 5 * (chan / 10); if (flags & IEEE80211_CHAN_2GHZ) { /* 2GHz band */ if (chan == 14) return 2484; if (chan < 14) return 2407 + chan*5; else return 2512 + ((chan-15)*20); } else if (flags & IEEE80211_CHAN_5GHZ) {/* 5Ghz band */ if (flags & (IEEE80211_CHAN_HALF|IEEE80211_CHAN_QUARTER)) { chan -= 37; return 4940 + chan*5 + (chan % 5 ? 2 : 0); } return 5000 + (chan*5); } else { /* either, guess */ /* XXX can't distinguish PSB+GSM channels */ if (chan == 14) return 2484; if (chan < 14) /* 0-13 */ return 2407 + chan*5; if (chan < 27) /* 15-26 */ return 2512 + ((chan-15)*20); return 5000 + (chan*5); } } static __inline void set_extchan(struct ieee80211_channel *c) { /* * IEEE Std 802.11-2012, page 1738, subclause 20.3.15.4: * "the secondary channel number shall be 'N + [1,-1] * 4' */ if (c->ic_flags & IEEE80211_CHAN_HT40U) c->ic_extieee = c->ic_ieee + 4; else if (c->ic_flags & IEEE80211_CHAN_HT40D) c->ic_extieee = c->ic_ieee - 4; else c->ic_extieee = 0; } /* * Populate the freq1/freq2 fields as appropriate for VHT channels. * * This for now uses a hard-coded list of 80MHz wide channels. * * For HT20/HT40, freq1 just is the centre frequency of the 40MHz * wide channel we've already decided upon. * * For VHT80 and VHT160, there are only a small number of fixed * 80/160MHz wide channels, so we just use those. * * This is all likely very very wrong - both the regulatory code * and this code needs to ensure that all four channels are * available and valid before the VHT80 (and eight for VHT160) channel * is created. */ struct vht_chan_range { uint16_t freq_start; uint16_t freq_end; }; struct vht_chan_range vht80_chan_ranges[] = { { 5170, 5250 }, { 5250, 5330 }, { 5490, 5570 }, { 5570, 5650 }, { 5650, 5730 }, { 5735, 5815 }, { 0, 0 } }; struct vht_chan_range vht160_chan_ranges[] = { { 5170, 5330 }, { 5490, 5650 }, { 0, 0 } }; static int set_vht_extchan(struct ieee80211_channel *c) { int i; if (! IEEE80211_IS_CHAN_VHT(c)) return (0); if (IEEE80211_IS_CHAN_VHT80P80(c)) { printf("%s: TODO VHT80+80 channel (ieee=%d, flags=0x%08x)\n", __func__, c->ic_ieee, c->ic_flags); } if (IEEE80211_IS_CHAN_VHT160(c)) { for (i = 0; vht160_chan_ranges[i].freq_start != 0; i++) { if (c->ic_freq >= vht160_chan_ranges[i].freq_start && c->ic_freq < vht160_chan_ranges[i].freq_end) { int midpoint; midpoint = vht160_chan_ranges[i].freq_start + 80; c->ic_vht_ch_freq1 = ieee80211_mhz2ieee(midpoint, c->ic_flags); c->ic_vht_ch_freq2 = 0; #if 0 printf("%s: %d, freq=%d, midpoint=%d, freq1=%d, freq2=%d\n", __func__, c->ic_ieee, c->ic_freq, midpoint, c->ic_vht_ch_freq1, c->ic_vht_ch_freq2); #endif return (1); } } return (0); } if (IEEE80211_IS_CHAN_VHT80(c)) { for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) { if (c->ic_freq >= vht80_chan_ranges[i].freq_start && c->ic_freq < vht80_chan_ranges[i].freq_end) { int midpoint; midpoint = vht80_chan_ranges[i].freq_start + 40; c->ic_vht_ch_freq1 = ieee80211_mhz2ieee(midpoint, c->ic_flags); c->ic_vht_ch_freq2 = 0; #if 0 printf("%s: %d, freq=%d, midpoint=%d, freq1=%d, freq2=%d\n", __func__, c->ic_ieee, c->ic_freq, midpoint, c->ic_vht_ch_freq1, c->ic_vht_ch_freq2); #endif return (1); } } return (0); } if (IEEE80211_IS_CHAN_VHT40(c)) { if (IEEE80211_IS_CHAN_HT40U(c)) c->ic_vht_ch_freq1 = c->ic_ieee + 2; else if (IEEE80211_IS_CHAN_HT40D(c)) c->ic_vht_ch_freq1 = c->ic_ieee - 2; else return (0); return (1); } if (IEEE80211_IS_CHAN_VHT20(c)) { c->ic_vht_ch_freq1 = c->ic_ieee; return (1); } printf("%s: unknown VHT channel type (ieee=%d, flags=0x%08x)\n", __func__, c->ic_ieee, c->ic_flags); return (0); } /* * Return whether the current channel could possibly be a part of * a VHT80/VHT160 channel. * * This doesn't check that the whole range is in the allowed list * according to regulatory. */ static bool is_vht160_valid_freq(uint16_t freq) { int i; for (i = 0; vht160_chan_ranges[i].freq_start != 0; i++) { if (freq >= vht160_chan_ranges[i].freq_start && freq < vht160_chan_ranges[i].freq_end) return (true); } return (false); } static int is_vht80_valid_freq(uint16_t freq) { int i; for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) { if (freq >= vht80_chan_ranges[i].freq_start && freq < vht80_chan_ranges[i].freq_end) return (1); } return (0); } static int addchan(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags) { struct ieee80211_channel *c; if (*nchans >= maxchans) return (ENOBUFS); #if 0 printf("%s: %d of %d: ieee=%d, freq=%d, flags=0x%08x\n", __func__, *nchans, maxchans, ieee, freq, flags); #endif c = &chans[(*nchans)++]; c->ic_ieee = ieee; c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags); c->ic_maxregpower = maxregpower; c->ic_maxpower = 2 * maxregpower; c->ic_flags = flags; c->ic_vht_ch_freq1 = 0; c->ic_vht_ch_freq2 = 0; set_extchan(c); set_vht_extchan(c); return (0); } static int copychan_prev(struct ieee80211_channel chans[], int maxchans, int *nchans, uint32_t flags) { struct ieee80211_channel *c; KASSERT(*nchans > 0, ("channel list is empty\n")); if (*nchans >= maxchans) return (ENOBUFS); #if 0 printf("%s: %d of %d: flags=0x%08x\n", __func__, *nchans, maxchans, flags); #endif c = &chans[(*nchans)++]; c[0] = c[-1]; c->ic_flags = flags; c->ic_vht_ch_freq1 = 0; c->ic_vht_ch_freq2 = 0; set_extchan(c); set_vht_extchan(c); return (0); } /* * XXX VHT-2GHz */ static void getflags_2ghz(const uint8_t bands[], uint32_t flags[], int cbw_flags) { int nmodes; nmodes = 0; if (isset(bands, IEEE80211_MODE_11B)) flags[nmodes++] = IEEE80211_CHAN_B; if (isset(bands, IEEE80211_MODE_11G)) flags[nmodes++] = IEEE80211_CHAN_G; if (isset(bands, IEEE80211_MODE_11NG)) flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT20; if (cbw_flags & NET80211_CBW_FLAG_HT40) { flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40U; flags[nmodes++] = IEEE80211_CHAN_G | IEEE80211_CHAN_HT40D; } flags[nmodes] = 0; } static void getflags_5ghz(const uint8_t bands[], uint32_t flags[], int cbw_flags) { int nmodes; /* * The addchan_list() function seems to expect the flags array to * be in channel width order, so the VHT bits are interspersed * as appropriate to maintain said order. * * It also assumes HT40U is before HT40D. */ nmodes = 0; /* 20MHz */ if (isset(bands, IEEE80211_MODE_11A)) flags[nmodes++] = IEEE80211_CHAN_A; if (isset(bands, IEEE80211_MODE_11NA)) flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20; if (isset(bands, IEEE80211_MODE_VHT_5GHZ)) { flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 | IEEE80211_CHAN_VHT20; } /* 40MHz */ if (cbw_flags & NET80211_CBW_FLAG_HT40) flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U; if ((cbw_flags & NET80211_CBW_FLAG_HT40) && isset(bands, IEEE80211_MODE_VHT_5GHZ)) flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT40U; if (cbw_flags & NET80211_CBW_FLAG_HT40) flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D; if ((cbw_flags & NET80211_CBW_FLAG_HT40) && isset(bands, IEEE80211_MODE_VHT_5GHZ)) flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT40D; /* 80MHz */ if ((cbw_flags & NET80211_CBW_FLAG_VHT80) && isset(bands, IEEE80211_MODE_VHT_5GHZ)) { flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT80; flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT80; } /* VHT160 */ if ((cbw_flags & NET80211_CBW_FLAG_VHT160) && isset(bands, IEEE80211_MODE_VHT_5GHZ)) { flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT160; flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT160; } /* VHT80+80 */ if ((cbw_flags & NET80211_CBW_FLAG_VHT80P80) && isset(bands, IEEE80211_MODE_VHT_5GHZ)) { flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT80P80; flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT80P80; } flags[nmodes] = 0; } static void getflags(const uint8_t bands[], uint32_t flags[], int cbw_flags) { flags[0] = 0; if (isset(bands, IEEE80211_MODE_11A) || isset(bands, IEEE80211_MODE_11NA) || isset(bands, IEEE80211_MODE_VHT_5GHZ)) { if (isset(bands, IEEE80211_MODE_11B) || isset(bands, IEEE80211_MODE_11G) || isset(bands, IEEE80211_MODE_11NG) || isset(bands, IEEE80211_MODE_VHT_2GHZ)) return; getflags_5ghz(bands, flags, cbw_flags); } else getflags_2ghz(bands, flags, cbw_flags); } /* * Add one 20 MHz channel into specified channel list. * You MUST NOT mix bands when calling this. It will not add 5ghz * channels if you have any B/G/N band bit set. * The _cbw() variant does also support HT40/VHT80/160/80+80. */ int ieee80211_add_channel_cbw(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t chan_flags, const uint8_t bands[], int cbw_flags) { uint32_t flags[IEEE80211_MODE_MAX]; int i, error; getflags(bands, flags, cbw_flags); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower, flags[0] | chan_flags); for (i = 1; flags[i] != 0 && error == 0; i++) { error = copychan_prev(chans, maxchans, nchans, flags[i] | chan_flags); } return (error); } int ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t chan_flags, const uint8_t bands[]) { return (ieee80211_add_channel_cbw(chans, maxchans, nchans, ieee, freq, maxregpower, chan_flags, bands, 0)); } static struct ieee80211_channel * findchannel(struct ieee80211_channel chans[], int nchans, uint16_t freq, uint32_t flags) { struct ieee80211_channel *c; int i; flags &= IEEE80211_CHAN_ALLTURBO; /* brute force search */ for (i = 0; i < nchans; i++) { c = &chans[i]; if (c->ic_freq == freq && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; } return NULL; } /* * Add 40 MHz channel pair into specified channel list. */ /* XXX VHT */ int ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans, int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags) { struct ieee80211_channel *cent, *extc; uint16_t freq; int error; freq = ieee80211_ieee2mhz(ieee, flags); /* * Each entry defines an HT40 channel pair; find the * center channel, then the extension channel above. */ flags |= IEEE80211_CHAN_HT20; cent = findchannel(chans, *nchans, freq, flags); if (cent == NULL) return (EINVAL); extc = findchannel(chans, *nchans, freq + 20, flags); if (extc == NULL) return (ENOENT); flags &= ~IEEE80211_CHAN_HT; error = addchan(chans, maxchans, nchans, cent->ic_ieee, cent->ic_freq, maxregpower, flags | IEEE80211_CHAN_HT40U); if (error != 0) return (error); error = addchan(chans, maxchans, nchans, extc->ic_ieee, extc->ic_freq, maxregpower, flags | IEEE80211_CHAN_HT40D); return (error); } /* * Fetch the center frequency for the primary channel. */ uint32_t ieee80211_get_channel_center_freq(const struct ieee80211_channel *c) { return (c->ic_freq); } /* * Fetch the center frequency for the primary BAND channel. * * For 5, 10, 20MHz channels it'll be the normally configured channel * frequency. * * For 40MHz, 80MHz, 160MHz channels it will be the centre of the * wide channel, not the centre of the primary channel (that's ic_freq). * * For 80+80MHz channels this will be the centre of the primary * 80MHz channel; the secondary 80MHz channel will be center_freq2(). */ uint32_t ieee80211_get_channel_center_freq1(const struct ieee80211_channel *c) { /* * VHT - use the pre-calculated centre frequency * of the given channel. */ if (IEEE80211_IS_CHAN_VHT(c)) return (ieee80211_ieee2mhz(c->ic_vht_ch_freq1, c->ic_flags)); if (IEEE80211_IS_CHAN_HT40U(c)) { return (c->ic_freq + 10); } if (IEEE80211_IS_CHAN_HT40D(c)) { return (c->ic_freq - 10); } return (c->ic_freq); } /* * For now, no 80+80 support; it will likely always return 0. */ uint32_t ieee80211_get_channel_center_freq2(const struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_VHT(c) && (c->ic_vht_ch_freq2 != 0)) return (ieee80211_ieee2mhz(c->ic_vht_ch_freq2, c->ic_flags)); return (0); } /* * Adds channels into specified channel list (ieee[] array must be sorted). * Channels are already sorted. */ static int add_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans, const uint8_t ieee[], int nieee, uint32_t flags[]) { uint16_t freq; int i, j, error; int is_vht; for (i = 0; i < nieee; i++) { freq = ieee80211_ieee2mhz(ieee[i], flags[0]); for (j = 0; flags[j] != 0; j++) { /* * Notes: * + HT40 and VHT40 channels occur together, so * we need to be careful that we actually allow that. * + VHT80, VHT160 will coexist with HT40/VHT40, so * make sure it's not skipped because of the overlap * check used for (V)HT40. */ is_vht = !! (flags[j] & IEEE80211_CHAN_VHT); /* XXX TODO FIXME VHT80P80. */ /* Test for VHT160 analogue to the VHT80 below. */ if (is_vht && flags[j] & IEEE80211_CHAN_VHT160) if (! is_vht160_valid_freq(freq)) continue; /* * Test for VHT80. * XXX This is all very broken right now. * What we /should/ do is: * * + check that the frequency is in the list of * allowed VHT80 ranges; and * + the other 3 channels in the list are actually * also available. */ if (is_vht && flags[j] & IEEE80211_CHAN_VHT80) if (! is_vht80_valid_freq(freq)) continue; /* * Test for (V)HT40. * * This is also a fall through from VHT80; as we only * allow a VHT80 channel if the VHT40 combination is * also valid. If the VHT40 form is not valid then * we certainly can't do VHT80.. */ if (flags[j] & IEEE80211_CHAN_HT40D) /* * Can't have a "lower" channel if we are the * first channel. * * Can't have a "lower" channel if it's below/ * within 20MHz of the first channel. * * Can't have a "lower" channel if the channel * below it is not 20MHz away. */ if (i == 0 || ieee[i] < ieee[0] + 4 || freq - 20 != ieee80211_ieee2mhz(ieee[i] - 4, flags[j])) continue; if (flags[j] & IEEE80211_CHAN_HT40U) /* * Can't have an "upper" channel if we are * the last channel. * * Can't have an "upper" channel be above the * last channel in the list. * * Can't have an "upper" channel if the next * channel according to the math isn't 20MHz * away. (Likely for channel 13/14.) */ if (i == nieee - 1 || ieee[i] + 4 > ieee[nieee - 1] || freq + 20 != ieee80211_ieee2mhz(ieee[i] + 4, flags[j])) continue; if (j == 0) { error = addchan(chans, maxchans, nchans, ieee[i], freq, 0, flags[j]); } else { error = copychan_prev(chans, maxchans, nchans, flags[j]); } if (error != 0) return (error); } } return (0); } int ieee80211_add_channel_list_2ghz(struct ieee80211_channel chans[], int maxchans, int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], int cbw_flags) { uint32_t flags[IEEE80211_MODE_MAX]; /* XXX no VHT for now */ getflags_2ghz(bands, flags, cbw_flags); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); } int ieee80211_add_channels_default_2ghz(struct ieee80211_channel chans[], int maxchans, int *nchans, const uint8_t bands[], int cbw_flags) { const uint8_t default_chan_list[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; return (ieee80211_add_channel_list_2ghz(chans, maxchans, nchans, default_chan_list, nitems(default_chan_list), bands, cbw_flags)); } int ieee80211_add_channel_list_5ghz(struct ieee80211_channel chans[], int maxchans, int *nchans, const uint8_t ieee[], int nieee, const uint8_t bands[], int cbw_flags) { /* * XXX-BZ with HT and VHT there is no 1:1 mapping anymore. Review all * uses of IEEE80211_MODE_MAX and add a new #define name for array size. */ uint32_t flags[2 * IEEE80211_MODE_MAX]; getflags_5ghz(bands, flags, cbw_flags); KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__)); return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags)); } /* * Locate a channel given a frequency+flags. We cache * the previous lookup to optimize switching between two * channels--as happens with dynamic turbo. */ struct ieee80211_channel * ieee80211_find_channel(struct ieee80211com *ic, int freq, int flags) { struct ieee80211_channel *c; flags &= IEEE80211_CHAN_ALLTURBO; c = ic->ic_prevchan; if (c != NULL && c->ic_freq == freq && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; /* brute force search */ return (findchannel(ic->ic_channels, ic->ic_nchans, freq, flags)); } /* * Locate a channel given a channel number+flags. We cache * the previous lookup to optimize switching between two * channels--as happens with dynamic turbo. */ struct ieee80211_channel * ieee80211_find_channel_byieee(struct ieee80211com *ic, int ieee, int flags) { struct ieee80211_channel *c; int i; flags &= IEEE80211_CHAN_ALLTURBO; c = ic->ic_prevchan; if (c != NULL && c->ic_ieee == ieee && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; /* brute force search */ for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (c->ic_ieee == ieee && (c->ic_flags & IEEE80211_CHAN_ALLTURBO) == flags) return c; } return NULL; } /* * Lookup a channel suitable for the given rx status. * * This is used to find a channel for a frame (eg beacon, probe * response) based purely on the received PHY information. * * For now it tries to do it based on R_FREQ / R_IEEE. * This is enough for 11bg and 11a (and thus 11ng/11na) * but it will not be enough for GSM, PSB channels and the * like. It also doesn't know about legacy-turbog and * legacy-turbo modes, which some offload NICs actually * support in weird ways. * * Takes the ic and rxstatus; returns the channel or NULL * if not found. * * XXX TODO: Add support for that when the need arises. */ struct ieee80211_channel * ieee80211_lookup_channel_rxstatus(struct ieee80211vap *vap, const struct ieee80211_rx_stats *rxs) { struct ieee80211com *ic = vap->iv_ic; uint32_t flags; struct ieee80211_channel *c; if (rxs == NULL) return (NULL); /* * Strictly speaking we only use freq for now, * however later on we may wish to just store * the ieee for verification. */ if ((rxs->r_flags & IEEE80211_R_FREQ) == 0) return (NULL); if ((rxs->r_flags & IEEE80211_R_IEEE) == 0) return (NULL); if ((rxs->r_flags & IEEE80211_R_BAND) == 0) return (NULL); /* * If the rx status contains a valid ieee/freq, then * ensure we populate the correct channel information * in rxchan before passing it up to the scan infrastructure. * Offload NICs will pass up beacons from all channels * during background scans. */ /* Determine a band */ switch (rxs->c_band) { case IEEE80211_CHAN_2GHZ: flags = IEEE80211_CHAN_G; break; case IEEE80211_CHAN_5GHZ: flags = IEEE80211_CHAN_A; break; default: if (rxs->c_freq < 3000) { flags = IEEE80211_CHAN_G; } else { flags = IEEE80211_CHAN_A; } break; } /* Channel lookup */ c = ieee80211_find_channel(ic, rxs->c_freq, flags); IEEE80211_DPRINTF(vap, IEEE80211_MSG_INPUT, "%s: freq=%d, ieee=%d, flags=0x%08x; c=%p\n", __func__, (int) rxs->c_freq, (int) rxs->c_ieee, flags, c); return (c); } static void addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword) { #define ADD(_ic, _s, _o) \ ifmedia_add(media, \ IFM_MAKEWORD(IFM_IEEE80211, (_s), (_o), 0), 0, NULL) static const u_int mopts[IEEE80211_MODE_MAX] = { [IEEE80211_MODE_AUTO] = IFM_AUTO, [IEEE80211_MODE_11A] = IFM_IEEE80211_11A, [IEEE80211_MODE_11B] = IFM_IEEE80211_11B, [IEEE80211_MODE_11G] = IFM_IEEE80211_11G, [IEEE80211_MODE_FH] = IFM_IEEE80211_FH, [IEEE80211_MODE_TURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, [IEEE80211_MODE_TURBO_G] = IFM_IEEE80211_11G|IFM_IEEE80211_TURBO, [IEEE80211_MODE_STURBO_A] = IFM_IEEE80211_11A|IFM_IEEE80211_TURBO, [IEEE80211_MODE_HALF] = IFM_IEEE80211_11A, /* XXX */ [IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */ [IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA, [IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG, [IEEE80211_MODE_VHT_2GHZ] = IFM_IEEE80211_VHT2G, [IEEE80211_MODE_VHT_5GHZ] = IFM_IEEE80211_VHT5G, }; u_int mopt; mopt = mopts[mode]; if (addsta) ADD(ic, mword, mopt); /* STA mode has no cap */ if (caps & IEEE80211_C_IBSS) ADD(media, mword, mopt | IFM_IEEE80211_ADHOC); if (caps & IEEE80211_C_HOSTAP) ADD(media, mword, mopt | IFM_IEEE80211_HOSTAP); if (caps & IEEE80211_C_AHDEMO) ADD(media, mword, mopt | IFM_IEEE80211_ADHOC | IFM_FLAG0); if (caps & IEEE80211_C_MONITOR) ADD(media, mword, mopt | IFM_IEEE80211_MONITOR); if (caps & IEEE80211_C_WDS) ADD(media, mword, mopt | IFM_IEEE80211_WDS); if (caps & IEEE80211_C_MBSS) ADD(media, mword, mopt | IFM_IEEE80211_MBSS); #undef ADD } /* * Setup the media data structures according to the channel and * rate tables. */ static int ieee80211_media_setup(struct ieee80211com *ic, struct ifmedia *media, int caps, int addsta, ifm_change_cb_t media_change, ifm_stat_cb_t media_stat) { int i, j, rate, maxrate, mword, r; enum ieee80211_phymode mode; const struct ieee80211_rateset *rs; struct ieee80211_rateset allrates; /* * Fill in media characteristics. */ ifmedia_init(media, 0, media_change, media_stat); maxrate = 0; /* * Add media for legacy operating modes. */ memset(&allrates, 0, sizeof(allrates)); for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; addmedia(media, caps, addsta, mode, IFM_AUTO); if (mode == IEEE80211_MODE_AUTO) continue; rs = &ic->ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { rate = rs->rs_rates[i]; mword = ieee80211_rate2media(ic, rate, mode); if (mword == 0) continue; addmedia(media, caps, addsta, mode, mword); /* * Add legacy rate to the collection of all rates. */ r = rate & IEEE80211_RATE_VAL; for (j = 0; j < allrates.rs_nrates; j++) if (allrates.rs_rates[j] == r) break; if (j == allrates.rs_nrates) { /* unique, add to the set */ allrates.rs_rates[j] = r; allrates.rs_nrates++; } rate = (rate & IEEE80211_RATE_VAL) / 2; if (rate > maxrate) maxrate = rate; } } for (i = 0; i < allrates.rs_nrates; i++) { mword = ieee80211_rate2media(ic, allrates.rs_rates[i], IEEE80211_MODE_AUTO); if (mword == 0) continue; /* NB: remove media options from mword */ addmedia(media, caps, addsta, IEEE80211_MODE_AUTO, IFM_SUBTYPE(mword)); } /* * Add HT/11n media. Note that we do not have enough * bits in the media subtype to express the MCS so we * use a "placeholder" media subtype and any fixed MCS * must be specified with a different mechanism. */ for (; mode <= IEEE80211_MODE_11NG; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; addmedia(media, caps, addsta, mode, IFM_AUTO); addmedia(media, caps, addsta, mode, IFM_IEEE80211_MCS); } if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) { addmedia(media, caps, addsta, IEEE80211_MODE_AUTO, IFM_IEEE80211_MCS); i = ic->ic_txstream * 8 - 1; if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) rate = ieee80211_htrates[i].ht40_rate_400ns; else if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40)) rate = ieee80211_htrates[i].ht40_rate_800ns; else if ((ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20)) rate = ieee80211_htrates[i].ht20_rate_400ns; else rate = ieee80211_htrates[i].ht20_rate_800ns; if (rate > maxrate) maxrate = rate; } /* * Add VHT media. * XXX-BZ skip "VHT_2GHZ" for now. */ for (mode = IEEE80211_MODE_VHT_5GHZ; mode <= IEEE80211_MODE_VHT_5GHZ; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; addmedia(media, caps, addsta, mode, IFM_AUTO); addmedia(media, caps, addsta, mode, IFM_IEEE80211_VHT); } if (isset(ic->ic_modecaps, IEEE80211_MODE_VHT_5GHZ)) { addmedia(media, caps, addsta, IEEE80211_MODE_AUTO, IFM_IEEE80211_VHT); /* XXX TODO: VHT maxrate */ } return maxrate; } /* XXX inline or eliminate? */ const struct ieee80211_rateset * ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *c) { /* XXX does this work for 11ng basic rates? */ return &ic->ic_sup_rates[ieee80211_chan2mode(c)]; } /* XXX inline or eliminate? */ const struct ieee80211_htrateset * ieee80211_get_suphtrates(struct ieee80211com *ic, const struct ieee80211_channel *c) { return &ic->ic_sup_htrates; } void ieee80211_announce(struct ieee80211com *ic) { int i, rate, mword; enum ieee80211_phymode mode; const struct ieee80211_rateset *rs; /* NB: skip AUTO since it has no rates */ for (mode = IEEE80211_MODE_AUTO+1; mode < IEEE80211_MODE_11NA; mode++) { if (isclr(ic->ic_modecaps, mode)) continue; ic_printf(ic, "%s rates: ", ieee80211_phymode_name[mode]); rs = &ic->ic_sup_rates[mode]; for (i = 0; i < rs->rs_nrates; i++) { mword = ieee80211_rate2media(ic, rs->rs_rates[i], mode); if (mword == 0) continue; rate = ieee80211_media2rate(mword); printf("%s%d%sMbps", (i != 0 ? " " : ""), rate / 2, ((rate & 0x1) != 0 ? ".5" : "")); } printf("\n"); } ieee80211_ht_announce(ic); ieee80211_vht_announce(ic); } void ieee80211_announce_channels(struct ieee80211com *ic) { const struct ieee80211_channel *c; char type; int i, cw; printf("Chan Freq CW RegPwr MinPwr MaxPwr\n"); for (i = 0; i < ic->ic_nchans; i++) { c = &ic->ic_channels[i]; if (IEEE80211_IS_CHAN_ST(c)) type = 'S'; else if (IEEE80211_IS_CHAN_108A(c)) type = 'T'; else if (IEEE80211_IS_CHAN_108G(c)) type = 'G'; else if (IEEE80211_IS_CHAN_HT(c)) type = 'n'; else if (IEEE80211_IS_CHAN_A(c)) type = 'a'; else if (IEEE80211_IS_CHAN_ANYG(c)) type = 'g'; else if (IEEE80211_IS_CHAN_B(c)) type = 'b'; else type = 'f'; if (IEEE80211_IS_CHAN_HT40(c) || IEEE80211_IS_CHAN_TURBO(c)) cw = 40; else if (IEEE80211_IS_CHAN_HALF(c)) cw = 10; else if (IEEE80211_IS_CHAN_QUARTER(c)) cw = 5; else cw = 20; printf("%4d %4d%c %2d%c %6d %4d.%d %4d.%d\n" , c->ic_ieee, c->ic_freq, type , cw , IEEE80211_IS_CHAN_HT40U(c) ? '+' : IEEE80211_IS_CHAN_HT40D(c) ? '-' : ' ' , c->ic_maxregpower , c->ic_minpower / 2, c->ic_minpower & 1 ? 5 : 0 , c->ic_maxpower / 2, c->ic_maxpower & 1 ? 5 : 0 ); } } static int media2mode(const struct ifmedia_entry *ime, uint32_t flags, uint16_t *mode) { switch (IFM_MODE(ime->ifm_media)) { case IFM_IEEE80211_11A: *mode = IEEE80211_MODE_11A; break; case IFM_IEEE80211_11B: *mode = IEEE80211_MODE_11B; break; case IFM_IEEE80211_11G: *mode = IEEE80211_MODE_11G; break; case IFM_IEEE80211_FH: *mode = IEEE80211_MODE_FH; break; case IFM_IEEE80211_11NA: *mode = IEEE80211_MODE_11NA; break; case IFM_IEEE80211_11NG: *mode = IEEE80211_MODE_11NG; break; case IFM_IEEE80211_VHT2G: *mode = IEEE80211_MODE_VHT_2GHZ; break; case IFM_IEEE80211_VHT5G: *mode = IEEE80211_MODE_VHT_5GHZ; break; case IFM_AUTO: *mode = IEEE80211_MODE_AUTO; break; default: return 0; } /* * Turbo mode is an ``option''. * XXX does not apply to AUTO */ if (ime->ifm_media & IFM_IEEE80211_TURBO) { if (*mode == IEEE80211_MODE_11A) { if (flags & IEEE80211_F_TURBOP) *mode = IEEE80211_MODE_TURBO_A; else *mode = IEEE80211_MODE_STURBO_A; } else if (*mode == IEEE80211_MODE_11G) *mode = IEEE80211_MODE_TURBO_G; else return 0; } /* XXX HT40 +/- */ return 1; } /* * Handle a media change request on the vap interface. */ int ieee80211_media_change(struct ifnet *ifp) { struct ieee80211vap *vap = ifp->if_softc; struct ifmedia_entry *ime = vap->iv_media.ifm_cur; uint16_t newmode; if (!media2mode(ime, vap->iv_flags, &newmode)) return EINVAL; if (vap->iv_des_mode != newmode) { vap->iv_des_mode = newmode; /* XXX kick state machine if up+running */ } return 0; } /* * Common code to calculate the media status word * from the operating mode and channel state. */ static int media_status(enum ieee80211_opmode opmode, const struct ieee80211_channel *chan) { int status; status = IFM_IEEE80211; switch (opmode) { case IEEE80211_M_STA: break; case IEEE80211_M_IBSS: status |= IFM_IEEE80211_ADHOC; break; case IEEE80211_M_HOSTAP: status |= IFM_IEEE80211_HOSTAP; break; case IEEE80211_M_MONITOR: status |= IFM_IEEE80211_MONITOR; break; case IEEE80211_M_AHDEMO: status |= IFM_IEEE80211_ADHOC | IFM_FLAG0; break; case IEEE80211_M_WDS: status |= IFM_IEEE80211_WDS; break; case IEEE80211_M_MBSS: status |= IFM_IEEE80211_MBSS; break; } if (IEEE80211_IS_CHAN_VHT_5GHZ(chan)) { status |= IFM_IEEE80211_VHT5G; } else if (IEEE80211_IS_CHAN_VHT_2GHZ(chan)) { status |= IFM_IEEE80211_VHT2G; } else if (IEEE80211_IS_CHAN_HTA(chan)) { status |= IFM_IEEE80211_11NA; } else if (IEEE80211_IS_CHAN_HTG(chan)) { status |= IFM_IEEE80211_11NG; } else if (IEEE80211_IS_CHAN_A(chan)) { status |= IFM_IEEE80211_11A; } else if (IEEE80211_IS_CHAN_B(chan)) { status |= IFM_IEEE80211_11B; } else if (IEEE80211_IS_CHAN_ANYG(chan)) { status |= IFM_IEEE80211_11G; } else if (IEEE80211_IS_CHAN_FHSS(chan)) { status |= IFM_IEEE80211_FH; } /* XXX else complain? */ if (IEEE80211_IS_CHAN_TURBO(chan)) status |= IFM_IEEE80211_TURBO; #if 0 if (IEEE80211_IS_CHAN_HT20(chan)) status |= IFM_IEEE80211_HT20; if (IEEE80211_IS_CHAN_HT40(chan)) status |= IFM_IEEE80211_HT40; #endif return status; } void ieee80211_media_status(struct ifnet *ifp, struct ifmediareq *imr) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; enum ieee80211_phymode mode; imr->ifm_status = IFM_AVALID; /* * NB: use the current channel's mode to lock down a xmit * rate only when running; otherwise we may have a mismatch * in which case the rate will not be convertible. */ if (vap->iv_state == IEEE80211_S_RUN || vap->iv_state == IEEE80211_S_SLEEP) { imr->ifm_status |= IFM_ACTIVE; mode = ieee80211_chan2mode(ic->ic_curchan); } else mode = IEEE80211_MODE_AUTO; imr->ifm_active = media_status(vap->iv_opmode, ic->ic_curchan); /* * Calculate a current rate if possible. */ if (vap->iv_txparms[mode].ucastrate != IEEE80211_FIXED_RATE_NONE) { /* * A fixed rate is set, report that. */ imr->ifm_active |= ieee80211_rate2media(ic, vap->iv_txparms[mode].ucastrate, mode); } else if (vap->iv_opmode == IEEE80211_M_STA) { /* * In station mode report the current transmit rate. */ imr->ifm_active |= ieee80211_rate2media(ic, vap->iv_bss->ni_txrate, mode); } else imr->ifm_active |= IFM_AUTO; if (imr->ifm_status & IFM_ACTIVE) imr->ifm_current = imr->ifm_active; } /* * Set the current phy mode and recalculate the active channel * set based on the available channels for this mode. Also * select a new default/current channel if the current one is * inappropriate for this mode. */ int ieee80211_setmode(struct ieee80211com *ic, enum ieee80211_phymode mode) { /* * Adjust basic rates in 11b/11g supported rate set. * Note that if operating on a hal/quarter rate channel * this is a noop as those rates sets are different * and used instead. */ if (mode == IEEE80211_MODE_11G || mode == IEEE80211_MODE_11B) ieee80211_setbasicrates(&ic->ic_sup_rates[mode], mode); ic->ic_curmode = mode; ieee80211_reset_erp(ic); /* reset global ERP state */ return 0; } /* * Return the phy mode for with the specified channel. */ enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *chan) { if (IEEE80211_IS_CHAN_VHT_2GHZ(chan)) return IEEE80211_MODE_VHT_2GHZ; else if (IEEE80211_IS_CHAN_VHT_5GHZ(chan)) return IEEE80211_MODE_VHT_5GHZ; else if (IEEE80211_IS_CHAN_HTA(chan)) return IEEE80211_MODE_11NA; else if (IEEE80211_IS_CHAN_HTG(chan)) return IEEE80211_MODE_11NG; else if (IEEE80211_IS_CHAN_108G(chan)) return IEEE80211_MODE_TURBO_G; else if (IEEE80211_IS_CHAN_ST(chan)) return IEEE80211_MODE_STURBO_A; else if (IEEE80211_IS_CHAN_TURBO(chan)) return IEEE80211_MODE_TURBO_A; else if (IEEE80211_IS_CHAN_HALF(chan)) return IEEE80211_MODE_HALF; else if (IEEE80211_IS_CHAN_QUARTER(chan)) return IEEE80211_MODE_QUARTER; else if (IEEE80211_IS_CHAN_A(chan)) return IEEE80211_MODE_11A; else if (IEEE80211_IS_CHAN_ANYG(chan)) return IEEE80211_MODE_11G; else if (IEEE80211_IS_CHAN_B(chan)) return IEEE80211_MODE_11B; else if (IEEE80211_IS_CHAN_FHSS(chan)) return IEEE80211_MODE_FH; /* NB: should not get here */ printf("%s: cannot map channel to mode; freq %u flags 0x%x\n", __func__, chan->ic_freq, chan->ic_flags); return IEEE80211_MODE_11B; } struct ratemedia { u_int match; /* rate + mode */ u_int media; /* if_media rate */ }; static int findmedia(const struct ratemedia rates[], int n, u_int match) { int i; for (i = 0; i < n; i++) if (rates[i].match == match) return rates[i].media; return IFM_AUTO; } /* * Convert IEEE80211 rate value to ifmedia subtype. * Rate is either a legacy rate in units of 0.5Mbps * or an MCS index. */ int ieee80211_rate2media(struct ieee80211com *ic, int rate, enum ieee80211_phymode mode) { static const struct ratemedia rates[] = { { 2 | IFM_IEEE80211_FH, IFM_IEEE80211_FH1 }, { 4 | IFM_IEEE80211_FH, IFM_IEEE80211_FH2 }, { 2 | IFM_IEEE80211_11B, IFM_IEEE80211_DS1 }, { 4 | IFM_IEEE80211_11B, IFM_IEEE80211_DS2 }, { 11 | IFM_IEEE80211_11B, IFM_IEEE80211_DS5 }, { 22 | IFM_IEEE80211_11B, IFM_IEEE80211_DS11 }, { 44 | IFM_IEEE80211_11B, IFM_IEEE80211_DS22 }, { 12 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM6 }, { 18 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM9 }, { 24 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM12 }, { 36 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM18 }, { 48 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM24 }, { 72 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM36 }, { 96 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM48 }, { 108 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM54 }, { 2 | IFM_IEEE80211_11G, IFM_IEEE80211_DS1 }, { 4 | IFM_IEEE80211_11G, IFM_IEEE80211_DS2 }, { 11 | IFM_IEEE80211_11G, IFM_IEEE80211_DS5 }, { 22 | IFM_IEEE80211_11G, IFM_IEEE80211_DS11 }, { 12 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM6 }, { 18 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM9 }, { 24 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM12 }, { 36 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM18 }, { 48 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM24 }, { 72 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM36 }, { 96 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM48 }, { 108 | IFM_IEEE80211_11G, IFM_IEEE80211_OFDM54 }, { 6 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM3 }, { 9 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM4 }, { 54 | IFM_IEEE80211_11A, IFM_IEEE80211_OFDM27 }, /* NB: OFDM72 doesn't really exist so we don't handle it */ }; static const struct ratemedia htrates[] = { { 0, IFM_IEEE80211_MCS }, { 1, IFM_IEEE80211_MCS }, { 2, IFM_IEEE80211_MCS }, { 3, IFM_IEEE80211_MCS }, { 4, IFM_IEEE80211_MCS }, { 5, IFM_IEEE80211_MCS }, { 6, IFM_IEEE80211_MCS }, { 7, IFM_IEEE80211_MCS }, { 8, IFM_IEEE80211_MCS }, { 9, IFM_IEEE80211_MCS }, { 10, IFM_IEEE80211_MCS }, { 11, IFM_IEEE80211_MCS }, { 12, IFM_IEEE80211_MCS }, { 13, IFM_IEEE80211_MCS }, { 14, IFM_IEEE80211_MCS }, { 15, IFM_IEEE80211_MCS }, { 16, IFM_IEEE80211_MCS }, { 17, IFM_IEEE80211_MCS }, { 18, IFM_IEEE80211_MCS }, { 19, IFM_IEEE80211_MCS }, { 20, IFM_IEEE80211_MCS }, { 21, IFM_IEEE80211_MCS }, { 22, IFM_IEEE80211_MCS }, { 23, IFM_IEEE80211_MCS }, { 24, IFM_IEEE80211_MCS }, { 25, IFM_IEEE80211_MCS }, { 26, IFM_IEEE80211_MCS }, { 27, IFM_IEEE80211_MCS }, { 28, IFM_IEEE80211_MCS }, { 29, IFM_IEEE80211_MCS }, { 30, IFM_IEEE80211_MCS }, { 31, IFM_IEEE80211_MCS }, { 32, IFM_IEEE80211_MCS }, { 33, IFM_IEEE80211_MCS }, { 34, IFM_IEEE80211_MCS }, { 35, IFM_IEEE80211_MCS }, { 36, IFM_IEEE80211_MCS }, { 37, IFM_IEEE80211_MCS }, { 38, IFM_IEEE80211_MCS }, { 39, IFM_IEEE80211_MCS }, { 40, IFM_IEEE80211_MCS }, { 41, IFM_IEEE80211_MCS }, { 42, IFM_IEEE80211_MCS }, { 43, IFM_IEEE80211_MCS }, { 44, IFM_IEEE80211_MCS }, { 45, IFM_IEEE80211_MCS }, { 46, IFM_IEEE80211_MCS }, { 47, IFM_IEEE80211_MCS }, { 48, IFM_IEEE80211_MCS }, { 49, IFM_IEEE80211_MCS }, { 50, IFM_IEEE80211_MCS }, { 51, IFM_IEEE80211_MCS }, { 52, IFM_IEEE80211_MCS }, { 53, IFM_IEEE80211_MCS }, { 54, IFM_IEEE80211_MCS }, { 55, IFM_IEEE80211_MCS }, { 56, IFM_IEEE80211_MCS }, { 57, IFM_IEEE80211_MCS }, { 58, IFM_IEEE80211_MCS }, { 59, IFM_IEEE80211_MCS }, { 60, IFM_IEEE80211_MCS }, { 61, IFM_IEEE80211_MCS }, { 62, IFM_IEEE80211_MCS }, { 63, IFM_IEEE80211_MCS }, { 64, IFM_IEEE80211_MCS }, { 65, IFM_IEEE80211_MCS }, { 66, IFM_IEEE80211_MCS }, { 67, IFM_IEEE80211_MCS }, { 68, IFM_IEEE80211_MCS }, { 69, IFM_IEEE80211_MCS }, { 70, IFM_IEEE80211_MCS }, { 71, IFM_IEEE80211_MCS }, { 72, IFM_IEEE80211_MCS }, { 73, IFM_IEEE80211_MCS }, { 74, IFM_IEEE80211_MCS }, { 75, IFM_IEEE80211_MCS }, { 76, IFM_IEEE80211_MCS }, }; static const struct ratemedia vhtrates[] = { { 0, IFM_IEEE80211_VHT }, { 1, IFM_IEEE80211_VHT }, { 2, IFM_IEEE80211_VHT }, { 3, IFM_IEEE80211_VHT }, { 4, IFM_IEEE80211_VHT }, { 5, IFM_IEEE80211_VHT }, { 6, IFM_IEEE80211_VHT }, { 7, IFM_IEEE80211_VHT }, { 8, IFM_IEEE80211_VHT }, /* Optional. */ { 9, IFM_IEEE80211_VHT }, /* Optional. */ #if 0 /* Some QCA and BRCM seem to support this; offspec. */ { 10, IFM_IEEE80211_VHT }, { 11, IFM_IEEE80211_VHT }, #endif }; int m; /* * Check 11ac/11n rates first for match as an MCS. */ if (mode == IEEE80211_MODE_VHT_5GHZ) { if (rate & IFM_IEEE80211_VHT) { rate &= ~IFM_IEEE80211_VHT; m = findmedia(vhtrates, nitems(vhtrates), rate); if (m != IFM_AUTO) return (m | IFM_IEEE80211_VHT); } } else if (mode == IEEE80211_MODE_11NA) { if (rate & IEEE80211_RATE_MCS) { rate &= ~IEEE80211_RATE_MCS; m = findmedia(htrates, nitems(htrates), rate); if (m != IFM_AUTO) return m | IFM_IEEE80211_11NA; } } else if (mode == IEEE80211_MODE_11NG) { /* NB: 12 is ambiguous, it will be treated as an MCS */ if (rate & IEEE80211_RATE_MCS) { rate &= ~IEEE80211_RATE_MCS; m = findmedia(htrates, nitems(htrates), rate); if (m != IFM_AUTO) return m | IFM_IEEE80211_11NG; } } rate &= IEEE80211_RATE_VAL; switch (mode) { case IEEE80211_MODE_11A: case IEEE80211_MODE_HALF: /* XXX good 'nuf */ case IEEE80211_MODE_QUARTER: case IEEE80211_MODE_11NA: case IEEE80211_MODE_TURBO_A: case IEEE80211_MODE_STURBO_A: return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11A); case IEEE80211_MODE_11B: return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11B); case IEEE80211_MODE_FH: return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_FH); case IEEE80211_MODE_AUTO: /* NB: ic may be NULL for some drivers */ if (ic != NULL && ic->ic_phytype == IEEE80211_T_FH) return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_FH); /* NB: hack, 11g matches both 11b+11a rates */ /* fall thru... */ case IEEE80211_MODE_11G: case IEEE80211_MODE_11NG: case IEEE80211_MODE_TURBO_G: return findmedia(rates, nitems(rates), rate | IFM_IEEE80211_11G); case IEEE80211_MODE_VHT_2GHZ: case IEEE80211_MODE_VHT_5GHZ: /* XXX TODO: need to figure out mapping for VHT rates */ return IFM_AUTO; } return IFM_AUTO; } int ieee80211_media2rate(int mword) { static const int ieeerates[] = { -1, /* IFM_AUTO */ 0, /* IFM_MANUAL */ 0, /* IFM_NONE */ 2, /* IFM_IEEE80211_FH1 */ 4, /* IFM_IEEE80211_FH2 */ 2, /* IFM_IEEE80211_DS1 */ 4, /* IFM_IEEE80211_DS2 */ 11, /* IFM_IEEE80211_DS5 */ 22, /* IFM_IEEE80211_DS11 */ 44, /* IFM_IEEE80211_DS22 */ 12, /* IFM_IEEE80211_OFDM6 */ 18, /* IFM_IEEE80211_OFDM9 */ 24, /* IFM_IEEE80211_OFDM12 */ 36, /* IFM_IEEE80211_OFDM18 */ 48, /* IFM_IEEE80211_OFDM24 */ 72, /* IFM_IEEE80211_OFDM36 */ 96, /* IFM_IEEE80211_OFDM48 */ 108, /* IFM_IEEE80211_OFDM54 */ 144, /* IFM_IEEE80211_OFDM72 */ 0, /* IFM_IEEE80211_DS354k */ 0, /* IFM_IEEE80211_DS512k */ 6, /* IFM_IEEE80211_OFDM3 */ 9, /* IFM_IEEE80211_OFDM4 */ 54, /* IFM_IEEE80211_OFDM27 */ -1, /* IFM_IEEE80211_MCS */ -1, /* IFM_IEEE80211_VHT */ }; return IFM_SUBTYPE(mword) < nitems(ieeerates) ? ieeerates[IFM_SUBTYPE(mword)] : 0; } /* * The following hash function is adapted from "Hash Functions" by Bob Jenkins * ("Algorithm Alley", Dr. Dobbs Journal, September 1997). */ #define mix(a, b, c) \ do { \ a -= b; a -= c; a ^= (c >> 13); \ b -= c; b -= a; b ^= (a << 8); \ c -= a; c -= b; c ^= (b >> 13); \ a -= b; a -= c; a ^= (c >> 12); \ b -= c; b -= a; b ^= (a << 16); \ c -= a; c -= b; c ^= (b >> 5); \ a -= b; a -= c; a ^= (c >> 3); \ b -= c; b -= a; b ^= (a << 10); \ c -= a; c -= b; c ^= (b >> 15); \ } while (/*CONSTCOND*/0) uint32_t ieee80211_mac_hash(const struct ieee80211com *ic, const uint8_t addr[IEEE80211_ADDR_LEN]) { uint32_t a = 0x9e3779b9, b = 0x9e3779b9, c = ic->ic_hash_key; b += addr[5] << 8; b += addr[4]; a += addr[3] << 24; a += addr[2] << 16; a += addr[1] << 8; a += addr[0]; mix(a, b, c); return c; } #undef mix char ieee80211_channel_type_char(const struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_ST(c)) return 'S'; if (IEEE80211_IS_CHAN_108A(c)) return 'T'; if (IEEE80211_IS_CHAN_108G(c)) return 'G'; if (IEEE80211_IS_CHAN_VHT(c)) return 'v'; if (IEEE80211_IS_CHAN_HT(c)) return 'n'; if (IEEE80211_IS_CHAN_A(c)) return 'a'; if (IEEE80211_IS_CHAN_ANYG(c)) return 'g'; if (IEEE80211_IS_CHAN_B(c)) return 'b'; return 'f'; } diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c index e4a09a58049b..e3f70fbe3f29 100644 --- a/sys/net80211/ieee80211_ddb.c +++ b/sys/net80211/ieee80211_ddb.c @@ -1,1040 +1,1040 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include "opt_ddb.h" #include "opt_wlan.h" #ifdef DDB /* * IEEE 802.11 DDB support */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef IEEE80211_SUPPORT_TDMA #include #endif #ifdef IEEE80211_SUPPORT_MESH #include #endif #include #include #define DB_PRINTSYM(prefix, name, addr) do { \ db_printf("%s%-25s : ", prefix, name); \ db_printsym((db_addr_t) addr, DB_STGY_ANY); \ db_printf("\n"); \ } while (0) static void _db_show_sta(const struct ieee80211_node *); static void _db_show_vap(const struct ieee80211vap *, int, int); static void _db_show_com(const struct ieee80211com *, int showvaps, int showsta, int showmesh, int showprocs, int); static void _db_show_all_vaps(void *, struct ieee80211com *); static void _db_show_node_table(const char *tag, const struct ieee80211_node_table *); static void _db_show_channel(const char *tag, const struct ieee80211_channel *); static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *); static void _db_show_appie(const char *tag, const struct ieee80211_appie *); static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *); static void _db_show_roamparams(const char *tag, const void *arg, const struct ieee80211_roamparam *rp); static void _db_show_txparams(const char *tag, const void *arg, const struct ieee80211_txparam *tp); static void _db_show_ageq(const char *tag, const struct ieee80211_ageq *q); static void _db_show_stats(const struct ieee80211_stats *); #ifdef IEEE80211_SUPPORT_MESH static void _db_show_mesh(const struct ieee80211_mesh_state *); #endif DB_SHOW_COMMAND(sta, db_show_sta) { if (!have_addr) { db_printf("usage: show sta \n"); return; } _db_show_sta((const struct ieee80211_node *) addr); } DB_SHOW_COMMAND(statab, db_show_statab) { if (!have_addr) { db_printf("usage: show statab \n"); return; } _db_show_node_table("", (const struct ieee80211_node_table *) addr); } DB_SHOW_COMMAND(vap, db_show_vap) { int i, showmesh = 0, showprocs = 0; if (!have_addr) { db_printf("usage: show vap \n"); return; } for (i = 0; modif[i] != '\0'; i++) switch (modif[i]) { case 'a': showprocs = 1; showmesh = 1; break; case 'm': showmesh = 1; break; case 'p': showprocs = 1; break; } _db_show_vap((const struct ieee80211vap *) addr, showmesh, showprocs); } DB_SHOW_COMMAND(com, db_show_com) { const struct ieee80211com *ic; int i, showprocs = 0, showvaps = 0, showsta = 0, showmesh = 0, showscan = 0; if (!have_addr) { db_printf("usage: show com \n"); return; } for (i = 0; modif[i] != '\0'; i++) switch (modif[i]) { case 'a': showsta = showmesh = showvaps = showprocs = showscan = 1; break; case 'S': showscan = 1; break; case 's': showsta = 1; break; case 'm': showmesh = 1; break; case 'v': showvaps = 1; break; case 'p': showprocs = 1; break; } ic = (const struct ieee80211com *) addr; _db_show_com(ic, showvaps, showsta, showmesh, showprocs, showscan); } DB_SHOW_ALL_COMMAND(vaps, db_show_all_vaps) { int i, showall = 0; for (i = 0; modif[i] != '\0'; i++) switch (modif[i]) { case 'a': showall = 1; break; } ieee80211_iterate_coms(_db_show_all_vaps, &showall); } #ifdef IEEE80211_SUPPORT_MESH DB_SHOW_ALL_COMMAND(mesh, db_show_mesh) { const struct ieee80211_mesh_state *ms; if (!have_addr) { db_printf("usage: show mesh \n"); return; } ms = (const struct ieee80211_mesh_state *) addr; _db_show_mesh(ms); } #endif /* IEEE80211_SUPPORT_MESH */ static void _db_show_txampdu(const char *sep, int ix, const struct ieee80211_tx_ampdu *tap) { db_printf("%stxampdu[%d]: %p flags %b %s\n", sep, ix, tap, tap->txa_flags, IEEE80211_AGGR_BITS, ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)]); db_printf("%s token %u lastsample %d pkts %d avgpps %d qbytes %d qframes %d\n", sep, tap->txa_token, tap->txa_lastsample, tap->txa_pkts, tap->txa_avgpps, tap->txa_qbytes, tap->txa_qframes); db_printf("%s start %u seqpending %u wnd %u attempts %d nextrequest %d\n", sep, tap->txa_start, tap->txa_seqpending, tap->txa_wnd, tap->txa_attempts, tap->txa_nextrequest); /* XXX timer */ } static void _db_show_rxampdu(const char *sep, int ix, const struct ieee80211_rx_ampdu *rap) { struct mbuf *m; int i; db_printf("%srxampdu[%d]: %p flags 0x%x tid %u\n", sep, ix, rap, rap->rxa_flags, ix /*XXX */); db_printf("%s qbytes %d qframes %d seqstart %u start %u wnd %u\n", sep, rap->rxa_qbytes, rap->rxa_qframes, rap->rxa_seqstart, rap->rxa_start, rap->rxa_wnd); db_printf("%s age %d nframes %d\n", sep, rap->rxa_age, rap->rxa_nframes); for (i = 0; i < IEEE80211_AGGR_BAWMAX; i++) if (mbufq_len(&rap->rxa_mq[i]) > 0) { db_printf("%s m[%2u:%4u] ", sep, i, IEEE80211_SEQ_ADD(rap->rxa_start, i)); STAILQ_FOREACH(m, &rap->rxa_mq[i].mq_head, m_stailqpkt) { db_printf(" %p", m); } db_printf("\n"); } } static void _db_show_sta(const struct ieee80211_node *ni) { int i; db_printf("STA: %p: mac %s refcnt %d\n", ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)); db_printf("\tvap %p wdsvap %p ic %p table %p\n", ni->ni_vap, ni->ni_wdsvap, ni->ni_ic, ni->ni_table); db_printf("\tflags=%b\n", ni->ni_flags, IEEE80211_NODE_BITS); db_printf("\tauthmode %u ath_flags 0x%x ath_defkeyix %u\n", ni->ni_authmode, ni->ni_ath_flags, ni->ni_ath_defkeyix); db_printf("\tassocid 0x%x txpower %u vlan %u\n", ni->ni_associd, ni->ni_txpower, ni->ni_vlan); db_printf("\tjointime %d (%lu secs) challenge %p\n", ni->ni_jointime, (unsigned long)(time_uptime - ni->ni_jointime), ni->ni_challenge); db_printf("\ties: data %p len %d\n", ni->ni_ies.data, ni->ni_ies.len); db_printf("\t[wpa_ie %p rsn_ie %p wme_ie %p ath_ie %p\n", ni->ni_ies.wpa_ie, ni->ni_ies.rsn_ie, ni->ni_ies.wme_ie, ni->ni_ies.ath_ie); db_printf("\t htcap_ie %p htinfo_ie %p]\n", ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie); db_printf("\t vhtcap_ie %p vhtopmode_ie %p vhtpwrenv_ie %p]\n", ni->ni_ies.vhtcap_ie, ni->ni_ies.vhtopmode_ie, ni->ni_ies.vhtpwrenv_ie); if (ni->ni_flags & IEEE80211_NODE_QOS) { for (i = 0; i < WME_NUM_TID; i++) { if (ni->ni_txseqs[i] || ni->ni_rxseqs[i]) db_printf("\t[%u] txseq %u rxseq %u fragno %u\n", i, ni->ni_txseqs[i], ni->ni_rxseqs[i] >> IEEE80211_SEQ_SEQ_SHIFT, ni->ni_rxseqs[i] & IEEE80211_SEQ_FRAG_MASK); } } db_printf("\ttxseq %u rxseq %u fragno %u rxfragstamp %u\n", ni->ni_txseqs[IEEE80211_NONQOS_TID], ni->ni_rxseqs[IEEE80211_NONQOS_TID] >> IEEE80211_SEQ_SEQ_SHIFT, ni->ni_rxseqs[IEEE80211_NONQOS_TID] & IEEE80211_SEQ_FRAG_MASK, ni->ni_rxfragstamp); db_printf("\trxfrag[0] %p rxfrag[1] %p rxfrag[2] %p\n", ni->ni_rxfrag[0], ni->ni_rxfrag[1], ni->ni_rxfrag[2]); _db_show_key("\tucastkey", 0, &ni->ni_ucastkey); db_printf("\tavgrssi 0x%x (rssi %d) noise %d\n", ni->ni_avgrssi, IEEE80211_RSSI_GET(ni->ni_avgrssi), ni->ni_noise); db_printf("\tintval %u capinfo %b\n", ni->ni_intval, ni->ni_capinfo, IEEE80211_CAPINFO_BITS); db_printf("\tbssid %s", ether_sprintf(ni->ni_bssid)); _db_show_ssid(" essid ", 0, ni->ni_esslen, ni->ni_essid); db_printf("\n"); _db_show_channel("\tchannel", ni->ni_chan); db_printf("\n"); db_printf("\terp %b dtim_period %u dtim_count %u\n", ni->ni_erp, IEEE80211_ERP_BITS, ni->ni_dtim_period, ni->ni_dtim_count); db_printf("\thtcap %b htparam 0x%x htctlchan %u ht2ndchan %u\n", ni->ni_htcap, IEEE80211_HTCAP_BITS, ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan); db_printf("\thtopmode 0x%x htstbc 0x%x chw %u\n", ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw); /* XXX ampdu state */ for (i = 0; i < WME_NUM_TID; i++) if (ni->ni_tx_ampdu[i].txa_flags & IEEE80211_AGGR_SETUP) _db_show_txampdu("\t", i, &ni->ni_tx_ampdu[i]); for (i = 0; i < WME_NUM_TID; i++) if (ni->ni_rx_ampdu[i].rxa_flags) _db_show_rxampdu("\t", i, &ni->ni_rx_ampdu[i]); db_printf("\tinact %u inact_reload %u txrate %u\n", ni->ni_inact, ni->ni_inact_reload, ni->ni_txrate); #ifdef IEEE80211_SUPPORT_MESH _db_show_ssid("\tmeshid ", 0, ni->ni_meshidlen, ni->ni_meshid); db_printf(" mlstate %b mllid 0x%x mlpid 0x%x mlrcnt %u mltval %u\n", ni->ni_mlstate, IEEE80211_MESH_MLSTATE_BITS, ni->ni_mllid, ni->ni_mlpid, ni->ni_mlrcnt, ni->ni_mltval); #endif /* VHT state */ db_printf("\tvhtcap %b vht_basicmcs %#06x vht_pad2 %#06x\n", ni->ni_vhtcap, IEEE80211_VHTCAP_BITS, ni->ni_vht_basicmcs, ni->ni_vht_pad2); db_printf("\tvht_mcsinfo: { rx_mcs_map %#06x rx_highest %#06x " "tx_mcs_map %#06x tx_highest %#06x }\n", ni->ni_vht_mcsinfo.rx_mcs_map, ni->ni_vht_mcsinfo.rx_highest, ni->ni_vht_mcsinfo.tx_mcs_map, ni->ni_vht_mcsinfo.tx_highest); db_printf("\tvht_chan1/chan2 %u/%u vht_chanwidth %#04x\n", ni->ni_vht_chan1, ni->ni_vht_chan2, ni->ni_vht_chanwidth); db_printf("\tvht_pad1 %#04x vht_spare { %#x %#x %#x %#x %#x %#x %#x %#x }\n", ni->ni_vht_pad1, ni->ni_vht_spare[0], ni->ni_vht_spare[1], ni->ni_vht_spare[2], ni->ni_vht_spare[3], ni->ni_vht_spare[4], ni->ni_vht_spare[5], ni->ni_vht_spare[6], ni->ni_vht_spare[7]); db_printf("\tni_tx_superg[] = {"); for (i = 0; i < WME_NUM_TID; i++) db_printf(" %p%s", ni->ni_tx_superg[i], (i == 0) ? "" : ","); db_printf(" }\n"); db_printf("\tni_rctls = %p", ni->ni_rctls); db_printf("\tni_drv_data = %p", ni->ni_drv_data); db_printf("\n"); db_printf("\tni_spare[3] = { %#jx %#jx %#jx }", ni->ni_spare[0], ni->ni_spare[1], ni->ni_spare[2]); db_printf("\n"); #ifdef __notyet__ struct ieee80211_psq ni_psq; /* power save queue */ struct ieee80211_nodestats ni_stats; /* per-node statistics */ /* quiet time IE state for the given node */ uint32_t ni_quiet_ie_set; /* Quiet time IE was seen */ struct ieee80211_quiet_ie ni_quiet_ie; /* last seen quiet IE */ /* U-APSD */ uint8_t ni_uapsd; /* U-APSD per-node flags matching WMM STA QoS Info field */ #endif } #ifdef IEEE80211_SUPPORT_TDMA static void _db_show_tdma(const char *sep, const struct ieee80211_tdma_state *ts, int showprocs) { db_printf("%stdma %p:\n", sep, ts); db_printf("%s version %u slot %u bintval %u peer %p\n", sep, ts->tdma_version, ts->tdma_slot, ts->tdma_bintval, ts->tdma_peer); db_printf("%s slotlen %u slotcnt %u", sep, ts->tdma_slotlen, ts->tdma_slotcnt); db_printf(" inuse 0x%x active 0x%x count %d\n", ts->tdma_inuse[0], ts->tdma_active[0], ts->tdma_count); if (showprocs) { DB_PRINTSYM(sep, " tdma_newstate", ts->tdma_newstate); DB_PRINTSYM(sep, " tdma_recv_mgmt", ts->tdma_recv_mgmt); DB_PRINTSYM(sep, " tdma_opdetach", ts->tdma_opdetach); } } #endif /* IEEE80211_SUPPORT_TDMA */ static void _db_show_scan(const struct ieee80211_scan_state *ss, int showprocs) { int i; const struct ieee80211_scanner *ss_ops; db_printf("SCAN %p:", ss); db_printf(" vap %p ic %p", ss->ss_vap, ss->ss_ic); db_printf("\n"); db_printf("\tss_ops %p (%s) ss_priv %p", ss->ss_ops, ss->ss_ops->scan_name, ss->ss_priv); db_printf("\n"); if (showprocs) { ss_ops = ss->ss_ops; DB_PRINTSYM("\t", "scan_attach", ss_ops->scan_attach); DB_PRINTSYM("\t", "scan_detach", ss_ops->scan_detach); DB_PRINTSYM("\t", "scan_start", ss_ops->scan_start); DB_PRINTSYM("\t", "scan_restart", ss_ops->scan_restart); DB_PRINTSYM("\t", "scan_cancel", ss_ops->scan_cancel); DB_PRINTSYM("\t", "scan_end", ss_ops->scan_end); DB_PRINTSYM("\t", "scan_flush", ss_ops->scan_flush); DB_PRINTSYM("\t", "scan_pickchan", ss_ops->scan_pickchan); DB_PRINTSYM("\t", "scan_add", ss_ops->scan_add); DB_PRINTSYM("\t", "scan_age", ss_ops->scan_age); DB_PRINTSYM("\t", "scan_assoc_fail", ss_ops->scan_assoc_fail); DB_PRINTSYM("\t", "scan_assoc_success", ss_ops->scan_assoc_success); DB_PRINTSYM("\t", "scan_iterate", ss_ops->scan_iterate); DB_PRINTSYM("\t", "scan_spare0", ss_ops->scan_spare0); DB_PRINTSYM("\t", "scan_spare1", ss_ops->scan_spare1); DB_PRINTSYM("\t", "scan_spare2", ss_ops->scan_spare2); DB_PRINTSYM("\t", "scan_spare3", ss_ops->scan_spare3); } db_printf("\tss_flags %b", ss->ss_flags, IEEE80211_SS_FLAGS_BITS); db_printf("\n"); db_printf("\tss_nssid %u", ss->ss_nssid); for (i = 0; i < ss->ss_nssid && i < IEEE80211_SCAN_MAX_SSID; i++) _db_show_ssid(" ss_nssid[%d]", i, ss->ss_ssid[i].len, ss->ss_ssid[i].ssid); db_printf("\n"); db_printf("\tss_chans:\n"); for (i = 0; i < ss->ss_last && i < IEEE80211_SCAN_MAX; i++) { db_printf("\t%-3d", i); _db_show_channel(" ", ss->ss_chans[i]); db_printf("\n"); } db_printf("\tss_next %u ss_last %u ss_mindwell %lu ss_maxdwell %lu", ss->ss_next, ss->ss_last, ss->ss_mindwell, ss->ss_maxdwell); db_printf("\n"); } static void _db_show_rate(const struct ieee80211_ratectl *rate, const void *rs, const int showprocs) { db_printf("\tiv_rate %p", rate); db_printf(" iv_rs %p", rs); db_printf("\n"); if (showprocs) { db_printf("\t ir_name %s", rate->ir_name); db_printf("\n"); DB_PRINTSYM("\t ", "ir_attach", rate->ir_attach); DB_PRINTSYM("\t ", "ir_detach", rate->ir_detach); DB_PRINTSYM("\t ", "ir_init", rate->ir_init); DB_PRINTSYM("\t ", "ir_deinit", rate->ir_deinit); DB_PRINTSYM("\t ", "ir_node_init", rate->ir_node_init); DB_PRINTSYM("\t ", "ir_node_deinit", rate->ir_node_deinit); DB_PRINTSYM("\t ", "ir_rate", rate->ir_rate); DB_PRINTSYM("\t ", "ir_tx_complete", rate->ir_tx_complete); DB_PRINTSYM("\t ", "ir_tx_update", rate->ir_tx_update); DB_PRINTSYM("\t ", "ir_setinterval", rate->ir_setinterval); DB_PRINTSYM("\t ", "ir_node_stats", rate->ir_node_stats); } } static void _db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs) { const struct ieee80211com *ic = vap->iv_ic; int i; db_printf("VAP %p:", vap); db_printf(" bss %p", vap->iv_bss); db_printf(" myaddr %s", ether_sprintf(vap->iv_myaddr)); db_printf("\n"); db_printf("\topmode %s", ieee80211_opmode_name[vap->iv_opmode]); #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) db_printf("(%p)", vap->iv_mesh); #endif db_printf(" state %s", ieee80211_state_name[vap->iv_state]); db_printf(" ifp %p(%s)", vap->iv_ifp, if_name(vap->iv_ifp)); db_printf("\n"); db_printf("\tic %p", vap->iv_ic); db_printf(" media %p", &vap->iv_media); db_printf(" bpf_if %p", vap->iv_rawbpf); db_printf(" mgtsend %p", &vap->iv_mgtsend); #if 0 struct sysctllog *iv_sysctl; /* dynamic sysctl context */ #endif db_printf("\n"); db_printf("\tdebug=%b\n", vap->iv_debug, IEEE80211_MSG_BITS); db_printf("\tflags=%b\n", vap->iv_flags, IEEE80211_F_BITS); db_printf("\tflags_ext=%b\n", vap->iv_flags_ext, IEEE80211_FEXT_BITS); db_printf("\tflags_ht=%b\n", vap->iv_flags_ht, IEEE80211_FHT_BITS); db_printf("\tflags_ven=%b\n", vap->iv_flags_ven, IEEE80211_FVEN_BITS); db_printf("\tcaps=%b\n", vap->iv_caps, IEEE80211_C_BITS); db_printf("\thtcaps=%b\n", vap->iv_htcaps, IEEE80211_C_HTCAP_BITS); - db_printf("\tvhtcaps=%b\n", vap->iv_vhtcaps, IEEE80211_VHTCAP_BITS); + db_printf("\tvhtcap=%b\n", vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_BITS); _db_show_stats(&vap->iv_stats); db_printf("\tinact_init %d", vap->iv_inact_init); db_printf(" inact_auth %d", vap->iv_inact_auth); db_printf(" inact_run %d", vap->iv_inact_run); db_printf(" inact_probe %d", vap->iv_inact_probe); db_printf("\n"); db_printf("\tdes_nssid %d", vap->iv_des_nssid); if (vap->iv_des_nssid) _db_show_ssid(" des_ssid[%u] ", 0, vap->iv_des_ssid[0].len, vap->iv_des_ssid[0].ssid); db_printf(" des_bssid %s", ether_sprintf(vap->iv_des_bssid)); db_printf("\n"); db_printf("\tdes_mode %d", vap->iv_des_mode); _db_show_channel(" des_chan", vap->iv_des_chan); db_printf("\n"); #if 0 int iv_nicknamelen; /* XXX junk */ uint8_t iv_nickname[IEEE80211_NWID_LEN]; #endif db_printf("\tbgscanidle %u", vap->iv_bgscanidle); db_printf(" bgscanintvl %u", vap->iv_bgscanintvl); db_printf(" scanvalid %u", vap->iv_scanvalid); db_printf("\n"); db_printf("\tscanreq_duration %u", vap->iv_scanreq_duration); db_printf(" scanreq_mindwell %u", vap->iv_scanreq_mindwell); db_printf(" scanreq_maxdwell %u", vap->iv_scanreq_maxdwell); db_printf("\n"); db_printf("\tscanreq_flags 0x%x", vap->iv_scanreq_flags); db_printf(" scanreq_nssid %d", vap->iv_scanreq_nssid); for (i = 0; i < vap->iv_scanreq_nssid; i++) _db_show_ssid(" scanreq_ssid[%u]", i, vap->iv_scanreq_ssid[i].len, vap->iv_scanreq_ssid[i].ssid); db_printf(" roaming %d", vap->iv_roaming); db_printf("\n"); for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) if (isset(ic->ic_modecaps, i)) { _db_show_roamparams("\troamparms[%s]", ieee80211_phymode_name[i], &vap->iv_roamparms[i]); db_printf("\n"); } db_printf("\tbmissthreshold %u", vap->iv_bmissthreshold); db_printf(" bmiss_max %u", vap->iv_bmiss_count); db_printf(" bmiss_max %d", vap->iv_bmiss_max); db_printf("\n"); db_printf("\tswbmiss_count %u", vap->iv_swbmiss_count); db_printf(" swbmiss_period %u", vap->iv_swbmiss_period); db_printf(" swbmiss %p", &vap->iv_swbmiss); db_printf("\n"); db_printf("\tampdu_rxmax %d", vap->iv_ampdu_rxmax); db_printf(" ampdu_density %d", vap->iv_ampdu_density); db_printf(" ampdu_limit %d", vap->iv_ampdu_limit); db_printf(" amsdu_limit %d", vap->iv_amsdu_limit); db_printf("\n"); db_printf("\tmax_aid %u", vap->iv_max_aid); db_printf(" aid_bitmap %p", vap->iv_aid_bitmap); db_printf("\n"); db_printf("\tsta_assoc %u", vap->iv_sta_assoc); db_printf(" ps_sta %u", vap->iv_ps_sta); db_printf(" ps_pending %u", vap->iv_ps_pending); db_printf(" tim_len %u", vap->iv_tim_len); db_printf(" tim_bitmap %p", vap->iv_tim_bitmap); db_printf("\n"); db_printf("\tdtim_period %u", vap->iv_dtim_period); db_printf(" dtim_count %u", vap->iv_dtim_count); db_printf(" set_tim %p", vap->iv_set_tim); db_printf(" csa_count %d", vap->iv_csa_count); db_printf("\n"); db_printf("\trtsthreshold %u", vap->iv_rtsthreshold); db_printf(" fragthreshold %u", vap->iv_fragthreshold); db_printf(" inact_timer %d", vap->iv_inact_timer); db_printf("\n"); for (i = IEEE80211_MODE_11A; i < IEEE80211_MODE_MAX; i++) if (isset(ic->ic_modecaps, i)) { _db_show_txparams("\ttxparms[%s]", ieee80211_phymode_name[i], &vap->iv_txparms[i]); db_printf("\n"); } /* application-specified IE's to attach to mgt frames */ _db_show_appie("\tappie_beacon", vap->iv_appie_beacon); _db_show_appie("\tappie_probereq", vap->iv_appie_probereq); _db_show_appie("\tappie_proberesp", vap->iv_appie_proberesp); _db_show_appie("\tappie_assocreq", vap->iv_appie_assocreq); _db_show_appie("\tappie_asscoresp", vap->iv_appie_assocresp); _db_show_appie("\tappie_wpa", vap->iv_appie_wpa); if (vap->iv_wpa_ie != NULL || vap->iv_rsn_ie != NULL) { if (vap->iv_wpa_ie != NULL) db_printf("\twpa_ie %p", vap->iv_wpa_ie); if (vap->iv_rsn_ie != NULL) db_printf("\trsn_ie %p", vap->iv_rsn_ie); db_printf("\n"); } db_printf("\tmax_keyix %u", vap->iv_max_keyix); db_printf(" def_txkey %d", vap->iv_def_txkey); db_printf("\n"); for (i = 0; i < IEEE80211_WEP_NKID; i++) _db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]); db_printf("\tauth %p(%s)", vap->iv_auth, vap->iv_auth->ia_name); db_printf(" ec %p", vap->iv_ec); db_printf(" acl %p", vap->iv_acl); db_printf(" as %p", vap->iv_as); db_printf("\n"); #ifdef IEEE80211_SUPPORT_MESH if (showmesh && vap->iv_mesh != NULL) _db_show_mesh(vap->iv_mesh); #endif #ifdef IEEE80211_SUPPORT_TDMA if (vap->iv_tdma != NULL) _db_show_tdma("\t", vap->iv_tdma, showprocs); #endif /* IEEE80211_SUPPORT_TDMA */ db_printf("\tsta_assoc %u", vap->iv_sta_assoc); db_printf(" ht_sta_assoc %u", vap->iv_ht_sta_assoc); db_printf(" ht40_sta_assoc %u", vap->iv_ht40_sta_assoc); db_printf("\n"); db_printf("\tnonerpsta %u", vap->iv_nonerpsta); db_printf(" longslotsta %u", vap->iv_longslotsta); db_printf(" lastnonerp %d", vap->iv_lastnonerp); db_printf(" lastnonht %d", vap->iv_lastnonht); db_printf("\n"); if (vap->iv_rate != NULL) _db_show_rate(vap->iv_rate, vap->iv_rs, showprocs); if (showprocs) { DB_PRINTSYM("\t", "iv_key_alloc", vap->iv_key_alloc); DB_PRINTSYM("\t", "iv_key_delete", vap->iv_key_delete); DB_PRINTSYM("\t", "iv_key_set", vap->iv_key_set); DB_PRINTSYM("\t", "iv_key_update_begin", vap->iv_key_update_begin); DB_PRINTSYM("\t", "iv_key_update_end", vap->iv_key_update_end); DB_PRINTSYM("\t", "iv_opdetach", vap->iv_opdetach); DB_PRINTSYM("\t", "iv_input", vap->iv_input); DB_PRINTSYM("\t", "iv_recv_mgmt", vap->iv_recv_mgmt); DB_PRINTSYM("\t", "iv_deliver_data", vap->iv_deliver_data); DB_PRINTSYM("\t", "iv_bmiss", vap->iv_bmiss); DB_PRINTSYM("\t", "iv_reset", vap->iv_reset); DB_PRINTSYM("\t", "iv_update_beacon", vap->iv_update_beacon); DB_PRINTSYM("\t", "iv_newstate", vap->iv_newstate); DB_PRINTSYM("\t", "iv_output", vap->iv_output); } } static void _db_show_com(const struct ieee80211com *ic, int showvaps, int showsta, int showmesh, int showprocs, int showscan) { struct ieee80211vap *vap; db_printf("COM: %p:", ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) db_printf(" %s(%p)", if_name(vap->iv_ifp), vap); db_printf("\n"); db_printf("\tsoftc %p", ic->ic_softc); db_printf("\tname %s", ic->ic_name); db_printf(" comlock %p", &ic->ic_comlock); db_printf(" txlock %p", &ic->ic_txlock); db_printf(" fflock %p", &ic->ic_fflock); db_printf("\n"); db_printf("\theadroom %d", ic->ic_headroom); db_printf(" phytype %d", ic->ic_phytype); db_printf(" opmode %s", ieee80211_opmode_name[ic->ic_opmode]); db_printf("\n"); db_printf("\tinact %p", &ic->ic_inact); db_printf("\n"); db_printf("\tflags=%b\n", ic->ic_flags, IEEE80211_F_BITS); db_printf("\tflags_ext=%b\n", ic->ic_flags_ext, IEEE80211_FEXT_BITS); db_printf("\tflags_ht=%b\n", ic->ic_flags_ht, IEEE80211_FHT_BITS); db_printf("\tflags_ven=%b\n", ic->ic_flags_ven, IEEE80211_FVEN_BITS); db_printf("\tcaps=%b\n", ic->ic_caps, IEEE80211_C_BITS); db_printf("\tcryptocaps=%b\n", ic->ic_cryptocaps, IEEE80211_CRYPTO_BITS); db_printf("\thtcaps=%b\n", ic->ic_htcaps, IEEE80211_HTCAP_BITS); db_printf("\tvhtcaps=%b\n", ic->ic_vhtcaps, IEEE80211_VHTCAP_BITS); #if 0 uint8_t ic_modecaps[2]; /* set of mode capabilities */ #endif db_printf("\tcurmode %u", ic->ic_curmode); db_printf(" promisc %u", ic->ic_promisc); db_printf(" allmulti %u", ic->ic_allmulti); db_printf(" nrunning %u", ic->ic_nrunning); db_printf("\n"); db_printf("\tbintval %u", ic->ic_bintval); db_printf(" lintval %u", ic->ic_lintval); db_printf(" holdover %u", ic->ic_holdover); db_printf(" txpowlimit %u", ic->ic_txpowlimit); db_printf("\n"); #if 0 struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; #endif /* * Channel state: * * ic_channels is the set of available channels for the device; * it is setup by the driver * ic_nchans is the number of valid entries in ic_channels * ic_chan_avail is a bit vector of these channels used to check * whether a channel is available w/o searching the channel table. * ic_chan_active is a (potentially) constrained subset of * ic_chan_avail that reflects any mode setting or user-specified * limit on the set of channels to use/scan * ic_curchan is the current channel the device is set to; it may * be different from ic_bsschan when we are off-channel scanning * or otherwise doing background work * ic_bsschan is the channel selected for operation; it may * be undefined (IEEE80211_CHAN_ANYC) * ic_prevchan is a cached ``previous channel'' used to optimize * lookups when switching back+forth between two channels * (e.g. for dynamic turbo) */ db_printf("\tnchans %d", ic->ic_nchans); #if 0 struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX]; uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; uint8_t ic_chan_active[IEEE80211_CHAN_BYTES]; uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; #endif db_printf("\n"); _db_show_channel("\tcurchan", ic->ic_curchan); db_printf("\n"); _db_show_channel("\tbsschan", ic->ic_bsschan); db_printf("\n"); _db_show_channel("\tprevchan", ic->ic_prevchan); db_printf("\n"); db_printf("\tregdomain %p", &ic->ic_regdomain); db_printf("\n"); _db_show_channel("\tcsa_newchan", ic->ic_csa_newchan); db_printf(" csa_count %d", ic->ic_csa_count); db_printf( "dfs %p", &ic->ic_dfs); db_printf("\n"); db_printf("\tscan %p", ic->ic_scan); db_printf(" lastdata %d", ic->ic_lastdata); db_printf(" lastscan %d", ic->ic_lastscan); db_printf("\n"); db_printf("\tmax_keyix %d", ic->ic_max_keyix); db_printf(" hash_key 0x%x", ic->ic_hash_key); db_printf(" wme %p", &ic->ic_wme); if (!showsta) db_printf(" sta %p", &ic->ic_sta); db_printf("\n"); db_printf("\tstageq@%p:\n", &ic->ic_stageq); _db_show_ageq("\t", &ic->ic_stageq); if (showsta) _db_show_node_table("\t", &ic->ic_sta); db_printf("\tprotmode %d", ic->ic_protmode); db_printf("\tcurhtprotmode 0x%x", ic->ic_curhtprotmode); db_printf(" htprotmode %d", ic->ic_htprotmode); db_printf("\n"); db_printf("\tsuperg %p\n", ic->ic_superg); db_printf("\tmontaps %d th %p txchan %p rh %p rxchan %p\n", ic->ic_montaps, ic->ic_th, ic->ic_txchan, ic->ic_rh, ic->ic_rxchan); if (showprocs) { DB_PRINTSYM("\t", "ic_vap_create", ic->ic_vap_create); DB_PRINTSYM("\t", "ic_vap_delete", ic->ic_vap_delete); #if 0 /* operating mode attachment */ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX]; #endif DB_PRINTSYM("\t", "ic_newassoc", ic->ic_newassoc); DB_PRINTSYM("\t", "ic_getradiocaps", ic->ic_getradiocaps); DB_PRINTSYM("\t", "ic_setregdomain", ic->ic_setregdomain); DB_PRINTSYM("\t", "ic_send_mgmt", ic->ic_send_mgmt); DB_PRINTSYM("\t", "ic_raw_xmit", ic->ic_raw_xmit); DB_PRINTSYM("\t", "ic_updateslot", ic->ic_updateslot); DB_PRINTSYM("\t", "ic_update_mcast", ic->ic_update_mcast); DB_PRINTSYM("\t", "ic_update_promisc", ic->ic_update_promisc); DB_PRINTSYM("\t", "ic_node_alloc", ic->ic_node_alloc); DB_PRINTSYM("\t", "ic_node_free", ic->ic_node_free); DB_PRINTSYM("\t", "ic_node_cleanup", ic->ic_node_cleanup); DB_PRINTSYM("\t", "ic_node_getrssi", ic->ic_node_getrssi); DB_PRINTSYM("\t", "ic_node_getsignal", ic->ic_node_getsignal); DB_PRINTSYM("\t", "ic_node_getmimoinfo", ic->ic_node_getmimoinfo); DB_PRINTSYM("\t", "ic_scan_start", ic->ic_scan_start); DB_PRINTSYM("\t", "ic_scan_end", ic->ic_scan_end); DB_PRINTSYM("\t", "ic_set_channel", ic->ic_set_channel); DB_PRINTSYM("\t", "ic_scan_curchan", ic->ic_scan_curchan); DB_PRINTSYM("\t", "ic_scan_mindwell", ic->ic_scan_mindwell); DB_PRINTSYM("\t", "ic_recv_action", ic->ic_recv_action); DB_PRINTSYM("\t", "ic_send_action", ic->ic_send_action); DB_PRINTSYM("\t", "ic_addba_request", ic->ic_addba_request); DB_PRINTSYM("\t", "ic_addba_response", ic->ic_addba_response); DB_PRINTSYM("\t", "ic_addba_stop", ic->ic_addba_stop); } if (showscan) { db_printf("\n"); _db_show_scan(ic->ic_scan, showprocs); } if (showvaps && !TAILQ_EMPTY(&ic->ic_vaps)) { db_printf("\n"); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) _db_show_vap(vap, showmesh, showprocs); } if (showsta && !TAILQ_EMPTY(&ic->ic_sta.nt_node)) { const struct ieee80211_node_table *nt = &ic->ic_sta; const struct ieee80211_node *ni; TAILQ_FOREACH(ni, &nt->nt_node, ni_list) { db_printf("\n"); _db_show_sta(ni); } } } static void _db_show_all_vaps(void *arg, struct ieee80211com *ic) { int showall = *(int *)arg; if (!showall) { const struct ieee80211vap *vap; db_printf("%s: com %p vaps:", ic->ic_name, ic); TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) db_printf(" %s(%p)", if_name(vap->iv_ifp), vap); db_printf("\n"); } else _db_show_com(ic, 1, 1, 1, 1, 1); } static void _db_show_node_table(const char *tag, const struct ieee80211_node_table *nt) { int i; db_printf("%s%s@%p:\n", tag, nt->nt_name, nt); db_printf("%s nodelock %p", tag, &nt->nt_nodelock); db_printf(" inact_init %d", nt->nt_inact_init); db_printf("%s keyixmax %d keyixmap %p\n", tag, nt->nt_keyixmax, nt->nt_keyixmap); for (i = 0; i < nt->nt_keyixmax; i++) { const struct ieee80211_node *ni = nt->nt_keyixmap[i]; if (ni != NULL) db_printf("%s [%3u] %p %s\n", tag, i, ni, ether_sprintf(ni->ni_macaddr)); } } static void _db_show_channel(const char *tag, const struct ieee80211_channel *c) { db_printf("%s ", tag); if (c == NULL) db_printf(""); else if (c == IEEE80211_CHAN_ANYC) db_printf(""); else db_printf("[%u (%u) flags=%b maxreg %d maxpow %d minpow %d state 0x%x extieee %u]", c->ic_freq, c->ic_ieee, c->ic_flags, IEEE80211_CHAN_BITS, c->ic_maxregpower, c->ic_maxpower, c->ic_minpower, c->ic_state, c->ic_extieee); } static void _db_show_ssid(const char *tag, int ix, int len, const uint8_t *ssid) { const uint8_t *p; int i; db_printf(tag, ix); if (len > IEEE80211_NWID_LEN) len = IEEE80211_NWID_LEN; /* determine printable or not */ for (i = 0, p = ssid; i < len; i++, p++) { if (*p < ' ' || *p > 0x7e) break; } if (i == len) { db_printf("\""); for (i = 0, p = ssid; i < len; i++, p++) db_printf("%c", *p); db_printf("\""); } else { db_printf("0x"); for (i = 0, p = ssid; i < len; i++, p++) db_printf("%02x", *p); } } static void _db_show_appie(const char *tag, const struct ieee80211_appie *ie) { const uint8_t *p; int i; if (ie == NULL) return; db_printf("%s [0x", tag); for (i = 0, p = ie->ie_data; i < ie->ie_len; i++, p++) db_printf("%02x", *p); db_printf("]\n"); } static void _db_show_key(const char *tag, int ix, const struct ieee80211_key *wk) { static const uint8_t zerodata[IEEE80211_KEYBUF_SIZE]; const struct ieee80211_cipher *cip = wk->wk_cipher; int keylen = wk->wk_keylen; db_printf(tag, ix); switch (cip->ic_cipher) { case IEEE80211_CIPHER_WEP: /* compatibility */ db_printf(" wepkey %u:%s", wk->wk_keyix, keylen <= 5 ? "40-bit" : keylen <= 13 ? "104-bit" : "128-bit"); break; case IEEE80211_CIPHER_TKIP: if (keylen > 128/8) keylen -= 128/8; /* ignore MIC for now */ db_printf(" TKIP %u:%u-bit", wk->wk_keyix, 8*keylen); break; case IEEE80211_CIPHER_AES_OCB: db_printf(" AES-OCB %u:%u-bit", wk->wk_keyix, 8*keylen); break; case IEEE80211_CIPHER_AES_CCM: db_printf(" AES-CCM %u:%u-bit", wk->wk_keyix, 8*keylen); break; case IEEE80211_CIPHER_CKIP: db_printf(" CKIP %u:%u-bit", wk->wk_keyix, 8*keylen); break; case IEEE80211_CIPHER_NONE: db_printf(" NULL %u:%u-bit", wk->wk_keyix, 8*keylen); break; default: db_printf(" UNKNOWN (0x%x) %u:%u-bit", cip->ic_cipher, wk->wk_keyix, 8*keylen); break; } if (wk->wk_rxkeyix != wk->wk_keyix) db_printf(" rxkeyix %u", wk->wk_rxkeyix); if (memcmp(wk->wk_key, zerodata, keylen) != 0) { int i; db_printf(" <"); for (i = 0; i < keylen; i++) db_printf("%02x", wk->wk_key[i]); db_printf(">"); if (cip->ic_cipher != IEEE80211_CIPHER_WEP && wk->wk_keyrsc[IEEE80211_NONQOS_TID] != 0) db_printf(" rsc %ju", (uintmax_t)wk->wk_keyrsc[IEEE80211_NONQOS_TID]); if (cip->ic_cipher != IEEE80211_CIPHER_WEP && wk->wk_keytsc != 0) db_printf(" tsc %ju", (uintmax_t)wk->wk_keytsc); db_printf(" flags=%b", wk->wk_flags, IEEE80211_KEY_BITS); } db_printf("\n"); } static void printrate(const char *tag, int v) { if (v == IEEE80211_FIXED_RATE_NONE) db_printf(" %s ", tag); else if (v == 11) db_printf(" %s 5.5", tag); else if (v & IEEE80211_RATE_MCS) db_printf(" %s MCS%d", tag, v &~ IEEE80211_RATE_MCS); else db_printf(" %s %d", tag, v/2); } static void _db_show_roamparams(const char *tag, const void *arg, const struct ieee80211_roamparam *rp) { db_printf(tag, arg); if (rp->rssi & 1) db_printf(" rssi %u.5", rp->rssi/2); else db_printf(" rssi %u", rp->rssi/2); printrate("rate", rp->rate); } static void _db_show_txparams(const char *tag, const void *arg, const struct ieee80211_txparam *tp) { db_printf(tag, arg); printrate("ucastrate", tp->ucastrate); printrate("mcastrate", tp->mcastrate); printrate("mgmtrate", tp->mgmtrate); db_printf(" maxretry %d", tp->maxretry); } static void _db_show_ageq(const char *tag, const struct ieee80211_ageq *q) { const struct mbuf *m; db_printf("%s lock %p len %d maxlen %d drops %d head %p tail %p\n", tag, &q->aq_lock, q->aq_len, q->aq_maxlen, q->aq_drops, q->aq_head, q->aq_tail); for (m = q->aq_head; m != NULL; m = m->m_nextpkt) db_printf("%s %p (len %d, %b)\n", tag, m, m->m_len, /* XXX could be either TX or RX but is mostly TX */ m->m_flags, IEEE80211_MBUF_TX_FLAG_BITS); } static void _db_show_stats(const struct ieee80211_stats *is) { } #ifdef IEEE80211_SUPPORT_MESH static void _db_show_mesh(const struct ieee80211_mesh_state *ms) { struct ieee80211_mesh_route *rt; int i; _db_show_ssid(" meshid ", 0, ms->ms_idlen, ms->ms_id); db_printf("nextseq %u ttl %u flags 0x%x\n", ms->ms_seq, ms->ms_ttl, ms->ms_flags); db_printf("routing table:\n"); i = 0; TAILQ_FOREACH(rt, &ms->ms_routes, rt_next) { db_printf("entry %d:\tdest: %6D nexthop: %6D metric: %u", i, rt->rt_dest, ":", rt->rt_nexthop, ":", rt->rt_metric); db_printf("\tlifetime: %u lastseq: %u priv: %p\n", ieee80211_mesh_rt_update(rt, 0), rt->rt_lastmseq, rt->rt_priv); i++; } } #endif /* IEEE80211_SUPPORT_MESH */ #endif /* DDB */ diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 34d330f0dd09..61c57b87c5d7 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -1,3610 +1,3610 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2007-2008 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifdef __FreeBSD__ #endif /* * IEEE 802.11n protocol support. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include const struct ieee80211_mcs_rates ieee80211_htrates[IEEE80211_HTRATE_MAXSIZE] = { { 13, 14, 27, 30 }, /* MCS 0 */ { 26, 29, 54, 60 }, /* MCS 1 */ { 39, 43, 81, 90 }, /* MCS 2 */ { 52, 58, 108, 120 }, /* MCS 3 */ { 78, 87, 162, 180 }, /* MCS 4 */ { 104, 116, 216, 240 }, /* MCS 5 */ { 117, 130, 243, 270 }, /* MCS 6 */ { 130, 144, 270, 300 }, /* MCS 7 */ { 26, 29, 54, 60 }, /* MCS 8 */ { 52, 58, 108, 120 }, /* MCS 9 */ { 78, 87, 162, 180 }, /* MCS 10 */ { 104, 116, 216, 240 }, /* MCS 11 */ { 156, 173, 324, 360 }, /* MCS 12 */ { 208, 231, 432, 480 }, /* MCS 13 */ { 234, 260, 486, 540 }, /* MCS 14 */ { 260, 289, 540, 600 }, /* MCS 15 */ { 39, 43, 81, 90 }, /* MCS 16 */ { 78, 87, 162, 180 }, /* MCS 17 */ { 117, 130, 243, 270 }, /* MCS 18 */ { 156, 173, 324, 360 }, /* MCS 19 */ { 234, 260, 486, 540 }, /* MCS 20 */ { 312, 347, 648, 720 }, /* MCS 21 */ { 351, 390, 729, 810 }, /* MCS 22 */ { 390, 433, 810, 900 }, /* MCS 23 */ { 52, 58, 108, 120 }, /* MCS 24 */ { 104, 116, 216, 240 }, /* MCS 25 */ { 156, 173, 324, 360 }, /* MCS 26 */ { 208, 231, 432, 480 }, /* MCS 27 */ { 312, 347, 648, 720 }, /* MCS 28 */ { 416, 462, 864, 960 }, /* MCS 29 */ { 468, 520, 972, 1080 }, /* MCS 30 */ { 520, 578, 1080, 1200 }, /* MCS 31 */ { 0, 0, 12, 13 }, /* MCS 32 */ { 78, 87, 162, 180 }, /* MCS 33 */ { 104, 116, 216, 240 }, /* MCS 34 */ { 130, 144, 270, 300 }, /* MCS 35 */ { 117, 130, 243, 270 }, /* MCS 36 */ { 156, 173, 324, 360 }, /* MCS 37 */ { 195, 217, 405, 450 }, /* MCS 38 */ { 104, 116, 216, 240 }, /* MCS 39 */ { 130, 144, 270, 300 }, /* MCS 40 */ { 130, 144, 270, 300 }, /* MCS 41 */ { 156, 173, 324, 360 }, /* MCS 42 */ { 182, 202, 378, 420 }, /* MCS 43 */ { 182, 202, 378, 420 }, /* MCS 44 */ { 208, 231, 432, 480 }, /* MCS 45 */ { 156, 173, 324, 360 }, /* MCS 46 */ { 195, 217, 405, 450 }, /* MCS 47 */ { 195, 217, 405, 450 }, /* MCS 48 */ { 234, 260, 486, 540 }, /* MCS 49 */ { 273, 303, 567, 630 }, /* MCS 50 */ { 273, 303, 567, 630 }, /* MCS 51 */ { 312, 347, 648, 720 }, /* MCS 52 */ { 130, 144, 270, 300 }, /* MCS 53 */ { 156, 173, 324, 360 }, /* MCS 54 */ { 182, 202, 378, 420 }, /* MCS 55 */ { 156, 173, 324, 360 }, /* MCS 56 */ { 182, 202, 378, 420 }, /* MCS 57 */ { 208, 231, 432, 480 }, /* MCS 58 */ { 234, 260, 486, 540 }, /* MCS 59 */ { 208, 231, 432, 480 }, /* MCS 60 */ { 234, 260, 486, 540 }, /* MCS 61 */ { 260, 289, 540, 600 }, /* MCS 62 */ { 260, 289, 540, 600 }, /* MCS 63 */ { 286, 318, 594, 660 }, /* MCS 64 */ { 195, 217, 405, 450 }, /* MCS 65 */ { 234, 260, 486, 540 }, /* MCS 66 */ { 273, 303, 567, 630 }, /* MCS 67 */ { 234, 260, 486, 540 }, /* MCS 68 */ { 273, 303, 567, 630 }, /* MCS 69 */ { 312, 347, 648, 720 }, /* MCS 70 */ { 351, 390, 729, 810 }, /* MCS 71 */ { 312, 347, 648, 720 }, /* MCS 72 */ { 351, 390, 729, 810 }, /* MCS 73 */ { 390, 433, 810, 900 }, /* MCS 74 */ { 390, 433, 810, 900 }, /* MCS 75 */ { 429, 477, 891, 990 }, /* MCS 76 */ }; static int ieee80211_ampdu_age = -1; /* threshold for ampdu reorder q (ms) */ SYSCTL_PROC(_net_wlan, OID_AUTO, ampdu_age, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &ieee80211_ampdu_age, 0, ieee80211_sysctl_msecs_ticks, "I", "AMPDU max reorder age (ms)"); static int ieee80211_recv_bar_ena = 1; SYSCTL_INT(_net_wlan, OID_AUTO, recv_bar, CTLFLAG_RW, &ieee80211_recv_bar_ena, 0, "BAR frame processing (ena/dis)"); static int ieee80211_addba_timeout = -1;/* timeout for ADDBA response */ SYSCTL_PROC(_net_wlan, OID_AUTO, addba_timeout, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &ieee80211_addba_timeout, 0, ieee80211_sysctl_msecs_ticks, "I", "ADDBA request timeout (ms)"); static int ieee80211_addba_backoff = -1;/* backoff after max ADDBA requests */ SYSCTL_PROC(_net_wlan, OID_AUTO, addba_backoff, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &ieee80211_addba_backoff, 0, ieee80211_sysctl_msecs_ticks, "I", "ADDBA request backoff (ms)"); static int ieee80211_addba_maxtries = 3;/* max ADDBA requests before backoff */ SYSCTL_INT(_net_wlan, OID_AUTO, addba_maxtries, CTLFLAG_RW, &ieee80211_addba_maxtries, 0, "max ADDBA requests sent before backoff"); static int ieee80211_bar_timeout = -1; /* timeout waiting for BAR response */ static int ieee80211_bar_maxtries = 50;/* max BAR requests before DELBA */ static ieee80211_recv_action_func ht_recv_action_ba_addba_request; static ieee80211_recv_action_func ht_recv_action_ba_addba_response; static ieee80211_recv_action_func ht_recv_action_ba_delba; static ieee80211_recv_action_func ht_recv_action_ht_mimopwrsave; static ieee80211_recv_action_func ht_recv_action_ht_txchwidth; static ieee80211_send_action_func ht_send_action_ba_addba; static ieee80211_send_action_func ht_send_action_ba_delba; static ieee80211_send_action_func ht_send_action_ht_txchwidth; static void ieee80211_ht_init(void) { /* * Setup HT parameters that depends on the clock frequency. */ ieee80211_ampdu_age = msecs_to_ticks(500); ieee80211_addba_timeout = msecs_to_ticks(250); ieee80211_addba_backoff = msecs_to_ticks(10*1000); ieee80211_bar_timeout = msecs_to_ticks(250); /* * Register action frame handlers. */ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_recv_action_ba_addba_request); ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_recv_action_ba_addba_response); ieee80211_recv_action_register(IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_DELBA, ht_recv_action_ba_delba); ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, IEEE80211_ACTION_HT_MIMOPWRSAVE, ht_recv_action_ht_mimopwrsave); ieee80211_recv_action_register(IEEE80211_ACTION_CAT_HT, IEEE80211_ACTION_HT_TXCHWIDTH, ht_recv_action_ht_txchwidth); ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_REQUEST, ht_send_action_ba_addba); ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_RESPONSE, ht_send_action_ba_addba); ieee80211_send_action_register(IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_DELBA, ht_send_action_ba_delba); ieee80211_send_action_register(IEEE80211_ACTION_CAT_HT, IEEE80211_ACTION_HT_TXCHWIDTH, ht_send_action_ht_txchwidth); } SYSINIT(wlan_ht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_ht_init, NULL); static int ieee80211_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); static int ieee80211_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout); static int ieee80211_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int code, int baparamset, int batimeout); static void ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); static void null_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap); static void ieee80211_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status); static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap); static void bar_stop_timer(struct ieee80211_tx_ampdu *tap); static int ampdu_rx_start(struct ieee80211_node *, struct ieee80211_rx_ampdu *, int baparamset, int batimeout, int baseqctl); static void ampdu_rx_stop(struct ieee80211_node *, struct ieee80211_rx_ampdu *); void ieee80211_ht_attach(struct ieee80211com *ic) { /* setup default aggregation policy */ ic->ic_recv_action = ieee80211_recv_action; ic->ic_send_action = ieee80211_send_action; ic->ic_ampdu_enable = ieee80211_ampdu_enable; ic->ic_addba_request = ieee80211_addba_request; ic->ic_addba_response = ieee80211_addba_response; ic->ic_addba_response_timeout = null_addba_response_timeout; ic->ic_addba_stop = ieee80211_addba_stop; ic->ic_bar_response = ieee80211_bar_response; ic->ic_ampdu_rx_start = ampdu_rx_start; ic->ic_ampdu_rx_stop = ampdu_rx_stop; ic->ic_htprotmode = IEEE80211_PROT_RTSCTS; ic->ic_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; } void ieee80211_ht_detach(struct ieee80211com *ic) { } void ieee80211_ht_vattach(struct ieee80211vap *vap) { /* driver can override defaults */ vap->iv_ampdu_rxmax = IEEE80211_HTCAP_MAXRXAMPDU_8K; vap->iv_ampdu_density = IEEE80211_HTCAP_MPDUDENSITY_NA; vap->iv_ampdu_limit = vap->iv_ampdu_rxmax; vap->iv_amsdu_limit = vap->iv_htcaps & IEEE80211_HTCAP_MAXAMSDU; /* tx aggregation traffic thresholds */ vap->iv_ampdu_mintraffic[WME_AC_BK] = 128; vap->iv_ampdu_mintraffic[WME_AC_BE] = 64; vap->iv_ampdu_mintraffic[WME_AC_VO] = 32; vap->iv_ampdu_mintraffic[WME_AC_VI] = 32; vap->iv_htprotmode = IEEE80211_PROT_RTSCTS; vap->iv_curhtprotmode = IEEE80211_HTINFO_OPMODE_PURE; if (vap->iv_htcaps & IEEE80211_HTC_HT) { /* * Device is HT capable; enable all HT-related * facilities by default. * XXX these choices may be too aggressive. */ vap->iv_flags_ht |= IEEE80211_FHT_HT | IEEE80211_FHT_HTCOMPAT ; if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI20) vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI20; /* XXX infer from channel list? */ if (vap->iv_htcaps & IEEE80211_HTCAP_CHWIDTH40) { vap->iv_flags_ht |= IEEE80211_FHT_USEHT40; if (vap->iv_htcaps & IEEE80211_HTCAP_SHORTGI40) vap->iv_flags_ht |= IEEE80211_FHT_SHORTGI40; } /* enable RIFS if capable */ if (vap->iv_htcaps & IEEE80211_HTC_RIFS) vap->iv_flags_ht |= IEEE80211_FHT_RIFS; /* NB: A-MPDU and A-MSDU rx are mandated, these are tx only */ vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_RX; if (vap->iv_htcaps & IEEE80211_HTC_AMPDU) vap->iv_flags_ht |= IEEE80211_FHT_AMPDU_TX; vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_RX; if (vap->iv_htcaps & IEEE80211_HTC_AMSDU) vap->iv_flags_ht |= IEEE80211_FHT_AMSDU_TX; if (vap->iv_htcaps & IEEE80211_HTCAP_TXSTBC) vap->iv_flags_ht |= IEEE80211_FHT_STBC_TX; if (vap->iv_htcaps & IEEE80211_HTCAP_RXSTBC) vap->iv_flags_ht |= IEEE80211_FHT_STBC_RX; if (vap->iv_htcaps & IEEE80211_HTCAP_LDPC) vap->iv_flags_ht |= IEEE80211_FHT_LDPC_RX; if (vap->iv_htcaps & IEEE80211_HTC_TXLDPC) vap->iv_flags_ht |= IEEE80211_FHT_LDPC_TX; } /* NB: disable default legacy WDS, too many issues right now */ if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY) vap->iv_flags_ht &= ~IEEE80211_FHT_HT; } void ieee80211_ht_vdetach(struct ieee80211vap *vap) { } static int ht_getrate(struct ieee80211com *ic, int index, enum ieee80211_phymode mode, int ratetype) { int mword, rate; mword = ieee80211_rate2media(ic, index | IEEE80211_RATE_MCS, mode); if (IFM_SUBTYPE(mword) != IFM_IEEE80211_MCS) return (0); switch (ratetype) { case 0: rate = ieee80211_htrates[index].ht20_rate_800ns; break; case 1: rate = ieee80211_htrates[index].ht20_rate_400ns; break; case 2: rate = ieee80211_htrates[index].ht40_rate_800ns; break; default: rate = ieee80211_htrates[index].ht40_rate_400ns; break; } return (rate); } static struct printranges { int minmcs; int maxmcs; int txstream; int ratetype; int htcapflags; } ranges[] = { { 0, 7, 1, 0, 0 }, { 8, 15, 2, 0, 0 }, { 16, 23, 3, 0, 0 }, { 24, 31, 4, 0, 0 }, { 32, 0, 1, 2, IEEE80211_HTC_TXMCS32 }, { 33, 38, 2, 0, IEEE80211_HTC_TXUNEQUAL }, { 39, 52, 3, 0, IEEE80211_HTC_TXUNEQUAL }, { 53, 76, 4, 0, IEEE80211_HTC_TXUNEQUAL }, { 0, 0, 0, 0, 0 }, }; static void ht_rateprint(struct ieee80211com *ic, enum ieee80211_phymode mode, int ratetype) { int minrate, maxrate; struct printranges *range; for (range = ranges; range->txstream != 0; range++) { if (ic->ic_txstream < range->txstream) continue; if (range->htcapflags && (ic->ic_htcaps & range->htcapflags) == 0) continue; if (ratetype < range->ratetype) continue; minrate = ht_getrate(ic, range->minmcs, mode, ratetype); maxrate = ht_getrate(ic, range->maxmcs, mode, ratetype); if (range->maxmcs) { ic_printf(ic, "MCS %d-%d: %d%sMbps - %d%sMbps\n", range->minmcs, range->maxmcs, minrate/2, ((minrate & 0x1) != 0 ? ".5" : ""), maxrate/2, ((maxrate & 0x1) != 0 ? ".5" : "")); } else { ic_printf(ic, "MCS %d: %d%sMbps\n", range->minmcs, minrate/2, ((minrate & 0x1) != 0 ? ".5" : "")); } } } static void ht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode) { const char *modestr = ieee80211_phymode_name[mode]; ic_printf(ic, "%s MCS 20MHz\n", modestr); ht_rateprint(ic, mode, 0); if (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI20) { ic_printf(ic, "%s MCS 20MHz SGI\n", modestr); ht_rateprint(ic, mode, 1); } if (ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) { ic_printf(ic, "%s MCS 40MHz:\n", modestr); ht_rateprint(ic, mode, 2); } if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && (ic->ic_htcaps & IEEE80211_HTCAP_SHORTGI40)) { ic_printf(ic, "%s MCS 40MHz SGI:\n", modestr); ht_rateprint(ic, mode, 3); } } void ieee80211_ht_announce(struct ieee80211com *ic) { if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA) || isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) ic_printf(ic, "%dT%dR\n", ic->ic_txstream, ic->ic_rxstream); if (isset(ic->ic_modecaps, IEEE80211_MODE_11NA)) ht_announce(ic, IEEE80211_MODE_11NA); if (isset(ic->ic_modecaps, IEEE80211_MODE_11NG)) ht_announce(ic, IEEE80211_MODE_11NG); } void ieee80211_init_suphtrates(struct ieee80211com *ic) { #define ADDRATE(x) do { \ htrateset->rs_rates[htrateset->rs_nrates] = x; \ htrateset->rs_nrates++; \ } while (0) struct ieee80211_htrateset *htrateset = &ic->ic_sup_htrates; int i; memset(htrateset, 0, sizeof(struct ieee80211_htrateset)); for (i = 0; i < ic->ic_txstream * 8; i++) ADDRATE(i); if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && (ic->ic_htcaps & IEEE80211_HTC_TXMCS32)) ADDRATE(32); if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) { if (ic->ic_txstream >= 2) { for (i = 33; i <= 38; i++) ADDRATE(i); } if (ic->ic_txstream >= 3) { for (i = 39; i <= 52; i++) ADDRATE(i); } if (ic->ic_txstream == 4) { for (i = 53; i <= 76; i++) ADDRATE(i); } } #undef ADDRATE } /* * Receive processing. */ /* * Decap the encapsulated A-MSDU frames and dispatch all but * the last for delivery. The last frame is returned for * delivery via the normal path. */ struct mbuf * ieee80211_decap_amsdu(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211vap *vap = ni->ni_vap; int framelen; struct mbuf *n; /* discard 802.3 header inserted by ieee80211_decap */ m_adj(m, sizeof(struct ether_header)); vap->iv_stats.is_amsdu_decap++; for (;;) { /* * Decap the first frame, bust it apart from the * remainder and deliver. We leave the last frame * delivery to the caller (for consistency with other * code paths, could also do it here). */ m = ieee80211_decap1(m, &framelen); if (m == NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "a-msdu", "%s", "decap failed"); vap->iv_stats.is_amsdu_tooshort++; return NULL; } if (m->m_pkthdr.len == framelen) break; n = m_split(m, framelen, IEEE80211_M_NOWAIT); if (n == NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ANY, ni->ni_macaddr, "a-msdu", "%s", "unable to split encapsulated frames"); vap->iv_stats.is_amsdu_split++; m_freem(m); /* NB: must reclaim */ return NULL; } vap->iv_deliver_data(vap, ni, m); /* * Remove frame contents; each intermediate frame * is required to be aligned to a 4-byte boundary. */ m = n; m_adj(m, roundup2(framelen, 4) - framelen); /* padding */ } return m; /* last delivered by caller */ } static void ampdu_rx_purge_slot(struct ieee80211_rx_ampdu *rap, int i) { struct mbuf *m; /* Walk the queue, removing frames as appropriate */ while (mbufq_len(&rap->rxa_mq[i]) != 0) { m = mbufq_dequeue(&rap->rxa_mq[i]); if (m == NULL) break; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; m_freem(m); } } /* * Add the given frame to the current RX reorder slot. * * For future offloaded A-MSDU handling where multiple frames with * the same sequence number show up here, this routine will append * those frames as long as they're appropriately tagged. */ static int ampdu_rx_add_slot(struct ieee80211_rx_ampdu *rap, int off, int tid, ieee80211_seq rxseq, struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_rx_stats *rxs) { const struct ieee80211_rx_stats *rxs_final = NULL; struct ieee80211vap *vap = ni->ni_vap; int toss_dup; #define PROCESS 0 /* caller should process frame */ #define CONSUMED 1 /* frame consumed, caller does nothing */ /* * Figure out if this is a duplicate frame for the given slot. * * We're assuming that the driver will hand us all the frames * for a given AMSDU decap pass and if we get /a/ frame * for an AMSDU decap then we'll get all of them. * * The tricksy bit is that we don't know when the /end/ of * the decap pass is, because we aren't tracking state here * per-slot to know that we've finished receiving the frame list. * * The driver sets RX_F_AMSDU and RX_F_AMSDU_MORE to tell us * what's going on; so ideally we'd just check the frame at the * end of the reassembly slot to see if its F_AMSDU w/ no F_AMSDU_MORE - * that means we've received the whole AMSDU decap pass. */ /* * Get the rxs of the final mbuf in the slot, if one exists. */ if (mbufq_len(&rap->rxa_mq[off]) != 0) { rxs_final = ieee80211_get_rx_params_ptr(mbufq_last(&rap->rxa_mq[off])); } /* Default to tossing the duplicate frame */ toss_dup = 1; /* * Check to see if the final frame has F_AMSDU and F_AMSDU set, AND * this frame has F_AMSDU set (MORE or otherwise.) That's a sign * that more can come. */ if ((rxs != NULL) && (rxs_final != NULL) && ieee80211_check_rxseq_amsdu(rxs) && ieee80211_check_rxseq_amsdu(rxs_final)) { if (! ieee80211_check_rxseq_amsdu_more(rxs_final)) { /* * amsdu_more() returning 0 means "it's not the * final frame" so we can append more * frames here. */ toss_dup = 0; } } /* * If the list is empty OR we have determined we can put more * driver decap'ed AMSDU frames in here, then insert. */ if ((mbufq_len(&rap->rxa_mq[off]) == 0) || (toss_dup == 0)) { if (mbufq_enqueue(&rap->rxa_mq[off], m) != 0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "a-mpdu queue fail", "seqno %u tid %u BA win <%u:%u> off=%d, qlen=%d, maxqlen=%d", rxseq, tid, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), off, mbufq_len(&rap->rxa_mq[off]), rap->rxa_mq[off].mq_maxlen); /* XXX error count */ m_freem(m); return CONSUMED; } rap->rxa_qframes++; rap->rxa_qbytes += m->m_pkthdr.len; vap->iv_stats.is_ampdu_rx_reorder++; /* * Statistics for AMSDU decap. */ if (rxs != NULL && ieee80211_check_rxseq_amsdu(rxs)) { if (ieee80211_check_rxseq_amsdu_more(rxs)) { /* more=1, AMSDU, end of batch */ IEEE80211_NODE_STAT(ni, rx_amsdu_more_end); } else { IEEE80211_NODE_STAT(ni, rx_amsdu_more); } } } else { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "a-mpdu duplicate", "seqno %u tid %u BA win <%u:%u>", rxseq, tid, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1)); if (rxs != NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "a-mpdu duplicate", "seqno %d tid %u pktflags 0x%08x\n", rxseq, tid, rxs->c_pktflags); } if (rxs_final != NULL) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "a-mpdu duplicate", "final: pktflags 0x%08x\n", rxs_final->c_pktflags); } vap->iv_stats.is_rx_dup++; IEEE80211_NODE_STAT(ni, rx_dup); m_freem(m); } return CONSUMED; #undef CONSUMED #undef PROCESS } /* * Purge all frames in the A-MPDU re-order queue. */ static void ampdu_rx_purge(struct ieee80211_rx_ampdu *rap) { int i; for (i = 0; i < rap->rxa_wnd; i++) { ampdu_rx_purge_slot(rap, i); if (rap->rxa_qframes == 0) break; } KASSERT(rap->rxa_qbytes == 0 && rap->rxa_qframes == 0, ("lost %u data, %u frames on ampdu rx q", rap->rxa_qbytes, rap->rxa_qframes)); } static void ieee80211_ampdu_rx_init_rap(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { int i; /* XXX TODO: ensure the queues are empty */ memset(rap, 0, sizeof(*rap)); for (i = 0; i < IEEE80211_AGGR_BAWMAX; i++) mbufq_init(&rap->rxa_mq[i], 256); } /* * Start A-MPDU rx/re-order processing for the specified TID. */ static int ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, int baparamset, int batimeout, int baseqctl) { struct ieee80211vap *vap = ni->ni_vap; int bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ); if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) { /* * AMPDU previously setup and not terminated with a DELBA, * flush the reorder q's in case anything remains. */ ampdu_rx_purge(rap); } ieee80211_ampdu_rx_init_rap(ni, rap); rap->rxa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); rap->rxa_start = _IEEE80211_MASKSHIFT(baseqctl, IEEE80211_BASEQ_START); rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND; /* XXX this should be a configuration flag */ if ((vap->iv_htcaps & IEEE80211_HTC_RX_AMSDU_AMPDU) && (_IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU))) rap->rxa_flags |= IEEE80211_AGGR_AMSDU; else rap->rxa_flags &= ~IEEE80211_AGGR_AMSDU; return 0; } /* * Public function; manually setup the RX ampdu state. */ int ieee80211_ampdu_rx_start_ext(struct ieee80211_node *ni, int tid, int seq, int baw) { struct ieee80211_rx_ampdu *rap; /* XXX TODO: sanity check tid, seq, baw */ rap = &ni->ni_rx_ampdu[tid]; if (rap->rxa_flags & IEEE80211_AGGR_RUNNING) { /* * AMPDU previously setup and not terminated with a DELBA, * flush the reorder q's in case anything remains. */ ampdu_rx_purge(rap); } ieee80211_ampdu_rx_init_rap(ni, rap); rap->rxa_wnd = (baw== 0) ? IEEE80211_AGGR_BAWMAX : min(baw, IEEE80211_AGGR_BAWMAX); if (seq == -1) { /* Wait for the first RX frame, use that as BAW */ rap->rxa_start = 0; rap->rxa_flags |= IEEE80211_AGGR_WAITRX; } else { rap->rxa_start = seq; } rap->rxa_flags |= IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND; /* XXX TODO: no amsdu flag */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: tid=%d, start=%d, wnd=%d, flags=0x%08x", __func__, tid, seq, rap->rxa_wnd, rap->rxa_flags); return 0; } /* * Public function; manually stop the RX AMPDU state. */ void ieee80211_ampdu_rx_stop_ext(struct ieee80211_node *ni, int tid) { struct ieee80211_rx_ampdu *rap; /* XXX TODO: sanity check tid, seq, baw */ rap = &ni->ni_rx_ampdu[tid]; ampdu_rx_stop(ni, rap); } /* * Stop A-MPDU rx processing for the specified TID. */ static void ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { ampdu_rx_purge(rap); rap->rxa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_XCHGPEND | IEEE80211_AGGR_WAITRX); } /* * Dispatch a frame from the A-MPDU reorder queue. The * frame is fed back into ieee80211_input marked with an * M_AMPDU_MPDU flag so it doesn't come back to us (it also * permits ieee80211_input to optimize re-processing). */ static __inline void ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m) { m->m_flags |= M_AMPDU_MPDU; /* bypass normal processing */ /* NB: rssi and noise are ignored w/ M_AMPDU_MPDU set */ (void) ieee80211_input(ni, m, 0, 0); } static int ampdu_dispatch_slot(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni, int i) { struct mbuf *m; int n = 0; while (mbufq_len(&rap->rxa_mq[i]) != 0) { m = mbufq_dequeue(&rap->rxa_mq[i]); if (m == NULL) break; n++; rap->rxa_qbytes -= m->m_pkthdr.len; rap->rxa_qframes--; ampdu_dispatch(ni, m); } return (n); } static void ampdu_rx_moveup(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni, int i, int winstart) { struct ieee80211vap *vap = ni->ni_vap; /* * If frames remain, copy the mbuf pointers down so * they correspond to the offsets in the new window. */ if (rap->rxa_qframes != 0) { int n = rap->rxa_qframes, j; for (j = i+1; j < rap->rxa_wnd; j++) { /* * Concat the list contents over, which will * blank the source list for us. */ if (mbufq_len(&rap->rxa_mq[j]) != 0) { n = n - mbufq_len(&rap->rxa_mq[j]); mbufq_concat(&rap->rxa_mq[j-i], &rap->rxa_mq[j]); KASSERT(n >= 0, ("%s: n < 0 (%d)", __func__, n)); if (n == 0) break; } } KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d " "BA win <%d:%d> winstart %d", __func__, n, rap->rxa_qframes, i, rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), winstart)); vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes; } } /* * Dispatch as many frames as possible from the re-order queue. * Frames will always be "at the front"; we process all frames * up to the first empty slot in the window. On completion we * cleanup state if there are still pending frames in the current * BA window. We assume the frame at slot 0 is already handled * by the caller; we always start at slot 1. */ static void ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; int i, r, r2; /* flush run of frames */ r2 = 0; for (i = 1; i < rap->rxa_wnd; i++) { r = ampdu_dispatch_slot(rap, ni, i); if (r == 0) break; r2 += r; } /* move up frames */ ampdu_rx_moveup(rap, ni, i, -1); /* * Adjust the start of the BA window to * reflect the frames just dispatched. */ rap->rxa_start = IEEE80211_SEQ_ADD(rap->rxa_start, i); vap->iv_stats.is_ampdu_rx_oor += r2; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: moved slot up %d slots to start at %d (%d frames)", __func__, i, rap->rxa_start, r2); } /* * Dispatch all frames in the A-MPDU re-order queue. */ static void ampdu_rx_flush(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap) { int i, r; for (i = 0; i < rap->rxa_wnd; i++) { r = ampdu_dispatch_slot(rap, ni, i); if (r == 0) continue; ni->ni_vap->iv_stats.is_ampdu_rx_oor += r; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: moved slot up %d slots to start at %d (%d frames)", __func__, 1, rap->rxa_start, r); if (rap->rxa_qframes == 0) break; } } /* * Dispatch all frames in the A-MPDU re-order queue * preceding the specified sequence number. This logic * handles window moves due to a received MSDU or BAR. */ static void ampdu_rx_flush_upto(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap, ieee80211_seq winstart) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_seq seqno; int i, r; /* * Flush any complete MSDU's with a sequence number lower * than winstart. Gaps may exist. Note that we may actually * dispatch frames past winstart if a run continues; this is * an optimization that avoids having to do a separate pass * to dispatch frames after moving the BA window start. */ seqno = rap->rxa_start; for (i = 0; i < rap->rxa_wnd; i++) { if ((r = mbufq_len(&rap->rxa_mq[i])) != 0) { (void) ampdu_dispatch_slot(rap, ni, i); } else { if (!IEEE80211_SEQ_BA_BEFORE(seqno, winstart)) break; } vap->iv_stats.is_ampdu_rx_oor += r; seqno = IEEE80211_SEQ_INC(seqno); IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: moved slot up %d slots to start at %d (%d frames)", __func__, 1, seqno, r); } /* * If frames remain, copy the mbuf pointers down so * they correspond to the offsets in the new window. */ ampdu_rx_moveup(rap, ni, i, winstart); /* * Move the start of the BA window; we use the * sequence number of the last MSDU that was * passed up the stack+1 or winstart if stopped on * a gap in the reorder buffer. */ rap->rxa_start = seqno; } /* * Process a received QoS data frame for an HT station. Handle * A-MPDU reordering: if this frame is received out of order * and falls within the BA window hold onto it. Otherwise if * this frame completes a run, flush any pending frames. We * return 1 if the frame is consumed. A 0 is returned if * the frame should be processed normally by the caller. * * A-MSDU: handle hardware decap'ed A-MSDU frames that are * pretending to be MPDU's. They're dispatched directly if * able; or attempted to put into the receive reordering slot. */ int ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_rx_stats *rxs) { #define PROCESS 0 /* caller should process frame */ #define CONSUMED 1 /* frame consumed, caller does nothing */ struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_qosframe *wh; struct ieee80211_rx_ampdu *rap; ieee80211_seq rxseq; uint8_t tid; int off; int amsdu = ieee80211_check_rxseq_amsdu(rxs); int amsdu_end = ieee80211_check_rxseq_amsdu_more(rxs); KASSERT((m->m_flags & (M_AMPDU | M_AMPDU_MPDU)) == M_AMPDU, ("!a-mpdu or already re-ordered, flags 0x%x", m->m_flags)); KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); /* NB: m_len known to be sufficient */ wh = mtod(m, struct ieee80211_qosframe *); if (wh->i_fc[0] != IEEE80211_FC0_QOSDATA) { /* * Not QoS data, shouldn't get here but just * return it to the caller for processing. */ return PROCESS; } /* * 802.11-2012 9.3.2.10 - Duplicate detection and recovery. * * Multicast QoS data frames are checked against a different * counter, not the per-TID counter. */ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) return PROCESS; tid = ieee80211_getqos(wh)[0]; tid &= IEEE80211_QOS_TID; rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { /* * No ADDBA request yet, don't touch. */ return PROCESS; } rxseq = le16toh(*(uint16_t *)wh->i_seq); if ((rxseq & IEEE80211_SEQ_FRAG_MASK) != 0) { /* * Fragments are not allowed; toss. */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "A-MPDU", "fragment, rxseq 0x%x tid %u%s", rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); vap->iv_stats.is_ampdu_rx_drop++; IEEE80211_NODE_STAT(ni, rx_drop); m_freem(m); return CONSUMED; } rxseq >>= IEEE80211_SEQ_SEQ_SHIFT; rap->rxa_nframes++; /* * Handle waiting for the first frame to define the BAW. * Some firmware doesn't provide the RX of the starting point * of the BAW and we have to cope. */ if (rap->rxa_flags & IEEE80211_AGGR_WAITRX) { rap->rxa_flags &= ~IEEE80211_AGGR_WAITRX; rap->rxa_start = rxseq; } again: if (rxseq == rap->rxa_start) { /* * First frame in window. */ if (rap->rxa_qframes != 0) { /* * Dispatch as many packets as we can. */ KASSERT((mbufq_len(&rap->rxa_mq[0]) == 0), ("unexpected dup")); ampdu_dispatch(ni, m); ampdu_rx_dispatch(rap, ni); return CONSUMED; } else { /* * In order; advance window if needed and notify * caller to dispatch directly. */ if (amsdu) { if (amsdu_end) { rap->rxa_start = IEEE80211_SEQ_INC(rxseq); IEEE80211_NODE_STAT(ni, rx_amsdu_more_end); } else { IEEE80211_NODE_STAT(ni, rx_amsdu_more); } } else { rap->rxa_start = IEEE80211_SEQ_INC(rxseq); } return PROCESS; } } /* * Frame is out of order; store if in the BA window. */ /* calculate offset in BA window */ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); if (off < rap->rxa_wnd) { /* * Common case (hopefully): in the BA window. * Sec 9.10.7.6.2 a) (p.137) */ /* * Check for frames sitting too long in the reorder queue. * This should only ever happen if frames are not delivered * without the sender otherwise notifying us (e.g. with a * BAR to move the window). Typically this happens because * of vendor bugs that cause the sequence number to jump. * When this happens we get a gap in the reorder queue that * leaves frame sitting on the queue until they get pushed * out due to window moves. When the vendor does not send * BAR this move only happens due to explicit packet sends * * NB: we only track the time of the oldest frame in the * reorder q; this means that if we flush we might push * frames that still "new"; if this happens then subsequent * frames will result in BA window moves which cost something * but is still better than a big throughput dip. */ if (rap->rxa_qframes != 0) { /* XXX honor batimeout? */ if (ticks - rap->rxa_age > ieee80211_ampdu_age) { /* * Too long since we received the first * frame; flush the reorder buffer. */ if (rap->rxa_qframes != 0) { vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; ampdu_rx_flush(ni, rap); } /* * Advance the window if needed and notify * the caller to dispatch directly. */ if (amsdu) { if (amsdu_end) { rap->rxa_start = IEEE80211_SEQ_INC(rxseq); IEEE80211_NODE_STAT(ni, rx_amsdu_more_end); } else { IEEE80211_NODE_STAT(ni, rx_amsdu_more); } } else { rap->rxa_start = IEEE80211_SEQ_INC(rxseq); } return PROCESS; } } else { /* * First frame, start aging timer. */ rap->rxa_age = ticks; } /* save packet - this consumes, no matter what */ ampdu_rx_add_slot(rap, off, tid, rxseq, ni, m, rxs); return CONSUMED; } if (off < IEEE80211_SEQ_BA_RANGE) { /* * Outside the BA window, but within range; * flush the reorder q and move the window. * Sec 9.10.7.6.2 b) (p.138) */ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "move BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); vap->iv_stats.is_ampdu_rx_move++; /* * The spec says to flush frames up to but not including: * WinStart_B = rxseq - rap->rxa_wnd + 1 * Then insert the frame or notify the caller to process * it immediately. We can safely do this by just starting * over again because we know the frame will now be within * the BA window. */ /* NB: rxa_wnd known to be >0 */ ampdu_rx_flush_upto(ni, rap, IEEE80211_SEQ_SUB(rxseq, rap->rxa_wnd-1)); goto again; } else { /* * Outside the BA window and out of range; toss. * Sec 9.10.7.6.2 c) (p.138) */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "MPDU", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); vap->iv_stats.is_ampdu_rx_drop++; IEEE80211_NODE_STAT(ni, rx_drop); m_freem(m); return CONSUMED; } #undef CONSUMED #undef PROCESS } /* * Process a BAR ctl frame. Dispatch all frames up to * the sequence number of the frame. If this frame is * out of range it's discarded. */ void ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_frame_bar *wh; struct ieee80211_rx_ampdu *rap; ieee80211_seq rxseq; int tid, off; if (!ieee80211_recv_bar_ena) { #if 0 IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "%s", "processing disabled"); #endif vap->iv_stats.is_ampdu_bar_bad++; return; } wh = mtod(m0, struct ieee80211_frame_bar *); /* XXX check basic BAR */ tid = _IEEE80211_MASKSHIFT(le16toh(wh->i_ctl), IEEE80211_BAR_TID); rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { /* * No ADDBA request yet, don't touch. */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "no BA stream, tid %u", tid); vap->iv_stats.is_ampdu_bar_bad++; return; } vap->iv_stats.is_ampdu_bar_rx++; rxseq = le16toh(wh->i_seq) >> IEEE80211_SEQ_SEQ_SHIFT; if (rxseq == rap->rxa_start) return; /* calculate offset in BA window */ off = IEEE80211_SEQ_SUB(rxseq, rap->rxa_start); if (off < IEEE80211_SEQ_BA_RANGE) { /* * Flush the reorder q up to rxseq and move the window. * Sec 9.10.7.6.3 a) (p.138) */ IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) rxseq %u tid %u", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid); vap->iv_stats.is_ampdu_bar_move++; ampdu_rx_flush_upto(ni, rap, rxseq); if (off >= rap->rxa_wnd) { /* * BAR specifies a window start to the right of BA * window; we must move it explicitly since * ampdu_rx_flush_upto will not. */ rap->rxa_start = rxseq; } } else { /* * Out of range; toss. * Sec 9.10.7.6.3 b) (p.138) */ IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_INPUT | IEEE80211_MSG_11N, ni->ni_macaddr, "BAR", "BA win <%u:%u> (%u frames) rxseq %u tid %u%s", rap->rxa_start, IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1), rap->rxa_qframes, rxseq, tid, wh->i_fc[1] & IEEE80211_FC1_RETRY ? " (retransmit)" : ""); vap->iv_stats.is_ampdu_bar_oow++; IEEE80211_NODE_STAT(ni, rx_drop); } } /* * Setup HT-specific state in a node. Called only * when HT use is negotiated so we don't do extra * work for temporary and/or legacy sta's. */ void ieee80211_ht_node_init(struct ieee80211_node *ni) { struct ieee80211_tx_ampdu *tap; int tid; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: called (%p)", __func__, ni); if (ni->ni_flags & IEEE80211_NODE_HT) { /* * Clean AMPDU state on re-associate. This handles the case * where a station leaves w/o notifying us and then returns * before node is reaped for inactivity. */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: calling cleanup (%p)", __func__, ni); ieee80211_ht_node_cleanup(ni); } for (tid = 0; tid < WME_NUM_TID; tid++) { tap = &ni->ni_tx_ampdu[tid]; tap->txa_tid = tid; tap->txa_ni = ni; ieee80211_txampdu_init_pps(tap); /* NB: further initialization deferred */ ieee80211_ampdu_rx_init_rap(ni, &ni->ni_rx_ampdu[tid]); } ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU | IEEE80211_NODE_AMSDU; } /* * Cleanup HT-specific state in a node. Called only * when HT use has been marked. */ void ieee80211_ht_node_cleanup(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; int i; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: called (%p)", __func__, ni); KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT node")); /* XXX optimize this */ for (i = 0; i < WME_NUM_TID; i++) { struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[i]; if (tap->txa_flags & IEEE80211_AGGR_SETUP) ampdu_tx_stop(tap); } for (i = 0; i < WME_NUM_TID; i++) ic->ic_ampdu_rx_stop(ni, &ni->ni_rx_ampdu[i]); ni->ni_htcap = 0; ni->ni_flags &= ~IEEE80211_NODE_HT_ALL; } /* * Age out HT resources for a station. */ void ieee80211_ht_node_age(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; uint8_t tid; KASSERT(ni->ni_flags & IEEE80211_NODE_HT, ("not an HT sta")); for (tid = 0; tid < WME_NUM_TID; tid++) { struct ieee80211_rx_ampdu *rap; rap = &ni->ni_rx_ampdu[tid]; if ((rap->rxa_flags & IEEE80211_AGGR_XCHGPEND) == 0) continue; if (rap->rxa_qframes == 0) continue; /* * Check for frames sitting too long in the reorder queue. * See above for more details on what's happening here. */ /* XXX honor batimeout? */ if (ticks - rap->rxa_age > ieee80211_ampdu_age) { /* * Too long since we received the first * frame; flush the reorder buffer. */ vap->iv_stats.is_ampdu_rx_age += rap->rxa_qframes; ampdu_rx_flush(ni, rap); } } } static struct ieee80211_channel * findhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int htflags) { return ieee80211_find_channel(ic, c->ic_freq, (c->ic_flags &~ IEEE80211_CHAN_HT) | htflags); } /* * Adjust a channel to be HT/non-HT according to the vap's configuration. */ struct ieee80211_channel * ieee80211_ht_adjust_channel(struct ieee80211com *ic, struct ieee80211_channel *chan, int flags) { struct ieee80211_channel *c; if (flags & IEEE80211_FHT_HT) { /* promote to HT if possible */ if (flags & IEEE80211_FHT_USEHT40) { if (!IEEE80211_IS_CHAN_HT40(chan)) { /* NB: arbitrarily pick ht40+ over ht40- */ c = findhtchan(ic, chan, IEEE80211_CHAN_HT40U); if (c == NULL) c = findhtchan(ic, chan, IEEE80211_CHAN_HT40D); if (c == NULL) c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); if (c != NULL) chan = c; } } else if (!IEEE80211_IS_CHAN_HT20(chan)) { c = findhtchan(ic, chan, IEEE80211_CHAN_HT20); if (c != NULL) chan = c; } } else if (IEEE80211_IS_CHAN_HT(chan)) { /* demote to legacy, HT use is disabled */ c = ieee80211_find_channel(ic, chan->ic_freq, chan->ic_flags &~ IEEE80211_CHAN_HT); if (c != NULL) chan = c; } return chan; } /* * Setup HT-specific state for a legacy WDS peer. */ void ieee80211_ht_wds_init(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_tx_ampdu *tap; int tid; KASSERT(vap->iv_flags_ht & IEEE80211_FHT_HT, ("no HT requested")); /* XXX check scan cache in case peer has an ap and we have info */ /* * If setup with a legacy channel; locate an HT channel. * Otherwise if the inherited channel (from a companion * AP) is suitable use it so we use the same location * for the extension channel). */ ni->ni_chan = ieee80211_ht_adjust_channel(ni->ni_ic, ni->ni_chan, ieee80211_htchanflags(ni->ni_chan)); ni->ni_htcap = 0; if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI20; if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) { ni->ni_htcap |= IEEE80211_HTCAP_CHWIDTH40; ni->ni_chw = 40; if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_BELOW; if (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) ni->ni_htcap |= IEEE80211_HTCAP_SHORTGI40; } else { ni->ni_chw = 20; ni->ni_ht2ndchan = IEEE80211_HTINFO_2NDCHAN_NONE; } ni->ni_htctlchan = ni->ni_chan->ic_ieee; if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) ni->ni_flags |= IEEE80211_NODE_RIFS; /* XXX does it make sense to enable SMPS? */ ni->ni_htopmode = 0; /* XXX need protection state */ ni->ni_htstbc = 0; /* XXX need info */ for (tid = 0; tid < WME_NUM_TID; tid++) { tap = &ni->ni_tx_ampdu[tid]; tap->txa_tid = tid; ieee80211_txampdu_init_pps(tap); } /* NB: AMPDU tx/rx governed by IEEE80211_FHT_AMPDU_{TX,RX} */ ni->ni_flags |= IEEE80211_NODE_HT | IEEE80211_NODE_AMPDU | IEEE80211_NODE_AMSDU; } /* * Notify a VAP of a change in the HTINFO ie if it's a hostap VAP. * * This is to be called from the deferred HT protection update * task once the flags are updated. */ void ieee80211_htinfo_notify(struct ieee80211vap *vap) { IEEE80211_LOCK_ASSERT(vap->iv_ic); if (vap->iv_opmode != IEEE80211_M_HOSTAP) return; if (vap->iv_state != IEEE80211_S_RUN || !IEEE80211_IS_CHAN_HT(vap->iv_bss->ni_chan)) return; IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, vap->iv_bss, "HT bss occupancy change: %d sta, %d ht, " "%d ht40%s, HT protmode now 0x%x" , vap->iv_sta_assoc , vap->iv_ht_sta_assoc , vap->iv_ht40_sta_assoc , (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) ? ", non-HT sta present" : "" , vap->iv_curhtprotmode); ieee80211_beacon_notify(vap, IEEE80211_BEACON_HTINFO); } /* * Calculate HT protection mode from current * state and handle updates. */ static void htinfo_update(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; uint8_t protmode; if (vap->iv_sta_assoc != vap->iv_ht_sta_assoc) { protmode = IEEE80211_HTINFO_OPMODE_MIXED | IEEE80211_HTINFO_NONHT_PRESENT; } else if (vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) { protmode = IEEE80211_HTINFO_OPMODE_PROTOPT | IEEE80211_HTINFO_NONHT_PRESENT; } else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC && IEEE80211_IS_CHAN_HT40(ic->ic_bsschan) && vap->iv_sta_assoc != vap->iv_ht40_sta_assoc) { protmode = IEEE80211_HTINFO_OPMODE_HT20PR; } else { protmode = IEEE80211_HTINFO_OPMODE_PURE; } if (protmode != vap->iv_curhtprotmode) { vap->iv_curhtprotmode = protmode; /* Update VAP with new protection mode */ ieee80211_vap_update_ht_protmode(vap); } } /* * Handle an HT station joining a BSS. */ void ieee80211_ht_node_join(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; IEEE80211_LOCK_ASSERT(vap->iv_ic); if (ni->ni_flags & IEEE80211_NODE_HT) { vap->iv_ht_sta_assoc++; if (ni->ni_chw == 40) vap->iv_ht40_sta_assoc++; } htinfo_update(vap); } /* * Handle an HT station leaving a BSS. */ void ieee80211_ht_node_leave(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; IEEE80211_LOCK_ASSERT(vap->iv_ic); if (ni->ni_flags & IEEE80211_NODE_HT) { vap->iv_ht_sta_assoc--; if (ni->ni_chw == 40) vap->iv_ht40_sta_assoc--; } htinfo_update(vap); } /* * Public version of htinfo_update; used for processing * beacon frames from overlapping bss. * * Caller can specify either IEEE80211_HTINFO_OPMODE_MIXED * (on receipt of a beacon that advertises MIXED) or * IEEE80211_HTINFO_OPMODE_PROTOPT (on receipt of a beacon * from an overlapping legacy bss). We treat MIXED with * a higher precedence than PROTOPT (i.e. we will not change * change PROTOPT -> MIXED; only MIXED -> PROTOPT). This * corresponds to how we handle things in htinfo_update. * */ void ieee80211_htprot_update(struct ieee80211vap *vap, int protmode) { struct ieee80211com *ic = vap->iv_ic; #define OPMODE(x) _IEEE80211_SHIFTMASK(x, IEEE80211_HTINFO_OPMODE) IEEE80211_LOCK(ic); /* track non-HT station presence */ KASSERT(protmode & IEEE80211_HTINFO_NONHT_PRESENT, ("protmode 0x%x", protmode)); vap->iv_flags_ht |= IEEE80211_FHT_NONHT_PR; vap->iv_lastnonht = ticks; if (protmode != vap->iv_curhtprotmode && (OPMODE(vap->iv_curhtprotmode) != IEEE80211_HTINFO_OPMODE_MIXED || OPMODE(protmode) == IEEE80211_HTINFO_OPMODE_PROTOPT)) { vap->iv_curhtprotmode = protmode; /* Update VAP with new protection mode */ ieee80211_vap_update_ht_protmode(vap); } IEEE80211_UNLOCK(ic); #undef OPMODE } /* * Time out presence of an overlapping bss with non-HT * stations. When operating in hostap mode we listen for * beacons from other stations and if we identify a non-HT * station is present we update the opmode field of the * HTINFO ie. To identify when all non-HT stations are * gone we time out this condition. */ void ieee80211_ht_timeout(struct ieee80211vap *vap) { IEEE80211_LOCK_ASSERT(vap->iv_ic); if ((vap->iv_flags_ht & IEEE80211_FHT_NONHT_PR) && ieee80211_time_after(ticks, vap->iv_lastnonht + IEEE80211_NONHT_PRESENT_AGE)) { IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N, "%s", "time out non-HT STA present on channel"); vap->iv_flags_ht &= ~IEEE80211_FHT_NONHT_PR; htinfo_update(vap); } } /* * Process an 802.11n HT capabilities ie. */ void ieee80211_parse_htcap(struct ieee80211_node *ni, const uint8_t *ie) { if (ie[0] == IEEE80211_ELEMID_VENDOR) { /* * Station used Vendor OUI ie to associate; * mark the node so when we respond we'll use * the Vendor OUI's and not the standard ie's. */ ni->ni_flags |= IEEE80211_NODE_HTCOMPAT; ie += 4; } else ni->ni_flags &= ~IEEE80211_NODE_HTCOMPAT; ni->ni_htcap = le16dec(ie + __offsetof(struct ieee80211_ie_htcap, hc_cap)); ni->ni_htparam = ie[__offsetof(struct ieee80211_ie_htcap, hc_param)]; } static void htinfo_parse(struct ieee80211_node *ni, const struct ieee80211_ie_htinfo *htinfo) { uint16_t w; ni->ni_htctlchan = htinfo->hi_ctrlchannel; ni->ni_ht2ndchan = _IEEE80211_SHIFTMASK(htinfo->hi_byte1, IEEE80211_HTINFO_2NDCHAN); w = le16dec(&htinfo->hi_byte2); ni->ni_htopmode = _IEEE80211_SHIFTMASK(w, IEEE80211_HTINFO_OPMODE); w = le16dec(&htinfo->hi_byte45); ni->ni_htstbc = _IEEE80211_SHIFTMASK(w, IEEE80211_HTINFO_BASIC_STBCMCS); } /* * Parse an 802.11n HT info ie and save useful information * to the node state. Note this does not effect any state * changes such as for channel width change. */ void ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) { if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; htinfo_parse(ni, (const struct ieee80211_ie_htinfo *) ie); } /* * Handle 11n/11ac channel switch. * * Use the received HT/VHT ie's to identify the right channel to use. * If we cannot locate it in the channel table then fallback to * legacy operation. * * Note that we use this information to identify the node's * channel only; the caller is responsible for insuring any * required channel change is done (e.g. in sta mode when * parsing the contents of a beacon frame). */ static int htinfo_update_chw(struct ieee80211_node *ni, int htflags, int vhtflags) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_channel *c; int chanflags; int ret = 0; /* * First step - do HT/VHT only channel lookup based on operating mode * flags. This involves masking out the VHT flags as well. * Otherwise we end up doing the full channel walk each time * we trigger this, which is expensive. */ chanflags = (ni->ni_chan->ic_flags &~ (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | htflags | vhtflags; if (chanflags == ni->ni_chan->ic_flags) goto done; /* * If HT /or/ VHT flags have changed then check both. * We need to start by picking a HT channel anyway. */ c = NULL; chanflags = (ni->ni_chan->ic_flags &~ (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | htflags; /* XXX not right for ht40- */ c = ieee80211_find_channel(ic, ni->ni_chan->ic_freq, chanflags); if (c == NULL && (htflags & IEEE80211_CHAN_HT40)) { /* * No HT40 channel entry in our table; fall back * to HT20 operation. This should not happen. */ c = findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT20); #if 0 IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "no HT40 channel (freq %u), falling back to HT20", ni->ni_chan->ic_freq); #endif /* XXX stat */ } /* Nothing found - leave it alone; move onto VHT */ if (c == NULL) c = ni->ni_chan; /* * If it's non-HT, then bail out now. */ if (! IEEE80211_IS_CHAN_HT(c)) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "not HT; skipping VHT check (%u/0x%x)", c->ic_freq, c->ic_flags); goto done; } /* * Next step - look at the current VHT flags and determine * if we need to upgrade. Mask out the VHT and HT flags since * the vhtflags field will already have the correct HT * flags to use. */ if (IEEE80211_CONF_VHT(ic) && ni->ni_vhtcap != 0 && vhtflags != 0) { chanflags = (c->ic_flags &~ (IEEE80211_CHAN_HT | IEEE80211_CHAN_VHT)) | vhtflags; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "%s: VHT; chanwidth=0x%02x; vhtflags=0x%08x", __func__, ni->ni_vht_chanwidth, vhtflags); IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "%s: VHT; trying lookup for %d/0x%08x", __func__, c->ic_freq, chanflags); c = ieee80211_find_channel(ic, c->ic_freq, chanflags); } /* Finally, if it's changed */ if (c != NULL && c != ni->ni_chan) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, "switch station to %s%d channel %u/0x%x", IEEE80211_IS_CHAN_VHT(c) ? "VHT" : "HT", IEEE80211_IS_CHAN_VHT80(c) ? 80 : (IEEE80211_IS_CHAN_HT40(c) ? 40 : 20), c->ic_freq, c->ic_flags); ni->ni_chan = c; ret = 1; } /* NB: caller responsible for forcing any channel change */ done: /* update node's (11n) tx channel width */ ni->ni_chw = IEEE80211_IS_CHAN_HT40(ni->ni_chan)? 40 : 20; return (ret); } /* * Update 11n MIMO PS state according to received htcap. */ static __inline int htcap_update_mimo_ps(struct ieee80211_node *ni) { uint16_t oflags = ni->ni_flags; switch (ni->ni_htcap & IEEE80211_HTCAP_SMPS) { case IEEE80211_HTCAP_SMPS_DYNAMIC: ni->ni_flags |= IEEE80211_NODE_MIMO_PS; ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; break; case IEEE80211_HTCAP_SMPS_ENA: ni->ni_flags |= IEEE80211_NODE_MIMO_PS; ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; break; case IEEE80211_HTCAP_SMPS_OFF: default: /* disable on rx of reserved value */ ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; break; } return (oflags ^ ni->ni_flags); } /* * Update short GI state according to received htcap * and local settings. */ static __inline void htcap_update_shortgi(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; ni->ni_flags &= ~(IEEE80211_NODE_SGI20|IEEE80211_NODE_SGI40); if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI20) && (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20)) ni->ni_flags |= IEEE80211_NODE_SGI20; if ((ni->ni_htcap & IEEE80211_HTCAP_SHORTGI40) && (vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40)) ni->ni_flags |= IEEE80211_NODE_SGI40; } /* * Update LDPC state according to received htcap * and local settings. */ static __inline void htcap_update_ldpc(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; if ((ni->ni_htcap & IEEE80211_HTCAP_LDPC) && (vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX)) ni->ni_flags |= IEEE80211_NODE_LDPC; } /* * Parse and update HT-related state extracted from * the HT cap and info ie's. * * This is called from the STA management path and * the ieee80211_node_join() path. It will take into * account the IEs discovered during scanning and * adjust things accordingly. */ void ieee80211_ht_updateparams(struct ieee80211_node *ni, const uint8_t *htcapie, const uint8_t *htinfoie) { struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htinfo *htinfo; ieee80211_parse_htcap(ni, htcapie); if (vap->iv_htcaps & IEEE80211_HTC_SMPS) htcap_update_mimo_ps(ni); htcap_update_shortgi(ni); htcap_update_ldpc(ni); if (htinfoie[0] == IEEE80211_ELEMID_VENDOR) htinfoie += 4; htinfo = (const struct ieee80211_ie_htinfo *) htinfoie; htinfo_parse(ni, htinfo); /* * Defer the node channel change; we need to now * update VHT parameters before we do it. */ if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) && (vap->iv_flags_ht & IEEE80211_FHT_RIFS)) ni->ni_flags |= IEEE80211_NODE_RIFS; else ni->ni_flags &= ~IEEE80211_NODE_RIFS; } static uint32_t ieee80211_vht_get_vhtflags(struct ieee80211_node *ni, uint32_t htflags) { struct ieee80211vap *vap = ni->ni_vap; uint32_t vhtflags = 0; vhtflags = 0; if (ni->ni_flags & IEEE80211_NODE_VHT && vap->iv_vht_flags & IEEE80211_FVHT_VHT) { if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ) && /* XXX 2 means "160MHz and 80+80MHz", 1 means "160MHz" */ - (_IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + (_IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) >= 1) && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT160)) { vhtflags = IEEE80211_CHAN_VHT160; /* Mirror the HT40 flags */ if (htflags == IEEE80211_CHAN_HT40U) { vhtflags |= IEEE80211_CHAN_HT40U; } else if (htflags == IEEE80211_CHAN_HT40D) { vhtflags |= IEEE80211_CHAN_HT40D; } } else if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80P80MHZ) && /* XXX 2 means "160MHz and 80+80MHz" */ - (_IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + (_IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) == 2) && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80P80)) { vhtflags = IEEE80211_CHAN_VHT80P80; /* Mirror the HT40 flags */ if (htflags == IEEE80211_CHAN_HT40U) { vhtflags |= IEEE80211_CHAN_HT40U; } else if (htflags == IEEE80211_CHAN_HT40D) { vhtflags |= IEEE80211_CHAN_HT40D; } } else if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_80MHZ) && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80)) { vhtflags = IEEE80211_CHAN_VHT80; /* Mirror the HT40 flags */ if (htflags == IEEE80211_CHAN_HT40U) { vhtflags |= IEEE80211_CHAN_HT40U; } else if (htflags == IEEE80211_CHAN_HT40D) { vhtflags |= IEEE80211_CHAN_HT40D; } } else if (ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_USE_HT) { /* Mirror the HT40 flags */ /* * XXX TODO: if ht40 is disabled, but vht40 isn't * disabled then this logic will get very, very sad. * It's quite possible the only sane thing to do is * to not have vht40 as an option, and just obey * 'ht40' as that flag. */ if ((htflags == IEEE80211_CHAN_HT40U) && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40)) { vhtflags = IEEE80211_CHAN_VHT40U | IEEE80211_CHAN_HT40U; } else if (htflags == IEEE80211_CHAN_HT40D && (vap->iv_vht_flags & IEEE80211_FVHT_USEVHT40)) { vhtflags = IEEE80211_CHAN_VHT40D | IEEE80211_CHAN_HT40D; } else if (htflags == IEEE80211_CHAN_HT20) { vhtflags = IEEE80211_CHAN_VHT20 | IEEE80211_CHAN_HT20; } } else { vhtflags = IEEE80211_CHAN_VHT20; } } return (vhtflags); } /* * Final part of updating the HT parameters. * * This is called from the STA management path and * the ieee80211_node_join() path. It will take into * account the IEs discovered during scanning and * adjust things accordingly. * * This is done after a call to ieee80211_ht_updateparams() * because it (and the upcoming VHT version of updateparams) * needs to ensure everything is parsed before htinfo_update_chw() * is called - which will change the channel config for the * node for us. */ int ieee80211_ht_updateparams_final(struct ieee80211_node *ni, const uint8_t *htcapie, const uint8_t *htinfoie) { struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htinfo *htinfo; int htflags, vhtflags; int ret = 0; htinfo = (const struct ieee80211_ie_htinfo *) htinfoie; htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? IEEE80211_CHAN_HT20 : 0; /* NB: honor operating mode constraint */ if ((htinfo->hi_byte1 & IEEE80211_HTINFO_TXWIDTH_2040) && (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) { if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_ABOVE) htflags = IEEE80211_CHAN_HT40U; else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) htflags = IEEE80211_CHAN_HT40D; } /* * VHT flags - do much the same; check whether VHT is available * and if so, what our ideal channel use would be based on our * capabilities and the (pre-parsed) VHT info IE. */ vhtflags = ieee80211_vht_get_vhtflags(ni, htflags); if (htinfo_update_chw(ni, htflags, vhtflags)) ret = 1; return (ret); } /* * Parse and update HT-related state extracted from the HT cap ie * for a station joining an HT BSS. * * This is called from the hostap path for each station. */ void ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) { struct ieee80211vap *vap = ni->ni_vap; ieee80211_parse_htcap(ni, htcapie); if (vap->iv_htcaps & IEEE80211_HTC_SMPS) htcap_update_mimo_ps(ni); htcap_update_shortgi(ni); htcap_update_ldpc(ni); } /* * Called once HT and VHT capabilities are parsed in hostap mode - * this will adjust the channel configuration of the given node * based on the configuration and capabilities. */ void ieee80211_ht_updatehtcap_final(struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; int htflags; int vhtflags; /* NB: honor operating mode constraint */ /* XXX 40 MHz intolerant */ htflags = (vap->iv_flags_ht & IEEE80211_FHT_HT) ? IEEE80211_CHAN_HT20 : 0; if ((ni->ni_htcap & IEEE80211_HTCAP_CHWIDTH40) && (vap->iv_flags_ht & IEEE80211_FHT_USEHT40)) { if (IEEE80211_IS_CHAN_HT40U(vap->iv_bss->ni_chan)) htflags = IEEE80211_CHAN_HT40U; else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan)) htflags = IEEE80211_CHAN_HT40D; } /* * VHT flags - do much the same; check whether VHT is available * and if so, what our ideal channel use would be based on our * capabilities and the (pre-parsed) VHT info IE. */ vhtflags = ieee80211_vht_get_vhtflags(ni, htflags); (void) htinfo_update_chw(ni, htflags, vhtflags); } /* * Install received HT rate set by parsing the HT cap ie. */ int ieee80211_setup_htrates(struct ieee80211_node *ni, const uint8_t *ie, int flags) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; const struct ieee80211_ie_htcap *htcap; struct ieee80211_htrateset *rs; int i, maxequalmcs, maxunequalmcs; maxequalmcs = ic->ic_txstream * 8 - 1; maxunequalmcs = 0; if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) { if (ic->ic_txstream >= 2) maxunequalmcs = 38; if (ic->ic_txstream >= 3) maxunequalmcs = 52; if (ic->ic_txstream >= 4) maxunequalmcs = 76; } rs = &ni->ni_htrates; memset(rs, 0, sizeof(*rs)); if (ie != NULL) { if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; htcap = (const struct ieee80211_ie_htcap *) ie; for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { if (isclr(htcap->hc_mcsset, i)) continue; if (rs->rs_nrates == IEEE80211_HTRATE_MAXSIZE) { IEEE80211_NOTE(vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "WARNING, HT rate set too large; only " "using %u rates", IEEE80211_HTRATE_MAXSIZE); vap->iv_stats.is_rx_rstoobig++; break; } if (i <= 31 && i > maxequalmcs) continue; if (i == 32 && (ic->ic_htcaps & IEEE80211_HTC_TXMCS32) == 0) continue; if (i > 32 && i > maxunequalmcs) continue; rs->rs_rates[rs->rs_nrates++] = i; } } return ieee80211_fix_rate(ni, (struct ieee80211_rateset *) rs, flags); } /* * Mark rates in a node's HT rate set as basic according * to the information in the supplied HT info ie. */ void ieee80211_setup_basic_htrates(struct ieee80211_node *ni, const uint8_t *ie) { const struct ieee80211_ie_htinfo *htinfo; struct ieee80211_htrateset *rs; int i, j; if (ie[0] == IEEE80211_ELEMID_VENDOR) ie += 4; htinfo = (const struct ieee80211_ie_htinfo *) ie; rs = &ni->ni_htrates; if (rs->rs_nrates == 0) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_XRATE | IEEE80211_MSG_11N, ni, "%s", "WARNING, empty HT rate set"); return; } for (i = 0; i < IEEE80211_HTRATE_MAXSIZE; i++) { if (isclr(htinfo->hi_basicmcsset, i)) continue; for (j = 0; j < rs->rs_nrates; j++) if ((rs->rs_rates[j] & IEEE80211_RATE_VAL) == i) rs->rs_rates[j] |= IEEE80211_RATE_BASIC; } } static void ampdu_tx_setup(struct ieee80211_tx_ampdu *tap) { callout_init(&tap->txa_timer, 1); tap->txa_flags |= IEEE80211_AGGR_SETUP; tap->txa_lastsample = ticks; } static void ampdu_tx_stop(struct ieee80211_tx_ampdu *tap) { struct ieee80211_node *ni = tap->txa_ni; struct ieee80211com *ic = ni->ni_ic; IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: called", __func__); KASSERT(tap->txa_flags & IEEE80211_AGGR_SETUP, ("txa_flags 0x%x tid %d ac %d", tap->txa_flags, tap->txa_tid, TID_TO_WME_AC(tap->txa_tid))); /* * Stop BA stream if setup so driver has a chance * to reclaim any resources it might have allocated. */ ic->ic_addba_stop(ni, tap); /* * Stop any pending BAR transmit. */ bar_stop_timer(tap); /* * Reset packet estimate. */ ieee80211_txampdu_init_pps(tap); /* NB: clearing NAK means we may re-send ADDBA */ tap->txa_flags &= ~(IEEE80211_AGGR_SETUP | IEEE80211_AGGR_NAK); } /* * ADDBA response timeout. * * If software aggregation and per-TID queue management was done here, * that queue would be unpaused after the ADDBA timeout occurs. */ static void addba_timeout(void *arg) { struct ieee80211_tx_ampdu *tap = arg; struct ieee80211_node *ni = tap->txa_ni; struct ieee80211com *ic = ni->ni_ic; /* XXX ? */ tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; tap->txa_attempts++; ic->ic_addba_response_timeout(ni, tap); } static void addba_start_timeout(struct ieee80211_tx_ampdu *tap) { /* XXX use CALLOUT_PENDING instead? */ callout_reset(&tap->txa_timer, ieee80211_addba_timeout, addba_timeout, tap); tap->txa_flags |= IEEE80211_AGGR_XCHGPEND; tap->txa_nextrequest = ticks + ieee80211_addba_timeout; } static void addba_stop_timeout(struct ieee80211_tx_ampdu *tap) { /* XXX use CALLOUT_PENDING instead? */ if (tap->txa_flags & IEEE80211_AGGR_XCHGPEND) { callout_stop(&tap->txa_timer); tap->txa_flags &= ~IEEE80211_AGGR_XCHGPEND; } } static void null_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { } /* * Default method for requesting A-MPDU tx aggregation. * We setup the specified state block and start a timer * to wait for an ADDBA response frame. */ static int ieee80211_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int dialogtoken, int baparamset, int batimeout) { int bufsiz; /* XXX locking */ tap->txa_token = dialogtoken; tap->txa_flags |= IEEE80211_AGGR_IMMEDIATE; bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ); tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); addba_start_timeout(tap); return 1; } /* * Called by drivers that wish to request an ADDBA session be * setup. This brings it up and starts the request timer. */ int ieee80211_ampdu_tx_request_ext(struct ieee80211_node *ni, int tid) { struct ieee80211_tx_ampdu *tap; if (tid < 0 || tid > 15) return (0); tap = &ni->ni_tx_ampdu[tid]; /* XXX locking */ if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { /* do deferred setup of state */ ampdu_tx_setup(tap); } /* XXX hack for not doing proper locking */ tap->txa_flags &= ~IEEE80211_AGGR_NAK; addba_start_timeout(tap); return (1); } /* * Called by drivers that have marked a session as active. */ int ieee80211_ampdu_tx_request_active_ext(struct ieee80211_node *ni, int tid, int status) { struct ieee80211_tx_ampdu *tap; if (tid < 0 || tid > 15) return (0); tap = &ni->ni_tx_ampdu[tid]; /* XXX locking */ addba_stop_timeout(tap); if (status == 1) { tap->txa_flags |= IEEE80211_AGGR_RUNNING; tap->txa_attempts = 0; } else { /* mark tid so we don't try again */ tap->txa_flags |= IEEE80211_AGGR_NAK; } return (1); } /* * Default method for processing an A-MPDU tx aggregation * response. We shutdown any pending timer and update the * state block according to the reply. */ static int ieee80211_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status, int baparamset, int batimeout) { struct ieee80211vap *vap = ni->ni_vap; int bufsiz; /* XXX locking */ addba_stop_timeout(tap); if (status == IEEE80211_STATUS_SUCCESS) { bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ); /* XXX override our request? */ tap->txa_wnd = (bufsiz == 0) ? IEEE80211_AGGR_BAWMAX : min(bufsiz, IEEE80211_AGGR_BAWMAX); #ifdef __notyet__ tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID); #endif tap->txa_flags |= IEEE80211_AGGR_RUNNING; tap->txa_attempts = 0; /* TODO: this should be a vap flag */ if ((vap->iv_htcaps & IEEE80211_HTC_TX_AMSDU_AMPDU) && (ni->ni_flags & IEEE80211_NODE_AMSDU_TX) && (_IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU))) tap->txa_flags |= IEEE80211_AGGR_AMSDU; else tap->txa_flags &= ~IEEE80211_AGGR_AMSDU; } else { /* mark tid so we don't try again */ tap->txa_flags |= IEEE80211_AGGR_NAK; } return 1; } /* * Default method for stopping A-MPDU tx aggregation. * Any timer is cleared and we drain any pending frames. */ static void ieee80211_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { /* XXX locking */ addba_stop_timeout(tap); if (tap->txa_flags & IEEE80211_AGGR_RUNNING) { /* XXX clear aggregation queue */ tap->txa_flags &= ~(IEEE80211_AGGR_RUNNING | IEEE80211_AGGR_AMSDU); } tap->txa_attempts = 0; } /* * Process a received action frame using the default aggregation * policy. We intercept ADDBA-related frames and use them to * update our aggregation state. All other frames are passed up * for processing by ieee80211_recv_action. */ static int ht_recv_action_ba_addba_request(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_rx_ampdu *rap; uint8_t dialogtoken; uint16_t baparamset, batimeout, baseqctl; uint16_t args[5]; int tid; dialogtoken = frm[2]; baparamset = le16dec(frm+3); batimeout = le16dec(frm+5); baseqctl = le16dec(frm+7); tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID); IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA request: dialogtoken %u baparamset 0x%x " "(tid %d bufsiz %d) batimeout %d baseqctl %d:%d amsdu %d", dialogtoken, baparamset, tid, _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ), batimeout, _IEEE80211_MASKSHIFT(baseqctl, IEEE80211_BASEQ_START), _IEEE80211_MASKSHIFT(baseqctl, IEEE80211_BASEQ_FRAG), _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU)); rap = &ni->ni_rx_ampdu[tid]; /* Send ADDBA response */ args[0] = dialogtoken; /* * NB: We ack only if the sta associated with HT and * the ap is configured to do AMPDU rx (the latter * violates the 11n spec and is mostly for testing). */ if ((ni->ni_flags & IEEE80211_NODE_AMPDU_RX) && (vap->iv_flags_ht & IEEE80211_FHT_AMPDU_RX)) { /* XXX TODO: handle ampdu_rx_start failure */ ic->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl); args[1] = IEEE80211_STATUS_SUCCESS; } else { IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "reject ADDBA request: %s", ni->ni_flags & IEEE80211_NODE_AMPDU_RX ? "administratively disabled" : "not negotiated for station"); vap->iv_stats.is_addba_reject++; args[1] = IEEE80211_STATUS_UNSPECIFIED; } /* XXX honor rap flags? */ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE | _IEEE80211_SHIFTMASK(tid, IEEE80211_BAPS_TID) | _IEEE80211_SHIFTMASK(rap->rxa_wnd, IEEE80211_BAPS_BUFSIZ) ; /* * TODO: we're out of iv_flags_ht fields; once * this is extended we should make this configurable. */ if ((baparamset & IEEE80211_BAPS_AMSDU) && (ni->ni_flags & IEEE80211_NODE_AMSDU_RX) && (vap->iv_htcaps & IEEE80211_HTC_RX_AMSDU_AMPDU)) args[2] |= IEEE80211_BAPS_AMSDU; args[3] = 0; args[4] = 0; ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_RESPONSE, args); return 0; } static int ht_recv_action_ba_addba_response(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; struct ieee80211_tx_ampdu *tap; uint8_t dialogtoken, policy; uint16_t baparamset, batimeout, code; int tid; #ifdef IEEE80211_DEBUG int amsdu, bufsiz; #endif dialogtoken = frm[2]; code = le16dec(frm+3); baparamset = le16dec(frm+5); tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_TID); #ifdef IEEE80211_DEBUG bufsiz = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_BUFSIZ); amsdu = !! _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_AMSDU); #endif policy = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_BAPS_POLICY); batimeout = le16dec(frm+7); tap = &ni->ni_tx_ampdu[tid]; if ((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "no pending ADDBA, tid %d dialogtoken %u " "code %d", tid, dialogtoken, code); vap->iv_stats.is_addba_norequest++; return 0; } if (dialogtoken != tap->txa_token) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "dialogtoken mismatch: waiting for %d, " "received %d, tid %d code %d", tap->txa_token, dialogtoken, tid, code); vap->iv_stats.is_addba_badtoken++; return 0; } /* NB: assumes IEEE80211_AGGR_IMMEDIATE is 1 */ if (policy != (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE)) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "policy mismatch: expecting %s, " "received %s, tid %d code %d", tap->txa_flags & IEEE80211_AGGR_IMMEDIATE, policy, tid, code); vap->iv_stats.is_addba_badpolicy++; return 0; } #if 0 /* XXX we take MIN in ieee80211_addba_response */ if (bufsiz > IEEE80211_AGGR_BAWMAX) { IEEE80211_DISCARD_MAC(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni->ni_macaddr, "ADDBA response", "BA window too large: max %d, " "received %d, tid %d code %d", bufsiz, IEEE80211_AGGR_BAWMAX, tid, code); vap->iv_stats.is_addba_badbawinsize++; return 0; } #endif IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv ADDBA response: dialogtoken %u code %d " "baparamset 0x%x (tid %d bufsiz %d amsdu %d) batimeout %d", dialogtoken, code, baparamset, tid, bufsiz, amsdu, batimeout); ic->ic_addba_response(ni, tap, code, baparamset, batimeout); return 0; } static int ht_recv_action_ba_delba(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211_rx_ampdu *rap; struct ieee80211_tx_ampdu *tap; uint16_t baparamset; #ifdef IEEE80211_DEBUG uint16_t code; #endif int tid; baparamset = le16dec(frm+2); #ifdef IEEE80211_DEBUG code = le16dec(frm+4); #endif tid = _IEEE80211_MASKSHIFT(baparamset, IEEE80211_DELBAPS_TID); IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "recv DELBA: baparamset 0x%x (tid %d initiator %d) " "code %d", baparamset, tid, _IEEE80211_MASKSHIFT(baparamset, IEEE80211_DELBAPS_INIT), code); if ((baparamset & IEEE80211_DELBAPS_INIT) == 0) { tap = &ni->ni_tx_ampdu[tid]; ic->ic_addba_stop(ni, tap); } else { rap = &ni->ni_rx_ampdu[tid]; ic->ic_ampdu_rx_stop(ni, rap); } return 0; } static int ht_recv_action_ht_txchwidth(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { int chw; chw = (frm[2] == IEEE80211_A_HT_TXCHWIDTH_2040) ? 40 : 20; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT txchwidth, width %d%s", __func__, chw, ni->ni_chw != chw ? "*" : ""); if (chw != ni->ni_chw) { /* XXX does this need to change the ht40 station count? */ ni->ni_chw = chw; /* XXX notify on change */ } return 0; } static int ht_recv_action_ht_mimopwrsave(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { const struct ieee80211_action_ht_mimopowersave *mps = (const struct ieee80211_action_ht_mimopowersave *) frm; /* XXX check iv_htcaps */ if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_ENA) ni->ni_flags |= IEEE80211_NODE_MIMO_PS; else ni->ni_flags &= ~IEEE80211_NODE_MIMO_PS; if (mps->am_control & IEEE80211_A_HT_MIMOPWRSAVE_MODE) ni->ni_flags |= IEEE80211_NODE_MIMO_RTS; else ni->ni_flags &= ~IEEE80211_NODE_MIMO_RTS; /* XXX notify on change */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: HT MIMO PS (%s%s)", __func__, (ni->ni_flags & IEEE80211_NODE_MIMO_PS) ? "on" : "off", (ni->ni_flags & IEEE80211_NODE_MIMO_RTS) ? "+rts" : "" ); return 0; } /* * Transmit processing. */ /* * Check if A-MPDU should be requested/enabled for a stream. * We require a traffic rate above a per-AC threshold and we * also handle backoff from previous failed attempts. * * Drivers may override this method to bring in information * such as link state conditions in making the decision. */ static int ieee80211_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211vap *vap = ni->ni_vap; if (tap->txa_avgpps < vap->iv_ampdu_mintraffic[TID_TO_WME_AC(tap->txa_tid)]) return 0; /* XXX check rssi? */ if (tap->txa_attempts >= ieee80211_addba_maxtries && ieee80211_time_after(ticks, tap->txa_nextrequest)) { /* * Don't retry too often; txa_nextrequest is set * to the minimum interval we'll retry after * ieee80211_addba_maxtries failed attempts are made. */ return 0; } IEEE80211_NOTE(vap, IEEE80211_MSG_11N, ni, "enable AMPDU on tid %d (%s), avgpps %d pkts %d attempt %d", tap->txa_tid, ieee80211_wme_acnames[TID_TO_WME_AC(tap->txa_tid)], tap->txa_avgpps, tap->txa_pkts, tap->txa_attempts); return 1; } /* * Request A-MPDU tx aggregation. Setup local state and * issue an ADDBA request. BA use will only happen after * the other end replies with ADDBA response. */ int ieee80211_ampdu_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap) { struct ieee80211com *ic = ni->ni_ic; uint16_t args[5]; int tid, dialogtoken; static int tokens = 0; /* XXX */ /* XXX locking */ if ((tap->txa_flags & IEEE80211_AGGR_SETUP) == 0) { /* do deferred setup of state */ ampdu_tx_setup(tap); } /* XXX hack for not doing proper locking */ tap->txa_flags &= ~IEEE80211_AGGR_NAK; dialogtoken = (tokens+1) % 63; /* XXX */ tid = tap->txa_tid; /* * XXX TODO: This is racy with any other parallel TX going on. :( */ tap->txa_start = ni->ni_txseqs[tid]; args[0] = dialogtoken; args[1] = 0; /* NB: status code not used */ args[2] = IEEE80211_BAPS_POLICY_IMMEDIATE | _IEEE80211_SHIFTMASK(tid, IEEE80211_BAPS_TID) | _IEEE80211_SHIFTMASK(IEEE80211_AGGR_BAWMAX, IEEE80211_BAPS_BUFSIZ) ; /* XXX TODO: this should be a flag, not iv_htcaps */ if ((ni->ni_flags & IEEE80211_NODE_AMSDU_TX) && (ni->ni_vap->iv_htcaps & IEEE80211_HTC_TX_AMSDU_AMPDU)) args[2] |= IEEE80211_BAPS_AMSDU; args[3] = 0; /* batimeout */ /* NB: do first so there's no race against reply */ if (!ic->ic_addba_request(ni, tap, dialogtoken, args[2], args[3])) { /* unable to setup state, don't make request */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: could not setup BA stream for TID %d AC %d", __func__, tap->txa_tid, TID_TO_WME_AC(tap->txa_tid)); /* defer next try so we don't slam the driver with requests */ tap->txa_attempts = ieee80211_addba_maxtries; /* NB: check in case driver wants to override */ if (tap->txa_nextrequest <= ticks) tap->txa_nextrequest = ticks + ieee80211_addba_backoff; return 0; } tokens = dialogtoken; /* allocate token */ /* NB: after calling ic_addba_request so driver can set txa_start */ args[4] = _IEEE80211_SHIFTMASK(tap->txa_start, IEEE80211_BASEQ_START) | _IEEE80211_SHIFTMASK(0, IEEE80211_BASEQ_FRAG) ; return ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_ADDBA_REQUEST, args); } /* * Terminate an AMPDU tx stream. State is reclaimed * and the peer notified with a DelBA Action frame. */ void ieee80211_ampdu_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int reason) { struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; uint16_t args[4]; /* XXX locking */ tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; if (IEEE80211_AMPDU_RUNNING(tap)) { IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: stop BA stream for TID %d (reason: %d (%s))", __func__, tap->txa_tid, reason, ieee80211_reason_to_string(reason)); vap->iv_stats.is_ampdu_stop++; ic->ic_addba_stop(ni, tap); args[0] = tap->txa_tid; args[1] = IEEE80211_DELBAPS_INIT; args[2] = reason; /* XXX reason code */ ic->ic_send_action(ni, IEEE80211_ACTION_CAT_BA, IEEE80211_ACTION_BA_DELBA, args); } else { IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "%s: BA stream for TID %d not running " "(reason: %d (%s))", __func__, tap->txa_tid, reason, ieee80211_reason_to_string(reason)); vap->iv_stats.is_ampdu_stop_failed++; } } /* XXX */ static void bar_start_timer(struct ieee80211_tx_ampdu *tap); static void bar_timeout(void *arg) { struct ieee80211_tx_ampdu *tap = arg; struct ieee80211_node *ni = tap->txa_ni; KASSERT((tap->txa_flags & IEEE80211_AGGR_XCHGPEND) == 0, ("bar/addba collision, flags 0x%x", tap->txa_flags)); IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: tid %u flags 0x%x attempts %d", __func__, tap->txa_tid, tap->txa_flags, tap->txa_attempts); /* guard against race with bar_tx_complete */ if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) return; /* XXX ? */ if (tap->txa_attempts >= ieee80211_bar_maxtries) { struct ieee80211com *ic = ni->ni_ic; ni->ni_vap->iv_stats.is_ampdu_bar_tx_fail++; /* * If (at least) the last BAR TX timeout was due to * an ieee80211_send_bar() failures, then we need * to make sure we notify the driver that a BAR * TX did occur and fail. This gives the driver * a chance to undo any queue pause that may * have occurred. */ ic->ic_bar_response(ni, tap, 1); ieee80211_ampdu_stop(ni, tap, IEEE80211_REASON_TIMEOUT); } else { ni->ni_vap->iv_stats.is_ampdu_bar_tx_retry++; if (ieee80211_send_bar(ni, tap, tap->txa_seqpending) != 0) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: failed to TX, starting timer\n", __func__); /* * If ieee80211_send_bar() fails here, the * timer may have stopped and/or the pending * flag may be clear. Because of this, * fake the BARPEND and reset the timer. * A retransmission attempt will then occur * during the next timeout. */ /* XXX locking */ tap->txa_flags |= IEEE80211_AGGR_BARPEND; bar_start_timer(tap); } } } static void bar_start_timer(struct ieee80211_tx_ampdu *tap) { IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: called", __func__); callout_reset(&tap->txa_timer, ieee80211_bar_timeout, bar_timeout, tap); } static void bar_stop_timer(struct ieee80211_tx_ampdu *tap) { IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: called", __func__); callout_stop(&tap->txa_timer); } static void bar_tx_complete(struct ieee80211_node *ni, void *arg, int status) { struct ieee80211_tx_ampdu *tap = arg; IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: tid %u flags 0x%x pending %d status %d", __func__, tap->txa_tid, tap->txa_flags, callout_pending(&tap->txa_timer), status); ni->ni_vap->iv_stats.is_ampdu_bar_tx++; /* XXX locking */ if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) && callout_pending(&tap->txa_timer)) { struct ieee80211com *ic = ni->ni_ic; if (status == 0) /* ACK'd */ bar_stop_timer(tap); ic->ic_bar_response(ni, tap, status); /* NB: just let timer expire so we pace requests */ } } static void ieee80211_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, int status) { IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: called", __func__); if (status == 0) { /* got ACK */ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "BAR moves BA win <%u:%u> (%u frames) txseq %u tid %u", tap->txa_start, IEEE80211_SEQ_ADD(tap->txa_start, tap->txa_wnd-1), tap->txa_qframes, tap->txa_seqpending, tap->txa_tid); /* NB: timer already stopped in bar_tx_complete */ tap->txa_start = tap->txa_seqpending; tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; } } /* * Transmit a BAR frame to the specified node. The * BAR contents are drawn from the supplied aggregation * state associated with the node. * * NB: we only handle immediate ACK w/ compressed bitmap. */ int ieee80211_send_bar(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap, ieee80211_seq seq) { #define senderr(_x, _v) do { vap->iv_stats._v++; ret = _x; goto bad; } while (0) struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct ieee80211_frame_bar *bar; struct mbuf *m; uint16_t barctl, barseqctl; uint8_t *frm; int tid, ret; IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: called", __func__); if ((tap->txa_flags & IEEE80211_AGGR_RUNNING) == 0) { /* no ADDBA response, should not happen */ /* XXX stat+msg */ return EINVAL; } /* XXX locking */ bar_stop_timer(tap); ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom, sizeof(*bar)); if (m == NULL) senderr(ENOMEM, is_tx_nobuf); if (!ieee80211_add_callback(m, bar_tx_complete, tap)) { m_freem(m); senderr(ENOMEM, is_tx_nobuf); /* XXX */ /* NOTREACHED */ } bar = mtod(m, struct ieee80211_frame_bar *); bar->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_CTL | IEEE80211_FC0_SUBTYPE_BAR; bar->i_fc[1] = 0; IEEE80211_ADDR_COPY(bar->i_ra, ni->ni_macaddr); IEEE80211_ADDR_COPY(bar->i_ta, vap->iv_myaddr); tid = tap->txa_tid; barctl = (tap->txa_flags & IEEE80211_AGGR_IMMEDIATE ? 0 : IEEE80211_BAR_NOACK) | IEEE80211_BAR_COMP | _IEEE80211_SHIFTMASK(tid, IEEE80211_BAR_TID) ; barseqctl = _IEEE80211_SHIFTMASK(seq, IEEE80211_BAR_SEQ_START); /* NB: known to have proper alignment */ bar->i_ctl = htole16(barctl); bar->i_seq = htole16(barseqctl); m->m_pkthdr.len = m->m_len = sizeof(struct ieee80211_frame_bar); M_WME_SETAC(m, WME_AC_VO); IEEE80211_NODE_STAT(ni, tx_mgmt); /* XXX tx_ctl? */ /* XXX locking */ /* init/bump attempts counter */ if ((tap->txa_flags & IEEE80211_AGGR_BARPEND) == 0) tap->txa_attempts = 1; else tap->txa_attempts++; tap->txa_seqpending = seq; tap->txa_flags |= IEEE80211_AGGR_BARPEND; IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, ni, "send BAR: tid %u ctl 0x%x start %u (attempt %d)", tid, barctl, seq, tap->txa_attempts); /* * ic_raw_xmit will free the node reference * regardless of queue/TX success or failure. */ IEEE80211_TX_LOCK(ic); ret = ieee80211_raw_output(vap, ni, m, NULL); IEEE80211_TX_UNLOCK(ic); if (ret != 0) { IEEE80211_NOTE(vap, IEEE80211_MSG_DEBUG | IEEE80211_MSG_11N, ni, "send BAR: failed: (ret = %d)\n", ret); /* xmit failed, clear state flag */ tap->txa_flags &= ~IEEE80211_AGGR_BARPEND; vap->iv_stats.is_ampdu_bar_tx_fail++; return ret; } /* XXX hack against tx complete happening before timer is started */ if (tap->txa_flags & IEEE80211_AGGR_BARPEND) bar_start_timer(tap); return 0; bad: IEEE80211_NOTE(tap->txa_ni->ni_vap, IEEE80211_MSG_11N, tap->txa_ni, "%s: bad! ret=%d", __func__, ret); vap->iv_stats.is_ampdu_bar_tx_fail++; ieee80211_free_node(ni); return ret; #undef senderr } static int ht_action_output(struct ieee80211_node *ni, struct mbuf *m) { struct ieee80211_bpf_params params; memset(¶ms, 0, sizeof(params)); params.ibp_pri = WME_AC_VO; params.ibp_rate0 = ni->ni_txparms->mgmtrate; /* NB: we know all frames are unicast */ params.ibp_try0 = ni->ni_txparms->maxretry; params.ibp_power = ni->ni_txpower; return ieee80211_mgmt_output(ni, m, IEEE80211_FC0_SUBTYPE_ACTION, ¶ms); } #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) /* * Send an action management frame. The arguments are stuff * into a frame without inspection; the caller is assumed to * prepare them carefully (e.g. based on the aggregation state). */ static int ht_send_action_ba_addba(struct ieee80211_node *ni, int category, int action, void *arg0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; uint16_t *args = arg0; struct mbuf *m; uint8_t *frm; IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send ADDBA %s: dialogtoken %d status %d " "baparamset 0x%x (tid %d amsdu %d) batimeout 0x%x baseqctl 0x%x", (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ? "request" : "response", args[0], args[1], args[2], _IEEE80211_MASKSHIFT(args[2], IEEE80211_BAPS_TID), _IEEE80211_MASKSHIFT(args[2], IEEE80211_BAPS_AMSDU), args[3], args[4]); IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t) /* action+category */ /* XXX may action payload */ + sizeof(struct ieee80211_action_ba_addbaresponse) ); if (m != NULL) { *frm++ = category; *frm++ = action; *frm++ = args[0]; /* dialog token */ if (action == IEEE80211_ACTION_BA_ADDBA_RESPONSE) ADDSHORT(frm, args[1]); /* status code */ ADDSHORT(frm, args[2]); /* baparamset */ ADDSHORT(frm, args[3]); /* batimeout */ if (action == IEEE80211_ACTION_BA_ADDBA_REQUEST) ADDSHORT(frm, args[4]); /* baseqctl */ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); return ht_action_output(ni, m); } else { vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); return ENOMEM; } } static int ht_send_action_ba_delba(struct ieee80211_node *ni, int category, int action, void *arg0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; uint16_t *args = arg0; struct mbuf *m; uint16_t baparamset; uint8_t *frm; baparamset = _IEEE80211_SHIFTMASK(args[0], IEEE80211_DELBAPS_TID) | args[1] ; IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send DELBA action: tid %d, initiator %d reason %d (%s)", args[0], args[1], args[2], ieee80211_reason_to_string(args[2])); IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t) /* action+category */ /* XXX may action payload */ + sizeof(struct ieee80211_action_ba_addbaresponse) ); if (m != NULL) { *frm++ = category; *frm++ = action; ADDSHORT(frm, baparamset); ADDSHORT(frm, args[2]); /* reason code */ m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); return ht_action_output(ni, m); } else { vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); return ENOMEM; } } static int ht_send_action_ht_txchwidth(struct ieee80211_node *ni, int category, int action, void *arg0) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; struct mbuf *m; uint8_t *frm; IEEE80211_NOTE(vap, IEEE80211_MSG_ACTION | IEEE80211_MSG_11N, ni, "send HT txchwidth: width %d", IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? 40 : 20); IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE, "ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n", __func__, __LINE__, ni, ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni)+1); ieee80211_ref_node(ni); m = ieee80211_getmgtframe(&frm, ic->ic_headroom + sizeof(struct ieee80211_frame), sizeof(uint16_t) /* action+category */ /* XXX may action payload */ + sizeof(struct ieee80211_action_ba_addbaresponse) ); if (m != NULL) { *frm++ = category; *frm++ = action; *frm++ = IEEE80211_IS_CHAN_HT40(ni->ni_chan) ? IEEE80211_A_HT_TXCHWIDTH_2040 : IEEE80211_A_HT_TXCHWIDTH_20; m->m_pkthdr.len = m->m_len = frm - mtod(m, uint8_t *); return ht_action_output(ni, m); } else { vap->iv_stats.is_tx_nobuf++; ieee80211_free_node(ni); return ENOMEM; } } #undef ADDSHORT /* * Construct the MCS bit mask for inclusion in an HT capabilities * information element. */ static void ieee80211_set_mcsset(struct ieee80211com *ic, uint8_t *frm) { int i; uint8_t txparams; KASSERT((ic->ic_rxstream > 0 && ic->ic_rxstream <= 4), ("ic_rxstream %d out of range", ic->ic_rxstream)); KASSERT((ic->ic_txstream > 0 && ic->ic_txstream <= 4), ("ic_txstream %d out of range", ic->ic_txstream)); for (i = 0; i < ic->ic_rxstream * 8; i++) setbit(frm, i); if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) && (ic->ic_htcaps & IEEE80211_HTC_RXMCS32)) setbit(frm, 32); if (ic->ic_htcaps & IEEE80211_HTC_RXUNEQUAL) { if (ic->ic_rxstream >= 2) { for (i = 33; i <= 38; i++) setbit(frm, i); } if (ic->ic_rxstream >= 3) { for (i = 39; i <= 52; i++) setbit(frm, i); } if (ic->ic_rxstream >= 4) { for (i = 53; i <= 76; i++) setbit(frm, i); } } txparams = 0x1; /* TX MCS set defined */ if (ic->ic_rxstream != ic->ic_txstream) { txparams |= 0x2; /* TX RX MCS not equal */ txparams |= (ic->ic_txstream - 1) << 2; /* num TX streams */ if (ic->ic_htcaps & IEEE80211_HTC_TXUNEQUAL) txparams |= 0x16; /* TX unequal modulation sup */ } frm[12] = txparams; } /* * Add body of an HTCAP information element. */ static uint8_t * ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni) { #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) struct ieee80211com *ic = ni->ni_ic; struct ieee80211vap *vap = ni->ni_vap; uint16_t caps, extcaps; int rxmax, density; /* HT capabilities */ caps = vap->iv_htcaps & 0xffff; /* * Note channel width depends on whether we are operating as * a sta or not. When operating as a sta we are generating * a request based on our desired configuration. Otherwise * we are operational and the channel attributes identify * how we've been setup (which might be different if a fixed * channel is specified). */ if (vap->iv_opmode == IEEE80211_M_STA) { /* override 20/40 use based on config */ if (vap->iv_flags_ht & IEEE80211_FHT_USEHT40) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; /* Start by using the advertised settings */ rxmax = _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU); density = _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY); IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N, "%s: advertised rxmax=%d, density=%d, vap rxmax=%d, density=%d\n", __func__, rxmax, density, vap->iv_ampdu_rxmax, vap->iv_ampdu_density); /* Cap at VAP rxmax */ if (rxmax > vap->iv_ampdu_rxmax) rxmax = vap->iv_ampdu_rxmax; /* * If the VAP ampdu density value greater, use that. * * (Larger density value == larger minimum gap between A-MPDU * subframes.) */ if (vap->iv_ampdu_density > density) density = vap->iv_ampdu_density; /* * NB: Hardware might support HT40 on some but not all * channels. We can't determine this earlier because only * after association the channel is upgraded to HT based * on the negotiated capabilities. */ if (ni->ni_chan != IEEE80211_CHAN_ANYC && findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40U) == NULL && findhtchan(ic, ni->ni_chan, IEEE80211_CHAN_HT40D) == NULL) caps &= ~IEEE80211_HTCAP_CHWIDTH40; } else { /* override 20/40 use based on current channel */ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; /* XXX TODO should it start by using advertised settings? */ rxmax = vap->iv_ampdu_rxmax; density = vap->iv_ampdu_density; } /* adjust short GI based on channel and config */ if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI20; if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 || (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI40; /* adjust STBC based on receive capabilities */ if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0) caps &= ~IEEE80211_HTCAP_RXSTBC; /* adjust LDPC based on receive capabilites */ if ((vap->iv_flags_ht & IEEE80211_FHT_LDPC_RX) == 0) caps &= ~IEEE80211_HTCAP_LDPC; ADDSHORT(frm, caps); /* HT parameters */ *frm = _IEEE80211_SHIFTMASK(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) | _IEEE80211_SHIFTMASK(density, IEEE80211_HTCAP_MPDUDENSITY) ; frm++; /* pre-zero remainder of ie */ memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); /* supported MCS set */ /* * XXX: For sta mode the rate set should be restricted based * on the AP's capabilities, but ni_htrates isn't setup when * we're called to form an AssocReq frame so for now we're * restricted to the device capabilities. */ ieee80211_set_mcsset(ni->ni_ic, frm); frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset); /* HT extended capabilities */ extcaps = vap->iv_htextcaps & 0xffff; ADDSHORT(frm, extcaps); frm += sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_txbf); return frm; #undef ADDSHORT } /* * Add 802.11n HT capabilities information element */ uint8_t * ieee80211_add_htcap(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_HTCAP; frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; return ieee80211_add_htcap_body(frm + 2, ni); } /* * Non-associated probe request - add HT capabilities based on * the current channel configuration. */ static uint8_t * ieee80211_add_htcap_body_ch(uint8_t *frm, struct ieee80211vap *vap, struct ieee80211_channel *c) { #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) struct ieee80211com *ic = vap->iv_ic; uint16_t caps, extcaps; int rxmax, density; /* HT capabilities */ caps = vap->iv_htcaps & 0xffff; /* * We don't use this in STA mode; only in IBSS mode. * So in IBSS mode we base our HTCAP flags on the * given channel. */ /* override 20/40 use based on current channel */ if (IEEE80211_IS_CHAN_HT40(c)) caps |= IEEE80211_HTCAP_CHWIDTH40; else caps &= ~IEEE80211_HTCAP_CHWIDTH40; /* Use the currently configured values */ rxmax = vap->iv_ampdu_rxmax; density = vap->iv_ampdu_density; /* adjust short GI based on channel and config */ if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI20) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI20; if ((vap->iv_flags_ht & IEEE80211_FHT_SHORTGI40) == 0 || (caps & IEEE80211_HTCAP_CHWIDTH40) == 0) caps &= ~IEEE80211_HTCAP_SHORTGI40; ADDSHORT(frm, caps); /* HT parameters */ *frm = _IEEE80211_SHIFTMASK(rxmax, IEEE80211_HTCAP_MAXRXAMPDU) | _IEEE80211_SHIFTMASK(density, IEEE80211_HTCAP_MPDUDENSITY) ; frm++; /* pre-zero remainder of ie */ memset(frm, 0, sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset)); /* supported MCS set */ /* * XXX: For sta mode the rate set should be restricted based * on the AP's capabilities, but ni_htrates isn't setup when * we're called to form an AssocReq frame so for now we're * restricted to the device capabilities. */ ieee80211_set_mcsset(ic, frm); frm += __offsetof(struct ieee80211_ie_htcap, hc_extcap) - __offsetof(struct ieee80211_ie_htcap, hc_mcsset); /* HT extended capabilities */ extcaps = vap->iv_htextcaps & 0xffff; ADDSHORT(frm, extcaps); frm += sizeof(struct ieee80211_ie_htcap) - __offsetof(struct ieee80211_ie_htcap, hc_txbf); return frm; #undef ADDSHORT } /* * Add 802.11n HT capabilities information element */ uint8_t * ieee80211_add_htcap_ch(uint8_t *frm, struct ieee80211vap *vap, struct ieee80211_channel *c) { frm[0] = IEEE80211_ELEMID_HTCAP; frm[1] = sizeof(struct ieee80211_ie_htcap) - 2; return ieee80211_add_htcap_body_ch(frm + 2, vap, c); } /* * Add Broadcom OUI wrapped standard HTCAP ie; this is * used for compatibility w/ pre-draft implementations. */ uint8_t * ieee80211_add_htcap_vendor(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_VENDOR; frm[1] = 4 + sizeof(struct ieee80211_ie_htcap) - 2; frm[2] = (BCM_OUI >> 0) & 0xff; frm[3] = (BCM_OUI >> 8) & 0xff; frm[4] = (BCM_OUI >> 16) & 0xff; frm[5] = BCM_OUI_HTCAP; return ieee80211_add_htcap_body(frm + 6, ni); } /* * Construct the MCS bit mask of basic rates * for inclusion in an HT information element. */ static void ieee80211_set_basic_htrates(uint8_t *frm, const struct ieee80211_htrateset *rs) { int i; for (i = 0; i < rs->rs_nrates; i++) { int r = rs->rs_rates[i] & IEEE80211_RATE_VAL; if ((rs->rs_rates[i] & IEEE80211_RATE_BASIC) && r < IEEE80211_HTRATE_MAXSIZE) { /* NB: this assumes a particular implementation */ setbit(frm, r); } } } /* * Update the HTINFO ie for a beacon frame. */ void ieee80211_ht_update_beacon(struct ieee80211vap *vap, struct ieee80211_beacon_offsets *bo) { #define PROTMODE (IEEE80211_HTINFO_OPMODE|IEEE80211_HTINFO_NONHT_PRESENT) struct ieee80211_node *ni; const struct ieee80211_channel *bsschan; struct ieee80211com *ic = vap->iv_ic; struct ieee80211_ie_htinfo *ht = (struct ieee80211_ie_htinfo *) bo->bo_htinfo; ni = ieee80211_ref_node(vap->iv_bss); bsschan = ni->ni_chan; /* XXX only update on channel change */ ht->hi_ctrlchannel = ieee80211_chan2ieee(ic, bsschan); if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PERM; else ht->hi_byte1 = IEEE80211_HTINFO_RIFSMODE_PROH; if (IEEE80211_IS_CHAN_HT40U(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_BELOW; else ht->hi_byte1 |= IEEE80211_HTINFO_2NDCHAN_NONE; if (IEEE80211_IS_CHAN_HT40(bsschan)) ht->hi_byte1 |= IEEE80211_HTINFO_TXWIDTH_2040; /* protection mode */ /* * XXX TODO: this uses the global flag, not the per-VAP flag. * Eventually (once the protection modes are done per-channel * rather than per-VAP) we can flip this over to be per-VAP but * using the channel protection mode. */ ht->hi_byte2 = (ht->hi_byte2 &~ PROTMODE) | ic->ic_curhtprotmode; ieee80211_free_node(ni); /* XXX propagate to vendor ie's */ #undef PROTMODE } /* * Add body of an HTINFO information element. * * NB: We don't use struct ieee80211_ie_htinfo because we can * be called to fillin both a standard ie and a compat ie that * has a vendor OUI at the front. */ static uint8_t * ieee80211_add_htinfo_body(uint8_t *frm, struct ieee80211_node *ni) { struct ieee80211vap *vap = ni->ni_vap; struct ieee80211com *ic = ni->ni_ic; /* pre-zero remainder of ie */ memset(frm, 0, sizeof(struct ieee80211_ie_htinfo) - 2); /* primary/control channel center */ *frm++ = ieee80211_chan2ieee(ic, ni->ni_chan); if (vap->iv_flags_ht & IEEE80211_FHT_RIFS) frm[0] = IEEE80211_HTINFO_RIFSMODE_PERM; else frm[0] = IEEE80211_HTINFO_RIFSMODE_PROH; if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_2NDCHAN_ABOVE; else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_2NDCHAN_BELOW; else frm[0] |= IEEE80211_HTINFO_2NDCHAN_NONE; if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) frm[0] |= IEEE80211_HTINFO_TXWIDTH_2040; /* * Add current protection mode. Unlike for beacons, * this will respect the per-VAP flags. */ frm[1] = vap->iv_curhtprotmode; frm += 5; /* basic MCS set */ ieee80211_set_basic_htrates(frm, &ni->ni_htrates); frm += sizeof(struct ieee80211_ie_htinfo) - __offsetof(struct ieee80211_ie_htinfo, hi_basicmcsset); return frm; } /* * Add 802.11n HT information element. */ uint8_t * ieee80211_add_htinfo(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_HTINFO; frm[1] = sizeof(struct ieee80211_ie_htinfo) - 2; return ieee80211_add_htinfo_body(frm + 2, ni); } /* * Add Broadcom OUI wrapped standard HTINFO ie; this is * used for compatibility w/ pre-draft implementations. */ uint8_t * ieee80211_add_htinfo_vendor(uint8_t *frm, struct ieee80211_node *ni) { frm[0] = IEEE80211_ELEMID_VENDOR; frm[1] = 4 + sizeof(struct ieee80211_ie_htinfo) - 2; frm[2] = (BCM_OUI >> 0) & 0xff; frm[3] = (BCM_OUI >> 8) & 0xff; frm[4] = (BCM_OUI >> 16) & 0xff; frm[5] = BCM_OUI_HTINFO; return ieee80211_add_htinfo_body(frm + 6, ni); } diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 1208a8b3ab4e..08812c5eb27c 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -1,1106 +1,1105 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2001 Atsushi Onoe * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _NET80211_IEEE80211_VAR_H_ #define _NET80211_IEEE80211_VAR_H_ /* * Definitions for IEEE 802.11 drivers. */ /* NB: portability glue must go first */ #if defined(__NetBSD__) #include #elif defined(__FreeBSD__) #include #elif defined(__linux__) #include #else #error "No support for your operating system!" #endif #include #include #include #include #include #include /* for ieee80211_stats */ #include #include #include #include #include #include #define IEEE80211_TXPOWER_MAX 100 /* .5 dBm (XXX units?) */ #define IEEE80211_TXPOWER_MIN 0 /* kill radio */ #define IEEE80211_DTIM_DEFAULT 1 /* default DTIM period */ #define IEEE80211_BINTVAL_DEFAULT 100 /* default beacon interval (TU's) */ #define IEEE80211_BMISS_MAX 2 /* maximum consecutive bmiss allowed */ #define IEEE80211_HWBMISS_DEFAULT 7 /* h/w bmiss threshold (beacons) */ #define IEEE80211_BGSCAN_INTVAL_MIN 15 /* min bg scan intvl (secs) */ #define IEEE80211_BGSCAN_INTVAL_DEFAULT (5*60) /* default bg scan intvl */ #define IEEE80211_BGSCAN_IDLE_MIN 100 /* min idle time (ms) */ #define IEEE80211_BGSCAN_IDLE_DEFAULT 250 /* default idle time (ms) */ #define IEEE80211_SCAN_VALID_MIN 10 /* min scan valid time (secs) */ #define IEEE80211_SCAN_VALID_DEFAULT 60 /* default scan valid time */ #define IEEE80211_PS_SLEEP 0x1 /* STA is in power saving mode */ #define IEEE80211_PS_MAX_QUEUE 50 /* maximum saved packets */ #define IEEE80211_FIXED_RATE_NONE 0xff #define IEEE80211_TXMAX_DEFAULT 6 /* default ucast max retries */ #define IEEE80211_RTS_DEFAULT IEEE80211_RTS_MAX #define IEEE80211_FRAG_DEFAULT IEEE80211_FRAG_MAX #define IEEE80211_MS_TO_TU(x) (((x) * 1000) / 1024) #define IEEE80211_TU_TO_MS(x) (((x) * 1024) / 1000) /* XXX TODO: cap this at 1, in case hz is not 1000 */ #define IEEE80211_TU_TO_TICKS(x)(((uint64_t)(x) * 1024 * hz) / (1000 * 1000)) /* * Technically, vhtflags may be 0 /and/ 11ac is enabled. * At some point ic should just grow a flag somewhere that * says that VHT is supported - and then this macro can be * changed. */ #define IEEE80211_CONF_VHT(ic) \ ((ic)->ic_flags_ext & IEEE80211_FEXT_VHT) #define IEEE80211_CONF_SEQNO_OFFLOAD(ic) \ ((ic)->ic_flags_ext & IEEE80211_FEXT_SEQNO_OFFLOAD) #define IEEE80211_CONF_FRAG_OFFLOAD(ic) \ ((ic)->ic_flags_ext & IEEE80211_FEXT_FRAG_OFFLOAD) /* * 802.11 control state is split into a common portion that maps * 1-1 to a physical device and one or more "Virtual AP's" (VAP) * that are bound to an ieee80211com instance and share a single * underlying device. Each VAP has a corresponding OS device * entity through which traffic flows and that applications use * for issuing ioctls, etc. */ /* * Data common to one or more virtual AP's. State shared by * the underlying device and the net80211 layer is exposed here; * e.g. device-specific callbacks. */ struct ieee80211vap; typedef void (*ieee80211vap_attach)(struct ieee80211vap *); struct ieee80211_appie { uint16_t ie_len; /* size of ie_data */ uint8_t ie_data[]; /* user-specified IE's */ }; struct ieee80211_tdma_param; struct ieee80211_rate_table; struct ieee80211_tx_ampdu; struct ieee80211_rx_ampdu; struct ieee80211_superg; struct ieee80211_frame; struct net80211dump_methods; struct ieee80211com { void *ic_softc; /* driver softc */ const char *ic_name; /* usually device name */ ieee80211_com_lock_t ic_comlock; /* state update lock */ ieee80211_tx_lock_t ic_txlock; /* ic/vap TX lock */ ieee80211_ff_lock_t ic_fflock; /* stageq/ni_tx_superg lock */ LIST_ENTRY(ieee80211com) ic_next; /* on global list */ TAILQ_HEAD(, ieee80211vap) ic_vaps; /* list of vap instances */ int ic_headroom; /* driver tx headroom needs */ enum ieee80211_phytype ic_phytype; /* XXX wrong for multi-mode */ enum ieee80211_opmode ic_opmode; /* operation mode */ struct callout ic_inact; /* inactivity processing */ struct taskqueue *ic_tq; /* deferred state thread */ struct task ic_parent_task; /* deferred parent processing */ struct task ic_promisc_task;/* deferred promisc update */ struct task ic_mcast_task; /* deferred mcast update */ struct task ic_chan_task; /* deferred channel change */ struct task ic_bmiss_task; /* deferred beacon miss hndlr */ struct task ic_chw_task; /* deferred HT CHW update */ struct task ic_restart_task; /* deferred device restart */ counter_u64_t ic_ierrors; /* input errors */ counter_u64_t ic_oerrors; /* output errors */ uint32_t ic_flags; /* state flags */ uint32_t ic_flags_ext; /* extended state flags */ uint32_t ic_flags_ht; /* HT state flags */ uint32_t ic_flags_ven; /* vendor state flags */ uint32_t ic_caps; /* capabilities */ uint32_t ic_htcaps; /* HT capabilities */ uint32_t ic_htextcaps; /* HT extended capabilities */ uint32_t ic_cryptocaps; /* crypto capabilities */ /* set of mode capabilities */ uint8_t ic_modecaps[IEEE80211_MODE_BYTES]; uint8_t ic_promisc; /* vap's needing promisc mode */ uint8_t ic_allmulti; /* vap's needing all multicast*/ uint8_t ic_nrunning; /* vap's marked running */ uint8_t ic_curmode; /* current mode */ uint8_t ic_macaddr[IEEE80211_ADDR_LEN]; uint16_t ic_bintval; /* beacon interval */ uint16_t ic_lintval; /* listen interval */ uint16_t ic_holdover; /* PM hold over duration */ uint16_t ic_txpowlimit; /* global tx power limit */ struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX]; struct ieee80211_htrateset ic_sup_htrates; /* * Channel state: * * ic_channels is the set of available channels for the device; * it is setup by the driver * ic_nchans is the number of valid entries in ic_channels * ic_chan_avail is a bit vector of these channels used to check * whether a channel is available w/o searching the channel table. * ic_chan_active is a (potentially) constrained subset of * ic_chan_avail that reflects any mode setting or user-specified * limit on the set of channels to use/scan * ic_curchan is the current channel the device is set to; it may * be different from ic_bsschan when we are off-channel scanning * or otherwise doing background work * ic_bsschan is the channel selected for operation; it may * be undefined (IEEE80211_CHAN_ANYC) * ic_prevchan is a cached ``previous channel'' used to optimize * lookups when switching back+forth between two channels * (e.g. for dynamic turbo) */ int ic_nchans; /* # entries in ic_channels */ struct ieee80211_channel ic_channels[IEEE80211_CHAN_MAX]; uint8_t ic_chan_avail[IEEE80211_CHAN_BYTES]; uint8_t ic_chan_active[IEEE80211_CHAN_BYTES]; uint8_t ic_chan_scan[IEEE80211_CHAN_BYTES]; struct ieee80211_channel *ic_curchan; /* current channel */ const struct ieee80211_rate_table *ic_rt; /* table for ic_curchan */ struct ieee80211_channel *ic_bsschan; /* bss channel */ struct ieee80211_channel *ic_prevchan; /* previous channel */ struct ieee80211_regdomain ic_regdomain;/* regulatory data */ struct ieee80211_appie *ic_countryie; /* calculated country ie */ struct ieee80211_channel *ic_countryie_chan; /* 802.11h/DFS state */ struct ieee80211_channel *ic_csa_newchan;/* channel for doing CSA */ short ic_csa_mode; /* mode for doing CSA */ short ic_csa_count; /* count for doing CSA */ struct ieee80211_dfs_state ic_dfs; /* DFS state */ struct ieee80211_scan_state *ic_scan; /* scan state */ struct ieee80211_scan_methods *ic_scan_methods; /* scan methods */ int ic_lastdata; /* time of last data frame */ int ic_lastscan; /* time last scan completed */ /* NB: this is the union of all vap stations/neighbors */ int ic_max_keyix; /* max h/w key index */ struct ieee80211_node_table ic_sta; /* stations/neighbors */ struct ieee80211_ageq ic_stageq; /* frame staging queue */ uint32_t ic_hash_key; /* random key for mac hash */ /* XXX multi-bss: split out common/vap parts */ struct ieee80211_wme_state ic_wme; /* WME/WMM state */ /* Protection mode for net80211 driven channel NICs */ enum ieee80211_protmode ic_protmode; /* 802.11g protection mode */ enum ieee80211_protmode ic_htprotmode; /* HT protection mode */ uint8_t ic_curhtprotmode;/* HTINFO bss state */ uint8_t ic_rxstream; /* # RX streams */ uint8_t ic_txstream; /* # TX streams */ /* VHT information */ uint32_t ic_vhtcaps; /* VHT capabilities */ uint32_t ic_vhtextcaps; /* VHT extended capabilities (TODO) */ struct ieee80211_vht_mcs_info ic_vht_mcsinfo; /* Support TX/RX VHT MCS */ uint32_t ic_flags_vht; /* VHT state flags */ uint32_t ic_vht_spare[3]; /* optional state for Atheros SuperG protocol extensions */ struct ieee80211_superg *ic_superg; /* radiotap handling */ struct ieee80211_radiotap_header *ic_th;/* tx radiotap headers */ void *ic_txchan; /* channel state in ic_th */ struct ieee80211_radiotap_header *ic_rh;/* rx radiotap headers */ void *ic_rxchan; /* channel state in ic_rh */ int ic_montaps; /* active monitor mode taps */ /* virtual ap create/delete */ struct ieee80211vap* (*ic_vap_create)(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); void (*ic_vap_delete)(struct ieee80211vap *); /* device specific ioctls */ int (*ic_ioctl)(struct ieee80211com *, u_long, void *); /* start/stop device */ void (*ic_parent)(struct ieee80211com *); /* operating mode attachment */ ieee80211vap_attach ic_vattach[IEEE80211_OPMODE_MAX]; /* return hardware/radio capabilities */ void (*ic_getradiocaps)(struct ieee80211com *, int, int *, struct ieee80211_channel []); /* check and/or prepare regdomain state change */ int (*ic_setregdomain)(struct ieee80211com *, struct ieee80211_regdomain *, int, struct ieee80211_channel []); int (*ic_set_quiet)(struct ieee80211_node *, u_int8_t *quiet_elm); /* regular transmit */ int (*ic_transmit)(struct ieee80211com *, struct mbuf *); /* send/recv 802.11 management frame */ int (*ic_send_mgmt)(struct ieee80211_node *, int, int); /* send raw 802.11 frame */ int (*ic_raw_xmit)(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); /* update device state for 802.11 slot time change */ void (*ic_updateslot)(struct ieee80211com *); /* handle multicast state changes */ void (*ic_update_mcast)(struct ieee80211com *); /* handle promiscuous mode changes */ void (*ic_update_promisc)(struct ieee80211com *); /* new station association callback/notification */ void (*ic_newassoc)(struct ieee80211_node *, int); /* TDMA update notification */ void (*ic_tdma_update)(struct ieee80211_node *, const struct ieee80211_tdma_param *, int); /* Node state management */ /* Allocate a new node */ struct ieee80211_node* (*ic_node_alloc)(struct ieee80211vap *, const uint8_t [IEEE80211_ADDR_LEN]); /* Driver node initialisation after net80211 setup */ int (*ic_node_init)(struct ieee80211_node *); /* Driver node deallocation */ void (*ic_node_free)(struct ieee80211_node *); /* Driver node state cleanup before deallocation */ void (*ic_node_cleanup)(struct ieee80211_node *); void (*ic_node_age)(struct ieee80211_node *); void (*ic_node_drain)(struct ieee80211_node *); int8_t (*ic_node_getrssi)(const struct ieee80211_node*); void (*ic_node_getsignal)(const struct ieee80211_node*, int8_t *, int8_t *); void (*ic_node_getmimoinfo)( const struct ieee80211_node*, struct ieee80211_mimo_info *); /* scanning support */ void (*ic_scan_start)(struct ieee80211com *); void (*ic_scan_end)(struct ieee80211com *); void (*ic_set_channel)(struct ieee80211com *); void (*ic_scan_curchan)(struct ieee80211_scan_state *, unsigned long); void (*ic_scan_mindwell)(struct ieee80211_scan_state *); /* * 802.11n ADDBA support. A simple/generic implementation * of A-MPDU tx aggregation is provided; the driver may * override these methods to provide their own support. * A-MPDU rx re-ordering happens automatically if the * driver passes out-of-order frames to ieee80211_input * from an assocated HT station. */ int (*ic_recv_action)(struct ieee80211_node *, const struct ieee80211_frame *, const uint8_t *frm, const uint8_t *efrm); int (*ic_send_action)(struct ieee80211_node *, int category, int action, void *); /* check if A-MPDU should be enabled this station+ac */ int (*ic_ampdu_enable)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); /* start/stop doing A-MPDU tx aggregation for a station */ int (*ic_addba_request)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int dialogtoken, int baparamset, int batimeout); int (*ic_addba_response)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int status, int baparamset, int batimeout); void (*ic_addba_stop)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); void (*ic_addba_response_timeout)(struct ieee80211_node *, struct ieee80211_tx_ampdu *); /* BAR response received */ void (*ic_bar_response)(struct ieee80211_node *, struct ieee80211_tx_ampdu *, int status); /* start/stop doing A-MPDU rx processing for a station */ int (*ic_ampdu_rx_start)(struct ieee80211_node *, struct ieee80211_rx_ampdu *, int baparamset, int batimeout, int baseqctl); void (*ic_ampdu_rx_stop)(struct ieee80211_node *, struct ieee80211_rx_ampdu *); /* The channel width has changed (20<->2040) */ void (*ic_update_chw)(struct ieee80211com *); const struct debugnet80211_methods *ic_debugnet_meth; uint64_t ic_spare[7]; }; struct ieee80211_aclator; struct ieee80211_tdma_state; struct ieee80211_mesh_state; struct ieee80211_hwmp_state; struct ieee80211_rx_histogram; struct ieee80211_tx_histogram; struct ieee80211vap { struct ifmedia iv_media; /* interface media config */ struct ifnet *iv_ifp; /* associated device */ struct bpf_if *iv_rawbpf; /* packet filter structure */ struct sysctl_ctx_list *iv_sysctl; /* dynamic sysctl context */ struct sysctl_oid *iv_oid; /* net.wlan.X sysctl oid */ TAILQ_ENTRY(ieee80211vap) iv_next; /* list of vap instances */ struct ieee80211com *iv_ic; /* back ptr to common state */ /* MAC address: ifp or ic */ uint8_t iv_myaddr[IEEE80211_ADDR_LEN]; uint32_t iv_debug; /* debug msg flags */ struct ieee80211_stats iv_stats; /* statistics */ uint32_t iv_flags; /* state flags */ uint32_t iv_flags_ext; /* extended state flags */ uint32_t iv_flags_ht; /* HT state flags */ uint32_t iv_flags_ven; /* vendor state flags */ uint32_t iv_ifflags; /* ifnet flags */ uint32_t iv_caps; /* capabilities */ uint32_t iv_htcaps; /* HT capabilities */ uint32_t iv_htextcaps; /* HT extended capabilities */ uint32_t iv_com_state; /* com usage / detached flag */ enum ieee80211_opmode iv_opmode; /* operation mode */ enum ieee80211_state iv_state; /* state machine state */ enum ieee80211_state iv_nstate; /* pending state */ int iv_nstate_arg; /* pending state arg */ struct task iv_nstate_task; /* deferred state processing */ struct task iv_swbmiss_task;/* deferred iv_bmiss call */ struct callout iv_mgtsend; /* mgmt frame response timer */ /* inactivity timer settings */ int iv_inact_init; /* setting for new station */ int iv_inact_auth; /* auth but not assoc setting */ int iv_inact_run; /* authorized setting */ int iv_inact_probe; /* inactive probe time */ /* VHT flags */ uint32_t iv_vht_flags; /* VHT state flags */ - uint32_t iv_vhtcaps; /* VHT capabilities */ + struct ieee80211_vht_cap iv_vht_cap; /* VHT capabilities + MCS info */ uint32_t iv_vhtextcaps; /* VHT extended capabilities (TODO) */ - struct ieee80211_vht_mcs_info iv_vht_mcsinfo; uint32_t iv_vht_spare[4]; int iv_des_nssid; /* # desired ssids */ struct ieee80211_scan_ssid iv_des_ssid[1];/* desired ssid table */ uint8_t iv_des_bssid[IEEE80211_ADDR_LEN]; struct ieee80211_channel *iv_des_chan; /* desired channel */ uint16_t iv_des_mode; /* desired mode */ int iv_nicknamelen; /* XXX junk */ uint8_t iv_nickname[IEEE80211_NWID_LEN]; u_int iv_bgscanidle; /* bg scan idle threshold */ u_int iv_bgscanintvl; /* bg scan min interval */ u_int iv_scanvalid; /* scan cache valid threshold */ u_int iv_scanreq_duration; u_int iv_scanreq_mindwell; u_int iv_scanreq_maxdwell; uint16_t iv_scanreq_flags;/* held scan request params */ uint8_t iv_scanreq_nssid; struct ieee80211_scan_ssid iv_scanreq_ssid[IEEE80211_SCAN_MAX_SSID]; /* sta-mode roaming state */ enum ieee80211_roamingmode iv_roaming; /* roaming mode */ struct ieee80211_roamparam iv_roamparms[IEEE80211_MODE_MAX]; uint8_t iv_bmissthreshold; uint8_t iv_bmiss_count; /* current beacon miss count */ int iv_bmiss_max; /* max bmiss before scan */ uint16_t iv_swbmiss_count;/* beacons in last period */ uint16_t iv_swbmiss_period;/* s/w bmiss period */ struct callout iv_swbmiss; /* s/w beacon miss timer */ int iv_ampdu_rxmax; /* A-MPDU rx limit (bytes) */ int iv_ampdu_density;/* A-MPDU density */ int iv_ampdu_limit; /* A-MPDU tx limit (bytes) */ int iv_amsdu_limit; /* A-MSDU tx limit (bytes) */ u_int iv_ampdu_mintraffic[WME_NUM_AC]; struct ieee80211_beacon_offsets iv_bcn_off; uint32_t *iv_aid_bitmap; /* association id map */ uint16_t iv_max_aid; uint16_t iv_sta_assoc; /* stations associated */ uint16_t iv_ps_sta; /* stations in power save */ uint16_t iv_ps_pending; /* ps sta's w/ pending frames */ uint16_t iv_txseq; /* mcast xmit seq# space */ uint16_t iv_tim_len; /* ic_tim_bitmap size (bytes) */ uint8_t *iv_tim_bitmap; /* power-save stations w/ data*/ uint8_t iv_dtim_period; /* DTIM period */ uint8_t iv_dtim_count; /* DTIM count from last bcn */ /* set/unset aid pwrsav state */ uint8_t iv_quiet; /* Quiet Element */ uint8_t iv_quiet_count; /* constant count for Quiet Element */ uint8_t iv_quiet_count_value; /* variable count for Quiet Element */ uint8_t iv_quiet_period; /* period for Quiet Element */ uint16_t iv_quiet_duration; /* duration for Quiet Element */ uint16_t iv_quiet_offset; /* offset for Quiet Element */ int iv_csa_count; /* count for doing CSA */ struct ieee80211_node *iv_bss; /* information for this node */ struct ieee80211_txparam iv_txparms[IEEE80211_MODE_MAX]; uint16_t iv_rtsthreshold; uint16_t iv_fragthreshold; int iv_inact_timer; /* inactivity timer wait */ /* application-specified IE's to attach to mgt frames */ struct ieee80211_appie *iv_appie_beacon; struct ieee80211_appie *iv_appie_probereq; struct ieee80211_appie *iv_appie_proberesp; struct ieee80211_appie *iv_appie_assocreq; struct ieee80211_appie *iv_appie_assocresp; struct ieee80211_appie *iv_appie_wpa; uint8_t *iv_wpa_ie; uint8_t *iv_rsn_ie; /* Key management */ uint16_t iv_max_keyix; /* max h/w key index */ ieee80211_keyix iv_def_txkey; /* default/group tx key index */ struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID]; int (*iv_key_alloc)(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); int (*iv_key_delete)(struct ieee80211vap *, const struct ieee80211_key *); int (*iv_key_set)(struct ieee80211vap *, const struct ieee80211_key *); void (*iv_key_update_begin)(struct ieee80211vap *); void (*iv_key_update_end)(struct ieee80211vap *); void (*iv_update_deftxkey)(struct ieee80211vap *, ieee80211_keyix deftxkey); const struct ieee80211_authenticator *iv_auth; /* authenticator glue */ void *iv_ec; /* private auth state */ const struct ieee80211_aclator *iv_acl; /* acl glue */ void *iv_as; /* private aclator state */ const struct ieee80211_ratectl *iv_rate; void *iv_rs; /* private ratectl state */ struct ieee80211_tdma_state *iv_tdma; /* tdma state */ struct ieee80211_mesh_state *iv_mesh; /* MBSS state */ struct ieee80211_hwmp_state *iv_hwmp; /* HWMP state */ /* operate-mode detach hook */ void (*iv_opdetach)(struct ieee80211vap *); /* receive processing */ int (*iv_input)(struct ieee80211_node *, struct mbuf *, const struct ieee80211_rx_stats *, int, int); void (*iv_recv_mgmt)(struct ieee80211_node *, struct mbuf *, int, const struct ieee80211_rx_stats *, int, int); void (*iv_recv_ctl)(struct ieee80211_node *, struct mbuf *, int); void (*iv_deliver_data)(struct ieee80211vap *, struct ieee80211_node *, struct mbuf *); #if 0 /* send processing */ int (*iv_send_mgmt)(struct ieee80211_node *, int, int); #endif /* beacon miss processing */ void (*iv_bmiss)(struct ieee80211vap *); /* reset device state after 802.11 parameter/state change */ int (*iv_reset)(struct ieee80211vap *, u_long); /* [schedule] beacon frame update */ void (*iv_update_beacon)(struct ieee80211vap *, int); /* power save handling */ void (*iv_update_ps)(struct ieee80211vap *, int); int (*iv_set_tim)(struct ieee80211_node *, int); void (*iv_node_ps)(struct ieee80211_node *, int); void (*iv_sta_ps)(struct ieee80211vap *, int); void (*iv_recv_pspoll)(struct ieee80211_node *, struct mbuf *); /* state machine processing */ int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *, struct ieee80211_node *); /* 802.3 output method for raw frame xmit */ int (*iv_output)(struct ifnet *, struct mbuf *, const struct sockaddr *, struct route *); int (*iv_wme_update)(struct ieee80211vap *, const struct wmeParams *wme_params); struct task iv_wme_task; /* deferred VAP WME update */ /* associated state; protection mode */ enum ieee80211_protmode iv_protmode; /* 802.11g protection mode */ enum ieee80211_protmode iv_htprotmode; /* HT protection mode */ uint8_t iv_curhtprotmode;/* HTINFO bss state */ uint16_t iv_nonerpsta; /* # non-ERP stations */ uint16_t iv_longslotsta; /* # long slot time stations */ uint16_t iv_ht_sta_assoc;/* HT stations associated */ uint16_t iv_ht40_sta_assoc;/* HT40 stations associated */ int iv_lastnonerp; /* last time non-ERP sta noted*/ int iv_lastnonht; /* last time non-HT sta noted */ /* update device state for 802.11 slot time change */ void (*iv_updateslot)(struct ieee80211vap *); struct task iv_slot_task; /* deferred slot time update */ struct task iv_erp_protmode_task; /* deferred ERP protmode update */ void (*iv_erp_protmode_update)(struct ieee80211vap *); struct task iv_preamble_task; /* deferred short/barker preamble update */ void (*iv_preamble_update)(struct ieee80211vap *); struct task iv_ht_protmode_task; /* deferred HT protmode update */ void (*iv_ht_protmode_update)(struct ieee80211vap *); /* per-vap U-APSD state */ uint8_t iv_uapsdinfo; /* sta mode QoS Info flags */ /* Optional transmit/receive histogram statistics */ struct ieee80211_rx_histogram *rx_histogram; struct ieee80211_tx_histogram *tx_histogram; uint64_t iv_spare[6]; }; MALLOC_DECLARE(M_80211_VAP); #define IEEE80211_ADDR_EQ(a1,a2) (memcmp(a1,a2,IEEE80211_ADDR_LEN) == 0) #define IEEE80211_ADDR_COPY(dst,src) memcpy(dst,src,IEEE80211_ADDR_LEN) /* ic_flags/iv_flags */ #define IEEE80211_F_TURBOP 0x00000001 /* CONF: ATH Turbo enabled*/ #define IEEE80211_F_COMP 0x00000002 /* CONF: ATH comp enabled */ #define IEEE80211_F_FF 0x00000004 /* CONF: ATH FF enabled */ #define IEEE80211_F_BURST 0x00000008 /* CONF: bursting enabled */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_PRIVACY */ #define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */ #define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */ #define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */ /* 0x00000300 reserved */ /* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */ #define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/ #define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */ #define IEEE80211_F_DESBSSID 0x00001000 /* CONF: des_bssid is set */ #define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */ #define IEEE80211_F_BGSCAN 0x00004000 /* CONF: bg scan enabled (???)*/ #define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */ /* 0x00030000 reserved */ #define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */ #define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */ #define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */ #define IEEE80211_F_USEBARKER 0x00200000 /* STATUS: use barker preamble*/ #define IEEE80211_F_CSAPENDING 0x00400000 /* STATUS: chan switch pending*/ #define IEEE80211_F_WPA1 0x00800000 /* CONF: WPA enabled */ #define IEEE80211_F_WPA2 0x01000000 /* CONF: WPA2 enabled */ #define IEEE80211_F_WPA 0x01800000 /* CONF: WPA/WPA2 enabled */ #define IEEE80211_F_DROPUNENC 0x02000000 /* CONF: drop unencrypted */ #define IEEE80211_F_COUNTERM 0x04000000 /* CONF: TKIP countermeasures */ #define IEEE80211_F_HIDESSID 0x08000000 /* CONF: hide SSID in beacon */ #define IEEE80211_F_NOBRIDGE 0x10000000 /* CONF: dis. internal bridge */ #define IEEE80211_F_PCF 0x20000000 /* CONF: PCF enabled */ #define IEEE80211_F_DOTH 0x40000000 /* CONF: 11h enabled */ #define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */ #define IEEE80211_F_BITS \ "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN" \ "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY" \ "\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \ "\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \ "\37DOTH\40DWDS" /* Atheros protocol-specific flags */ #define IEEE80211_F_ATHEROS \ (IEEE80211_F_FF | IEEE80211_F_COMP | IEEE80211_F_TURBOP) /* Check if an Atheros capability was negotiated for use */ #define IEEE80211_ATH_CAP(vap, ni, bit) \ ((vap)->iv_flags & (ni)->ni_ath_flags & (bit)) /* ic_flags_ext/iv_flags_ext */ #define IEEE80211_FEXT_INACT 0x00000002 /* CONF: sta inact handling */ #define IEEE80211_FEXT_SCANWAIT 0x00000004 /* STATUS: awaiting scan */ /* 0x00000006 reserved */ #define IEEE80211_FEXT_BGSCAN 0x00000008 /* STATUS: complete bgscan */ #define IEEE80211_FEXT_WPS 0x00000010 /* CONF: WPS enabled */ #define IEEE80211_FEXT_TSN 0x00000020 /* CONF: TSN enabled */ #define IEEE80211_FEXT_SCANREQ 0x00000040 /* STATUS: scan req params */ #define IEEE80211_FEXT_RESUME 0x00000080 /* STATUS: start on resume */ #define IEEE80211_FEXT_4ADDR 0x00000100 /* CONF: apply 4-addr encap */ #define IEEE80211_FEXT_NONERP_PR 0x00000200 /* STATUS: non-ERP sta present*/ #define IEEE80211_FEXT_SWBMISS 0x00000400 /* CONF: do bmiss in s/w */ #define IEEE80211_FEXT_DFS 0x00000800 /* CONF: DFS enabled */ #define IEEE80211_FEXT_DOTD 0x00001000 /* CONF: 11d enabled */ #define IEEE80211_FEXT_STATEWAIT 0x00002000 /* STATUS: awaiting state chg */ #define IEEE80211_FEXT_REINIT 0x00004000 /* STATUS: INIT state first */ #define IEEE80211_FEXT_BPF 0x00008000 /* STATUS: BPF tap present */ /* NB: immutable: should be set only when creating a vap */ #define IEEE80211_FEXT_WDSLEGACY 0x00010000 /* CONF: legacy WDS operation */ #define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/ #define IEEE80211_FEXT_UNIQMAC 0x00040000 /* CONF: user or computed mac */ #define IEEE80211_FEXT_SCAN_OFFLOAD 0x00080000 /* CONF: scan is fully offloaded */ #define IEEE80211_FEXT_SEQNO_OFFLOAD 0x00100000 /* CONF: driver does seqno insertion/allocation */ #define IEEE80211_FEXT_FRAG_OFFLOAD 0x00200000 /* CONF: hardware does 802.11 fragmentation + assignment */ #define IEEE80211_FEXT_VHT 0x00400000 /* CONF: VHT support */ #define IEEE80211_FEXT_QUIET_IE 0x00800000 /* STATUS: quiet IE in a beacon has been added */ #define IEEE80211_FEXT_UAPSD 0x01000000 /* CONF: enable U-APSD */ #define IEEE80211_FEXT_BITS \ "\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \ "\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \ "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC\24SCAN_OFFLOAD\25SEQNO_OFFLOAD" \ "\26FRAG_OFFLOAD\27VHT" \ "\30QUIET_IE\31UAPSD" /* ic_flags_ht/iv_flags_ht */ #define IEEE80211_FHT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */ #define IEEE80211_FHT_LDPC_TX 0x00010000 /* CONF: LDPC tx enabled */ #define IEEE80211_FHT_LDPC_RX 0x00020000 /* CONF: LDPC rx enabled */ #define IEEE80211_FHT_GF 0x00040000 /* CONF: Greenfield enabled */ #define IEEE80211_FHT_HT 0x00080000 /* CONF: HT supported */ #define IEEE80211_FHT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */ #define IEEE80211_FHT_AMPDU_RX 0x00200000 /* CONF: A-MPDU rx supported */ #define IEEE80211_FHT_AMSDU_TX 0x00400000 /* CONF: A-MSDU tx supported */ #define IEEE80211_FHT_AMSDU_RX 0x00800000 /* CONF: A-MSDU rx supported */ #define IEEE80211_FHT_USEHT40 0x01000000 /* CONF: 20/40 use enabled */ #define IEEE80211_FHT_PUREN 0x02000000 /* CONF: 11n w/o legacy sta's */ #define IEEE80211_FHT_SHORTGI20 0x04000000 /* CONF: short GI in HT20 */ #define IEEE80211_FHT_SHORTGI40 0x08000000 /* CONF: short GI in HT40 */ #define IEEE80211_FHT_HTCOMPAT 0x10000000 /* CONF: HT vendor OUI's */ #define IEEE80211_FHT_RIFS 0x20000000 /* CONF: RIFS enabled */ #define IEEE80211_FHT_STBC_TX 0x40000000 /* CONF: STBC tx enabled */ #define IEEE80211_FHT_STBC_RX 0x80000000 /* CONF: STBC rx enabled */ #define IEEE80211_FHT_BITS \ "\20\1NONHT_PR" \ "\23GF\24HT\25AMPDU_TX\26AMPDU_TX" \ "\27AMSDU_TX\30AMSDU_RX\31USEHT40\32PUREN\33SHORTGI20\34SHORTGI40" \ "\35HTCOMPAT\36RIFS\37STBC_TX\40STBC_RX" #define IEEE80211_FVEN_BITS "\20" #define IEEE80211_FVHT_VHT 0x000000001 /* CONF: VHT supported */ #define IEEE80211_FVHT_USEVHT40 0x000000002 /* CONF: Use VHT40 */ #define IEEE80211_FVHT_USEVHT80 0x000000004 /* CONF: Use VHT80 */ #define IEEE80211_FVHT_USEVHT160 0x000000008 /* CONF: Use VHT160 */ #define IEEE80211_FVHT_USEVHT80P80 0x000000010 /* CONF: Use VHT 80+80 */ #define IEEE80211_FVHT_MASK \ (IEEE80211_FVHT_VHT | IEEE80211_FVHT_USEVHT40 | \ IEEE80211_FVHT_USEVHT80 | IEEE80211_FVHT_USEVHT160 | \ IEEE80211_FVHT_USEVHT80P80) #define IEEE80211_VFHT_BITS \ "\20\1VHT\2VHT40\3VHT80\4VHT160\5VHT80P80" #define IEEE80211_COM_DETACHED 0x00000001 /* ieee80211_ifdetach called */ #define IEEE80211_COM_REF_ADD 0x00000002 /* add / remove reference */ #define IEEE80211_COM_REF 0xfffffffe /* reference counter bits */ #define IEEE80211_COM_REF_S 1 #define IEEE80211_COM_REF_MAX (IEEE80211_COM_REF >> IEEE80211_COM_REF_S) int ic_printf(struct ieee80211com *, const char *, ...) __printflike(2, 3); void ieee80211_ifattach(struct ieee80211com *); void ieee80211_ifdetach(struct ieee80211com *); int ieee80211_vap_setup(struct ieee80211com *, struct ieee80211vap *, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN]); int ieee80211_vap_attach(struct ieee80211vap *, ifm_change_cb_t, ifm_stat_cb_t, const uint8_t macaddr[IEEE80211_ADDR_LEN]); void ieee80211_vap_detach(struct ieee80211vap *); const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *); const struct ieee80211_htrateset *ieee80211_get_suphtrates( struct ieee80211com *, const struct ieee80211_channel *); void ieee80211_announce(struct ieee80211com *); void ieee80211_announce_channels(struct ieee80211com *); void ieee80211_drain(struct ieee80211com *); void ieee80211_chan_init(struct ieee80211com *); struct ieee80211com *ieee80211_find_vap(const uint8_t mac[IEEE80211_ADDR_LEN]); struct ieee80211com *ieee80211_find_com(const char *name); typedef void ieee80211_com_iter_func(void *, struct ieee80211com *); void ieee80211_iterate_coms(ieee80211_com_iter_func *, void *); int ieee80211_media_change(struct ifnet *); void ieee80211_media_status(struct ifnet *, struct ifmediareq *); int ieee80211_ioctl(struct ifnet *, u_long, caddr_t); int ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(int); int ieee80211_mhz2ieee(u_int, u_int); int ieee80211_chan2ieee(struct ieee80211com *, const struct ieee80211_channel *); u_int ieee80211_ieee2mhz(u_int, u_int); int ieee80211_add_channel_cbw(struct ieee80211_channel[], int, int *, uint8_t, uint16_t, int8_t, uint32_t, const uint8_t[], int); int ieee80211_add_channel(struct ieee80211_channel[], int, int *, uint8_t, uint16_t, int8_t, uint32_t, const uint8_t[]); int ieee80211_add_channel_ht40(struct ieee80211_channel[], int, int *, uint8_t, int8_t, uint32_t); uint32_t ieee80211_get_channel_center_freq(const struct ieee80211_channel *); uint32_t ieee80211_get_channel_center_freq1(const struct ieee80211_channel *); uint32_t ieee80211_get_channel_center_freq2(const struct ieee80211_channel *); #define NET80211_CBW_FLAG_HT40 0x01 #define NET80211_CBW_FLAG_VHT80 0x02 #define NET80211_CBW_FLAG_VHT160 0x04 #define NET80211_CBW_FLAG_VHT80P80 0x08 int ieee80211_add_channel_list_2ghz(struct ieee80211_channel[], int, int *, const uint8_t[], int, const uint8_t[], int); int ieee80211_add_channels_default_2ghz(struct ieee80211_channel[], int, int *, const uint8_t[], int); int ieee80211_add_channel_list_5ghz(struct ieee80211_channel[], int, int *, const uint8_t[], int, const uint8_t[], int); struct ieee80211_channel *ieee80211_find_channel(struct ieee80211com *, int freq, int flags); struct ieee80211_channel *ieee80211_find_channel_byieee(struct ieee80211com *, int ieee, int flags); struct ieee80211_channel *ieee80211_lookup_channel_rxstatus(struct ieee80211vap *, const struct ieee80211_rx_stats *); int ieee80211_setmode(struct ieee80211com *, enum ieee80211_phymode); enum ieee80211_phymode ieee80211_chan2mode(const struct ieee80211_channel *); uint32_t ieee80211_mac_hash(const struct ieee80211com *, const uint8_t addr[IEEE80211_ADDR_LEN]); char ieee80211_channel_type_char(const struct ieee80211_channel *c); #define ieee80211_get_current_channel(_ic) ((_ic)->ic_curchan) #define ieee80211_get_home_channel(_ic) ((_ic)->ic_bsschan) #define ieee80211_get_vap_desired_channel(_iv) ((_iv)->iv_des_chan) void ieee80211_radiotap_attach(struct ieee80211com *, struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, struct ieee80211_radiotap_header *rh, int rlen, uint32_t rx_radiotap); void ieee80211_radiotap_attachv(struct ieee80211com *, struct ieee80211_radiotap_header *th, int tlen, int n_tx_v, uint32_t tx_radiotap, struct ieee80211_radiotap_header *rh, int rlen, int n_rx_v, uint32_t rx_radiotap); void ieee80211_radiotap_detach(struct ieee80211com *); void ieee80211_radiotap_vattach(struct ieee80211vap *); void ieee80211_radiotap_vdetach(struct ieee80211vap *); void ieee80211_radiotap_chan_change(struct ieee80211com *); void ieee80211_radiotap_tx(struct ieee80211vap *, struct mbuf *); void ieee80211_radiotap_rx(struct ieee80211vap *, struct mbuf *); void ieee80211_radiotap_rx_all(struct ieee80211com *, struct mbuf *); static __inline int ieee80211_radiotap_active(const struct ieee80211com *ic) { return (ic->ic_flags_ext & IEEE80211_FEXT_BPF) != 0; } static __inline int ieee80211_radiotap_active_vap(const struct ieee80211vap *vap) { return (vap->iv_flags_ext & IEEE80211_FEXT_BPF) || vap->iv_ic->ic_montaps != 0; } /* * Enqueue a task on the state thread. */ static __inline void ieee80211_runtask(struct ieee80211com *ic, struct task *task) { taskqueue_enqueue(ic->ic_tq, task); } /* * Wait for a queued task to complete. */ static __inline void ieee80211_draintask(struct ieee80211com *ic, struct task *task) { taskqueue_drain(ic->ic_tq, task); } /* * Key update synchronization methods. XXX should not be visible. */ static __inline void ieee80211_key_update_begin(struct ieee80211vap *vap) { vap->iv_key_update_begin(vap); } static __inline void ieee80211_key_update_end(struct ieee80211vap *vap) { vap->iv_key_update_end(vap); } /* * XXX these need to be here for IEEE80211_F_DATAPAD */ /* * Return the space occupied by the 802.11 header and any * padding required by the driver. This works for a * management or data frame. */ static __inline int ieee80211_hdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_hdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) size = roundup(size, sizeof(uint32_t)); return size; } /* * Like ieee80211_hdrspace, but handles any type of frame. */ static __inline int ieee80211_anyhdrspace(struct ieee80211com *ic, const void *data) { int size = ieee80211_anyhdrsize(data); if (ic->ic_flags & IEEE80211_F_DATAPAD) size = roundup(size, sizeof(uint32_t)); return size; } /* * Notify a vap that beacon state has been updated. */ static __inline void ieee80211_beacon_notify(struct ieee80211vap *vap, int what) { if (vap->iv_state == IEEE80211_S_RUN) vap->iv_update_beacon(vap, what); } /* * Calculate HT channel promotion flags for a channel. * XXX belongs in ieee80211_ht.h but needs IEEE80211_FHT_* */ static __inline int ieee80211_htchanflags(const struct ieee80211_channel *c) { return IEEE80211_IS_CHAN_HT40(c) ? IEEE80211_FHT_HT | IEEE80211_FHT_USEHT40 : IEEE80211_IS_CHAN_HT(c) ? IEEE80211_FHT_HT : 0; } /* * Calculate VHT channel promotion flags for a channel. * XXX belongs in ieee80211_vht.h but needs IEEE80211_FVHT_* */ static __inline int ieee80211_vhtchanflags(const struct ieee80211_channel *c) { if (IEEE80211_IS_CHAN_VHT160(c)) return IEEE80211_FVHT_USEVHT160; if (IEEE80211_IS_CHAN_VHT80P80(c)) return IEEE80211_FVHT_USEVHT80P80; if (IEEE80211_IS_CHAN_VHT80(c)) return IEEE80211_FVHT_USEVHT80; if (IEEE80211_IS_CHAN_VHT40(c)) return IEEE80211_FVHT_USEVHT40; if (IEEE80211_IS_CHAN_VHT(c)) return IEEE80211_FVHT_VHT; return (0); } /* * Fetch the current TX power (cap) for the given node. * * This includes the node and ic/vap TX power limit as needed, * but it doesn't take into account any per-rate limit. */ static __inline uint16_t ieee80211_get_node_txpower(struct ieee80211_node *ni) { struct ieee80211com *ic = ni->ni_ic; uint16_t txpower; txpower = ni->ni_txpower; txpower = MIN(txpower, ic->ic_txpowlimit); if (ic->ic_curchan != NULL) { txpower = MIN(txpower, 2 * ic->ic_curchan->ic_maxregpower); txpower = MIN(txpower, ic->ic_curchan->ic_maxpower); } return (txpower); } /* * Debugging facilities compiled in when IEEE80211_DEBUG is defined. * * The intent is that any problem in the net80211 layer can be * diagnosed by inspecting the statistics (dumped by the wlanstats * program) and/or the msgs generated by net80211. Messages are * broken into functional classes and can be controlled with the * wlandebug program. Certain of these msg groups are for facilities * that are no longer part of net80211 (e.g. IEEE80211_MSG_DOT1XSM). */ #define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */ #define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */ #define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */ #define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */ #define IEEE80211_MSG_INPUT 0x08000000 /* input handling */ #define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */ #define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */ #define IEEE80211_MSG_NODE 0x01000000 /* node handling */ #define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */ #define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */ #define IEEE80211_MSG_SCAN 0x00200000 /* scanning */ #define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */ #define IEEE80211_MSG_STATE 0x00080000 /* state machine */ #define IEEE80211_MSG_POWER 0x00040000 /* power save handling */ #define IEEE80211_MSG_HWMP 0x00020000 /* hybrid mesh protocol */ #define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */ #define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */ #define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */ #define IEEE80211_MSG_MESH 0x00002000 /* mesh networking */ #define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */ #define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */ #define IEEE80211_MSG_WME 0x00000400 /* WME protocol */ #define IEEE80211_MSG_SUPERG 0x00000200 /* Atheros SuperG protocol */ #define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */ #define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */ #define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */ #define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */ #define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */ #define IEEE80211_MSG_WDS 0x00000008 /* WDS handling */ #define IEEE80211_MSG_IOCTL 0x00000004 /* ioctl handling */ #define IEEE80211_MSG_TDMA 0x00000002 /* TDMA handling */ #define IEEE80211_MSG_ANY 0xffffffff /* anything */ #define IEEE80211_MSG_BITS \ "\20\2TDMA\3IOCTL\4WDS\5ACTION\6RATECTL\7ROAM\10INACT\11DOTH\12SUPERG" \ "\13WME\14ACL\15WPA\16RADKEYS\17RADDUMP\20RADIUS\21DOT1XSM\22HWMP" \ "\23POWER\24STATE\25OUTPUT\26SCAN\27AUTH\30ASSOC\31NODE\32ELEMID" \ "\33XRATE\34INPUT\35CRYPTO\36DUPMPKTS\37DEBUG\04011N" /* Helper macros unified. */ #define _IEEE80211_MASKSHIFT(_v, _f) (((_v) & _f) >> _f##_S) #define _IEEE80211_SHIFTMASK(_v, _f) (((_v) << _f##_S) & _f) #ifdef IEEE80211_DEBUG #define ieee80211_msg(_vap, _m) ((_vap)->iv_debug & (_m)) #define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note(_vap, _fmt, __VA_ARGS__); \ } while (0) #define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note_mac(_vap, (_ni)->ni_macaddr, _fmt, __VA_ARGS__);\ } while (0) #define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note_mac(_vap, _mac, _fmt, __VA_ARGS__); \ } while (0) #define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) do { \ if (ieee80211_msg(_vap, _m)) \ ieee80211_note_frame(_vap, _wh, _fmt, __VA_ARGS__); \ } while (0) void ieee80211_note(const struct ieee80211vap *, const char *, ...); void ieee80211_note_mac(const struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN], const char *, ...); void ieee80211_note_frame(const struct ieee80211vap *, const struct ieee80211_frame *, const char *, ...); #define ieee80211_msg_debug(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_DEBUG) #define ieee80211_msg_dumppkts(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_DUMPPKTS) #define ieee80211_msg_input(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_INPUT) #define ieee80211_msg_radius(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_RADIUS) #define ieee80211_msg_dumpradius(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_RADDUMP) #define ieee80211_msg_dumpradkeys(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_RADKEYS) #define ieee80211_msg_scan(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_SCAN) #define ieee80211_msg_assoc(_vap) \ ((_vap)->iv_debug & IEEE80211_MSG_ASSOC) /* * Emit a debug message about discarding a frame or information * element. One format is for extracting the mac address from * the frame header; the other is for when a header is not * available or otherwise appropriate. */ #define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_frame(_vap, _wh, _type, \ "%s:%d: " _fmt, __func__, __LINE__, __VA_ARGS__); \ } while (0) #define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_ie(_vap, _wh, _type, \ "%s:%d: " _fmt, __func__, __LINE__, __VA_ARGS__); \ } while (0) #define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) do { \ if ((_vap)->iv_debug & (_m)) \ ieee80211_discard_mac(_vap, _mac, _type, \ "%s:%d: " _fmt, __func__, __LINE__, __VA_ARGS__); \ } while (0) void ieee80211_discard_frame(const struct ieee80211vap *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); void ieee80211_discard_ie(const struct ieee80211vap *, const struct ieee80211_frame *, const char *type, const char *fmt, ...); void ieee80211_discard_mac(const struct ieee80211vap *, const uint8_t mac[IEEE80211_ADDR_LEN], const char *type, const char *fmt, ...); #else #define IEEE80211_DPRINTF(_vap, _m, _fmt, ...) #define IEEE80211_NOTE(_vap, _m, _ni, _fmt, ...) #define IEEE80211_NOTE_FRAME(_vap, _m, _wh, _fmt, ...) #define IEEE80211_NOTE_MAC(_vap, _m, _mac, _fmt, ...) #define ieee80211_msg_dumppkts(_vap) 0 #define ieee80211_msg(_vap, _m) 0 #define IEEE80211_DISCARD(_vap, _m, _wh, _type, _fmt, ...) #define IEEE80211_DISCARD_IE(_vap, _m, _wh, _type, _fmt, ...) #define IEEE80211_DISCARD_MAC(_vap, _m, _mac, _type, _fmt, ...) #endif #endif /* _NET80211_IEEE80211_VAR_H_ */ diff --git a/sys/net80211/ieee80211_vht.c b/sys/net80211/ieee80211_vht.c index 32123e7a2815..7dd2a6be1d2e 100644 --- a/sys/net80211/ieee80211_vht.c +++ b/sys/net80211/ieee80211_vht.c @@ -1,892 +1,892 @@ /*- * Copyright (c) 2017 Adrian Chadd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #ifdef __FreeBSD__ #endif /* * IEEE 802.11ac-2013 protocol support. */ #include "opt_inet.h" #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ADDSHORT(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = (v) >> 8; \ frm += 2; \ } while (0) #define ADDWORD(frm, v) do { \ frm[0] = (v) & 0xff; \ frm[1] = ((v) >> 8) & 0xff; \ frm[2] = ((v) >> 16) & 0xff; \ frm[3] = ((v) >> 24) & 0xff; \ frm += 4; \ } while (0) /* * Immediate TODO: * * + handle WLAN_ACTION_VHT_OPMODE_NOTIF and other VHT action frames * + ensure vhtinfo/vhtcap parameters correctly use the negotiated * capabilities and ratesets * + group ID management operation */ /* * XXX TODO: handle WLAN_ACTION_VHT_OPMODE_NOTIF * * Look at mac80211/vht.c:ieee80211_vht_handle_opmode() for further details. */ static int vht_recv_action_placeholder(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const uint8_t *frm, const uint8_t *efrm) { #ifdef IEEE80211_DEBUG ieee80211_note(ni->ni_vap, "%s: called; fc=0x%.2x/0x%.2x", __func__, wh->i_fc[0], wh->i_fc[1]); #endif return (0); } static int vht_send_action_placeholder(struct ieee80211_node *ni, int category, int action, void *arg0) { #ifdef IEEE80211_DEBUG ieee80211_note(ni->ni_vap, "%s: called; category=%d, action=%d", __func__, category, action); #endif return (EINVAL); } static void ieee80211_vht_init(void) { ieee80211_recv_action_register(IEEE80211_ACTION_CAT_VHT, WLAN_ACTION_VHT_COMPRESSED_BF, vht_recv_action_placeholder); ieee80211_recv_action_register(IEEE80211_ACTION_CAT_VHT, WLAN_ACTION_VHT_GROUPID_MGMT, vht_recv_action_placeholder); ieee80211_recv_action_register(IEEE80211_ACTION_CAT_VHT, WLAN_ACTION_VHT_OPMODE_NOTIF, vht_recv_action_placeholder); ieee80211_send_action_register(IEEE80211_ACTION_CAT_VHT, WLAN_ACTION_VHT_COMPRESSED_BF, vht_send_action_placeholder); ieee80211_send_action_register(IEEE80211_ACTION_CAT_VHT, WLAN_ACTION_VHT_GROUPID_MGMT, vht_send_action_placeholder); ieee80211_send_action_register(IEEE80211_ACTION_CAT_VHT, WLAN_ACTION_VHT_OPMODE_NOTIF, vht_send_action_placeholder); } SYSINIT(wlan_vht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_vht_init, NULL); void ieee80211_vht_attach(struct ieee80211com *ic) { } void ieee80211_vht_detach(struct ieee80211com *ic) { } void ieee80211_vht_vattach(struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; if (! IEEE80211_CONF_VHT(ic)) return; - vap->iv_vhtcaps = ic->ic_vhtcaps; + vap->iv_vht_cap.vht_cap_info = ic->ic_vhtcaps; vap->iv_vhtextcaps = ic->ic_vhtextcaps; /* XXX assume VHT80 support; should really check vhtcaps */ vap->iv_vht_flags = IEEE80211_FVHT_VHT | IEEE80211_FVHT_USEVHT40 | IEEE80211_FVHT_USEVHT80; - if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(vap->iv_vhtcaps)) + if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(vap->iv_vht_cap.vht_cap_info)) vap->iv_vht_flags |= IEEE80211_FVHT_USEVHT160; - if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(vap->iv_vhtcaps)) + if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(vap->iv_vht_cap.vht_cap_info)) vap->iv_vht_flags |= IEEE80211_FVHT_USEVHT80P80; - memcpy(&vap->iv_vht_mcsinfo, &ic->ic_vht_mcsinfo, + memcpy(&vap->iv_vht_cap.supp_mcs, &ic->ic_vht_mcsinfo, sizeof(struct ieee80211_vht_mcs_info)); } void ieee80211_vht_vdetach(struct ieee80211vap *vap) { } #if 0 static void vht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode) { } #endif static int vht_mcs_to_num(int m) { switch (m) { case IEEE80211_VHT_MCS_SUPPORT_0_7: return (7); case IEEE80211_VHT_MCS_SUPPORT_0_8: return (8); case IEEE80211_VHT_MCS_SUPPORT_0_9: return (9); default: return (0); } } void ieee80211_vht_announce(struct ieee80211com *ic) { int i, tx, rx; if (! IEEE80211_CONF_VHT(ic)) return; /* Channel width */ ic_printf(ic, "[VHT] Channel Widths: 20MHz, 40MHz, 80MHz%s%s\n", (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(ic->ic_vhtcaps)) ? ", 160MHz" : "", (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(ic->ic_vhtcaps)) ? ", 80+80MHz" : ""); /* Features */ ic_printf(ic, "[VHT] Features: %b\n", ic->ic_vhtcaps, IEEE80211_VHTCAP_BITS); /* For now, just 5GHz VHT. Worry about 2GHz VHT later */ for (i = 0; i < 8; i++) { /* Each stream is 2 bits */ tx = (ic->ic_vht_mcsinfo.tx_mcs_map >> (2*i)) & 0x3; rx = (ic->ic_vht_mcsinfo.rx_mcs_map >> (2*i)) & 0x3; if (tx == 3 && rx == 3) continue; ic_printf(ic, "[VHT] NSS %d: TX MCS 0..%d, RX MCS 0..%d\n", i + 1, vht_mcs_to_num(tx), vht_mcs_to_num(rx)); } } void ieee80211_vht_node_init(struct ieee80211_node *ni) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: called", __func__); ni->ni_flags |= IEEE80211_NODE_VHT; } void ieee80211_vht_node_cleanup(struct ieee80211_node *ni) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: called", __func__); ni->ni_flags &= ~IEEE80211_NODE_VHT; ni->ni_vhtcap = 0; bzero(&ni->ni_vht_mcsinfo, sizeof(struct ieee80211_vht_mcs_info)); } /* * Parse an 802.11ac VHT operation IE. */ void ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie) { /* vht operation */ ni->ni_vht_chanwidth = ie[2]; ni->ni_vht_chan1 = ie[3]; ni->ni_vht_chan2 = ie[4]; ni->ni_vht_basicmcs = le16dec(ie + 5); #if 0 printf("%s: chan1=%d, chan2=%d, chanwidth=%d, basicmcs=0x%04x\n", __func__, ni->ni_vht_chan1, ni->ni_vht_chan2, ni->ni_vht_chanwidth, ni->ni_vht_basicmcs); #endif } /* * Parse an 802.11ac VHT capability IE. */ void ieee80211_parse_vhtcap(struct ieee80211_node *ni, const uint8_t *ie) { /* vht capability */ ni->ni_vhtcap = le32dec(ie + 2); /* suppmcs */ ni->ni_vht_mcsinfo.rx_mcs_map = le16dec(ie + 6); ni->ni_vht_mcsinfo.rx_highest = le16dec(ie + 8); ni->ni_vht_mcsinfo.tx_mcs_map = le16dec(ie + 10); ni->ni_vht_mcsinfo.tx_highest = le16dec(ie + 12); } int ieee80211_vht_updateparams(struct ieee80211_node *ni, const uint8_t *vhtcap_ie, const uint8_t *vhtop_ie) { //printf("%s: called\n", __func__); ieee80211_parse_vhtcap(ni, vhtcap_ie); ieee80211_parse_vhtopmode(ni, vhtop_ie); return (0); } void ieee80211_setup_vht_rates(struct ieee80211_node *ni, const uint8_t *vhtcap_ie, const uint8_t *vhtop_ie) { //printf("%s: called\n", __func__); /* XXX TODO */ } void ieee80211_vht_timeout(struct ieee80211vap *vap) { } void ieee80211_vht_node_join(struct ieee80211_node *ni) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: called", __func__); } void ieee80211_vht_node_leave(struct ieee80211_node *ni) { IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni, "%s: called", __func__); } /* * Calculate the VHTCAP IE for a given node. * * This includes calculating the capability intersection based on the * current operating mode and intersection of the TX/RX MCS maps. * * The standard only makes it clear about MCS rate negotiation * and MCS basic rates (which must be a subset of the general * negotiated rates). It doesn't make it clear that the AP should * figure out the minimum functional overlap with the STA and * support that. * * Note: this is in host order, not in 802.11 endian order. * * TODO: ensure I re-read 9.7.11 Rate Selection for VHT STAs. * * TODO: investigate what we should negotiate for MU-MIMO beamforming * options. * * opmode is '1' for "vhtcap as if I'm a STA", 0 otherwise. */ void ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni, struct ieee80211_ie_vhtcap *vhtcap, int opmode) { struct ieee80211vap *vap = ni->ni_vap; // struct ieee80211com *ic = vap->iv_ic; uint32_t val, val1, val2; uint32_t new_vhtcap; int i; vhtcap->ie = IEEE80211_ELEMID_VHT_CAP; vhtcap->len = sizeof(struct ieee80211_ie_vhtcap) - 2; /* * Capabilities - it depends on whether we are a station * or not. */ new_vhtcap = 0; /* * Station - use our desired configuration based on * local config, local device bits and the already-learnt * vhtcap/vhtinfo IE in the node. */ /* Limit MPDU size to the smaller of the two */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_MAX_MPDU_MASK); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_MAX_MPDU_MASK); } val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_MAX_MPDU_MASK); /* Limit supp channel config */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK); } if ((val2 == 2) && ((vap->iv_vht_flags & IEEE80211_FVHT_USEVHT80P80) == 0)) val2 = 1; if ((val2 == 1) && ((vap->iv_vht_flags & IEEE80211_FVHT_USEVHT160) == 0)) val2 = 0; val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK); /* RX LDPC */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_RXLDPC); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_RXLDPC); } val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_RXLDPC); /* Short-GI 80 */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SHORT_GI_80); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_SHORT_GI_80); } val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SHORT_GI_80); /* Short-GI 160 */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SHORT_GI_160); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_SHORT_GI_160); } val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SHORT_GI_160); /* * STBC is slightly more complicated. * * In non-STA mode, we just announce our capabilities and that * is that. * * In STA mode, we should calculate our capabilities based on * local capabilities /and/ what the remote says. So: * * + Only TX STBC if we support it and the remote supports RX STBC; * + Only announce RX STBC if we support it and the remote supports * TX STBC; * + RX STBC should be the minimum of local and remote RX STBC; */ /* TX STBC */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_TXSTBC); if (opmode == 1) { /* STA mode - enable it only if node RXSTBC is non-zero */ val2 = !! _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_RXSTBC_MASK); } val = MIN(val1, val2); /* XXX For now, use the 11n config flag */ if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_TX) == 0) val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_TXSTBC); /* RX STBC1..4 */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_RXSTBC_MASK); if (opmode == 1) { /* STA mode - enable it only if node TXSTBC is non-zero */ val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_TXSTBC); } val = MIN(val1, val2); /* XXX For now, use the 11n config flag */ if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0) val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_RXSTBC_MASK); /* * Finally - if RXSTBC is 0, then don't enable TXSTBC. * Strictly speaking a device can TXSTBC and not RXSTBC, but * it would be silly. */ if (val == 0) new_vhtcap &= ~IEEE80211_VHTCAP_TXSTBC; /* * Some of these fields require other fields to exist. * So before using it, the parent field needs to be checked * otherwise the overridden value may be wrong. * * For example, if SU beamformee is set to 0, then BF STS * needs to be 0. */ /* SU Beamformer capable */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE); } val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE); /* SU Beamformee capable */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE); } val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE); /* Beamformee STS capability - only if SU beamformee capable */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK); if (opmode == 1) { val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK); } val = MIN(val1, val2); if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE) == 0) val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK); /* Sounding dimensions - only if SU beamformer capable */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK); val = MIN(val1, val2); if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE) == 0) val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK); /* * MU Beamformer capable - only if SU BFF capable, MU BFF capable * and STA (not AP) */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE); val = MIN(val1, val2); if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE) == 0) val = 0; if (opmode != 1) /* Only enable for STA mode */ val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE); /* * MU Beamformee capable - only if SU BFE capable, MU BFE capable * and AP (not STA) */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE); val = MIN(val1, val2); if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE) == 0) val = 0; if (opmode != 0) /* Only enable for AP mode */ val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE); /* VHT TXOP PS */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_VHT_TXOP_PS); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_VHT_TXOP_PS); val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_VHT_TXOP_PS); /* HTC_VHT */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_HTC_VHT); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_HTC_VHT); val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_HTC_VHT); /* A-MPDU length max */ /* XXX TODO: we need a userland config knob for this */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); val = MIN(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); /* * Link adaptation is only valid if HTC-VHT capable is 1. * Otherwise, always set it to 0. */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK); val = MIN(val1, val2); if ((new_vhtcap & IEEE80211_VHTCAP_HTC_VHT) == 0) val = 0; new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK); /* * The following two options are 0 if the pattern may change, 1 if it * does not change. So, downgrade to the higher value. */ /* RX antenna pattern */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN); val = MAX(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN); /* TX antenna pattern */ - val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vhtcaps, + val2 = val1 = _IEEE80211_MASKSHIFT(vap->iv_vht_cap.vht_cap_info, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN); if (opmode == 1) val2 = _IEEE80211_MASKSHIFT(ni->ni_vhtcap, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN); val = MAX(val1, val2); new_vhtcap |= _IEEE80211_SHIFTMASK(val, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN); /* * MCS set - again, we announce what we want to use * based on configuration, device capabilities and * already-learnt vhtcap/vhtinfo IE information. */ /* MCS set - start with whatever the device supports */ - vhtcap->supp_mcs.rx_mcs_map = vap->iv_vht_mcsinfo.rx_mcs_map; + vhtcap->supp_mcs.rx_mcs_map = vap->iv_vht_cap.supp_mcs.rx_mcs_map; vhtcap->supp_mcs.rx_highest = 0; - vhtcap->supp_mcs.tx_mcs_map = vap->iv_vht_mcsinfo.tx_mcs_map; + vhtcap->supp_mcs.tx_mcs_map = vap->iv_vht_cap.supp_mcs.tx_mcs_map; vhtcap->supp_mcs.tx_highest = 0; vhtcap->vht_cap_info = new_vhtcap; /* * Now, if we're a STA, mask off whatever the AP doesn't support. * Ie, we continue to state we can receive whatever we can do, * but we only announce that we will transmit rates that meet * the AP requirement. * * Note: 0 - MCS0..7; 1 - MCS0..8; 2 - MCS0..9; 3 = not supported. * We can't just use MIN() because '3' means "no", so special case it. */ if (opmode) { for (i = 0; i < 8; i++) { val1 = (vhtcap->supp_mcs.tx_mcs_map >> (i*2)) & 0x3; val2 = (ni->ni_vht_mcsinfo.tx_mcs_map >> (i*2)) & 0x3; val = MIN(val1, val2); if (val1 == 3 || val2 == 3) val = 3; vhtcap->supp_mcs.tx_mcs_map &= ~(0x3 << (i*2)); vhtcap->supp_mcs.tx_mcs_map |= (val << (i*2)); } } } /* * Add a VHTCAP field. * * If in station mode, we announce what we would like our * desired configuration to be. * * Else, we announce our capabilities based on our current * configuration. */ uint8_t * ieee80211_add_vhtcap(uint8_t *frm, struct ieee80211_node *ni) { struct ieee80211_ie_vhtcap vhtcap; int opmode; opmode = 0; if (ni->ni_vap->iv_opmode == IEEE80211_M_STA) opmode = 1; ieee80211_vht_get_vhtcap_ie(ni, &vhtcap, opmode); memset(frm, '\0', sizeof(struct ieee80211_ie_vhtcap)); frm[0] = IEEE80211_ELEMID_VHT_CAP; frm[1] = sizeof(struct ieee80211_ie_vhtcap) - 2; frm += 2; /* 32-bit VHT capability */ ADDWORD(frm, vhtcap.vht_cap_info); /* suppmcs */ ADDSHORT(frm, vhtcap.supp_mcs.rx_mcs_map); ADDSHORT(frm, vhtcap.supp_mcs.rx_highest); ADDSHORT(frm, vhtcap.supp_mcs.tx_mcs_map); ADDSHORT(frm, vhtcap.supp_mcs.tx_highest); return (frm); } /* * Non-associated probe requests. Add VHT capabilities based on * the current channel configuration. No BSS yet. */ uint8_t * ieee80211_add_vhtcap_ch(uint8_t *frm, struct ieee80211vap *vap, struct ieee80211_channel *c) { struct ieee80211_vht_cap *vhtcap; memset(frm, 0, 2 + sizeof(*vhtcap)); frm[0] = IEEE80211_ELEMID_VHT_CAP; frm[1] = sizeof(*vhtcap); frm += 2; /* 32-bit VHT capability */ ADDWORD(frm, vap->iv_vht_cap.vht_cap_info); /* supp_mcs */ ADDSHORT(frm, vap->iv_vht_cap.supp_mcs.rx_mcs_map); ADDSHORT(frm, vap->iv_vht_cap.supp_mcs.rx_highest); ADDSHORT(frm, vap->iv_vht_cap.supp_mcs.tx_mcs_map); ADDSHORT(frm, vap->iv_vht_cap.supp_mcs.tx_highest); return (frm); } static uint8_t ieee80211_vht_get_chwidth_ie(struct ieee80211_channel *c) { /* * XXX TODO: look at the node configuration as * well? */ if (IEEE80211_IS_CHAN_VHT80P80(c)) return IEEE80211_VHT_CHANWIDTH_80P80MHZ; if (IEEE80211_IS_CHAN_VHT160(c)) return IEEE80211_VHT_CHANWIDTH_160MHZ; if (IEEE80211_IS_CHAN_VHT80(c)) return IEEE80211_VHT_CHANWIDTH_80MHZ; if (IEEE80211_IS_CHAN_VHT40(c)) return IEEE80211_VHT_CHANWIDTH_USE_HT; if (IEEE80211_IS_CHAN_VHT20(c)) return IEEE80211_VHT_CHANWIDTH_USE_HT; /* We shouldn't get here */ printf("%s: called on a non-VHT channel (freq=%d, flags=0x%08x\n", __func__, (int) c->ic_freq, c->ic_flags); return IEEE80211_VHT_CHANWIDTH_USE_HT; } /* * Note: this just uses the current channel information; * it doesn't use the node info after parsing. * * XXX TODO: need to make the basic MCS set configurable. * XXX TODO: read 802.11-2013 to determine what to set * chwidth to when scanning. I have a feeling * it isn't involved in scanning and we shouldn't * be sending it; and I don't yet know what to set * it to for IBSS or hostap where the peer may be * a completely different channel width to us. */ uint8_t * ieee80211_add_vhtinfo(uint8_t *frm, struct ieee80211_node *ni) { memset(frm, '\0', sizeof(struct ieee80211_ie_vht_operation)); frm[0] = IEEE80211_ELEMID_VHT_OPMODE; frm[1] = sizeof(struct ieee80211_ie_vht_operation) - 2; frm += 2; /* 8-bit chanwidth */ *frm++ = ieee80211_vht_get_chwidth_ie(ni->ni_chan); /* 8-bit freq1 */ *frm++ = ni->ni_chan->ic_vht_ch_freq1; /* 8-bit freq2 */ *frm++ = ni->ni_chan->ic_vht_ch_freq2; /* 16-bit basic MCS set - just MCS0..7 for NSS=1 for now */ ADDSHORT(frm, 0xfffc); return (frm); } void ieee80211_vht_update_cap(struct ieee80211_node *ni, const uint8_t *vhtcap_ie, const uint8_t *vhtop_ie) { ieee80211_parse_vhtcap(ni, vhtcap_ie); ieee80211_parse_vhtopmode(ni, vhtop_ie); } static struct ieee80211_channel * findvhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int vhtflags) { return (ieee80211_find_channel(ic, c->ic_freq, (c->ic_flags & ~IEEE80211_CHAN_VHT) | vhtflags)); } /* * Handle channel promotion to VHT, similar to ieee80211_ht_adjust_channel(). */ struct ieee80211_channel * ieee80211_vht_adjust_channel(struct ieee80211com *ic, struct ieee80211_channel *chan, int flags) { struct ieee80211_channel *c; /* First case - handle channel demotion - if VHT isn't set */ if ((flags & IEEE80211_FVHT_MASK) == 0) { #if 0 printf("%s: demoting channel %d/0x%08x\n", __func__, chan->ic_ieee, chan->ic_flags); #endif c = ieee80211_find_channel(ic, chan->ic_freq, chan->ic_flags & ~IEEE80211_CHAN_VHT); if (c == NULL) c = chan; #if 0 printf("%s: .. to %d/0x%08x\n", __func__, c->ic_ieee, c->ic_flags); #endif return (c); } /* * We can upgrade to VHT - attempt to do so * * Note: we don't clear the HT flags, these are the hints * for HT40U/HT40D when selecting VHT40 or larger channels. */ c = NULL; if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT160)) c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT160); if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT80P80)) c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT80P80); if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT80)) c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT80); if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT40)) c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT40U); if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT40)) c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT40D); /* * If we get here, VHT20 is always possible because we checked * for IEEE80211_FVHT_VHT above. */ if (c == NULL) c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT20); if (c != NULL) chan = c; #if 0 printf("%s: selected %d/0x%08x\n", __func__, c->ic_ieee, c->ic_flags); #endif return (chan); } /* * Calculate the VHT operation IE for a given node. * * This includes calculating the suitable channel width/parameters * and basic MCS set. * * TODO: ensure I read 9.7.11 Rate Selection for VHT STAs. * TODO: ensure I read 10.39.7 - BSS Basic VHT-MCS and NSS set operation. */ void ieee80211_vht_get_vhtinfo_ie(struct ieee80211_node *ni, struct ieee80211_ie_vht_operation *vhtop, int opmode) { printf("%s: called; TODO!\n", __func__); }