diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c index ec39a54d672f..6d26f85b56bc 100644 --- a/sys/net80211/ieee80211_adhoc.c +++ b/sys/net80211/ieee80211_adhoc.c @@ -822,10 +822,14 @@ adhoc_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, #if 0 if (scan.htcap != NULL && scan.htinfo != NULL && (vap->iv_flags_ht & IEEE80211_FHT_HT)) { - if (ieee80211_ht_updateparams(ni, + ieee80211_ht_updateparams(ni, + scan.htcap, scan.htinfo)); + if (ieee80211_ht_updateparams_final(ni, scan.htcap, scan.htinfo)) ht_state_change = 1; } + + /* XXX same for VHT? */ #endif if (ni != NULL) { IEEE80211_RSSI_LPF(ni->ni_avgrssi, rssi); diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 9868f133bca7..66df1bd463a1 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$"); #include #endif #include +#include #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) @@ -1745,6 +1746,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, struct ieee80211_frame *wh; uint8_t *frm, *efrm, *sfrm; uint8_t *ssid, *rates, *xrates, *wpa, *rsn, *wme, *ath, *htcap; + uint8_t *vhtcap, *vhtinfo; int reassoc, resp; uint8_t rate; @@ -2042,6 +2044,7 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, if (reassoc) frm += 6; /* ignore current AP info */ ssid = rates = xrates = wpa = rsn = wme = ath = htcap = NULL; + vhtcap = vhtinfo = NULL; sfrm = frm; while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); @@ -2061,6 +2064,12 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, case IEEE80211_ELEMID_HTCAP: htcap = frm; break; + case IEEE80211_ELEMID_VHT_CAP: + vhtcap = frm; + break; + case IEEE80211_ELEMID_VHT_OPMODE: + vhtinfo = frm; + break; case IEEE80211_ELEMID_VENDOR: if (iswpaoui(frm)) wpa = frm; @@ -2135,10 +2144,22 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, vap->iv_stats.is_rx_assoc_norate++; return; } + /* * Do HT rate set handling and setup HT node state. */ ni->ni_chan = vap->iv_bss->ni_chan; + + /* VHT */ + if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) { + /* XXX TODO; see below */ + printf("%s: VHT TODO!\n", __func__); + ieee80211_vht_node_init(ni); + ieee80211_vht_update_cap(ni, vhtcap, vhtinfo); + } else if (ni->ni_flags & IEEE80211_NODE_VHT) + ieee80211_vht_node_cleanup(ni); + + /* HT */ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { rate = ieee80211_setup_htrates(ni, htcap, IEEE80211_F_DOFMCS | IEEE80211_F_DONEGO | @@ -2153,6 +2174,12 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, ieee80211_ht_updatehtcap(ni, htcap); } else if (ni->ni_flags & IEEE80211_NODE_HT) ieee80211_ht_node_cleanup(ni); + + /* Finally - this will use HT/VHT info to change node channel */ + if (IEEE80211_IS_CHAN_HT(ni->ni_chan) && htcap != NULL) { + ieee80211_ht_updatehtcap_final(ni); + } + #ifdef IEEE80211_SUPPORT_SUPERG /* Always do ff node cleanup; for A-MSDU */ ieee80211_ff_node_cleanup(ni); diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index 74e265ec9dce..2f28f002ec32 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -1490,52 +1490,117 @@ ieee80211_parse_htinfo(struct ieee80211_node *ni, const uint8_t *ie) } /* - * Handle 11n channel switch. Use the received HT ie's to - * identify the right channel to use. If we cannot locate it - * in the channel table then fallback to legacy operation. + * 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) +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; - chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags; - if (chanflags != ni->ni_chan->ic_flags) { - /* 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); + /* + * 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); + 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 */ - } - if (c != NULL && c != ni->ni_chan) { - IEEE80211_NOTE(ni->ni_vap, - IEEE80211_MSG_ASSOC | IEEE80211_MSG_11N, ni, - "switch station to HT%d channel %u/0x%x", - 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 */ + /* XXX stat */ } - /* update node's tx channel width */ + + /* 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); } @@ -1587,15 +1652,18 @@ htcap_update_shortgi(struct ieee80211_node *ni) /* * 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. */ -int +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; - int htflags; - int ret = 0; ieee80211_parse_htcap(ni, htcapie); if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) @@ -1607,8 +1675,115 @@ ieee80211_ht_updateparams(struct ieee80211_node *ni, 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_flags_vht & IEEE80211_FVHT_VHT) { + if ((ni->ni_vht_chanwidth == IEEE80211_VHT_CHANWIDTH_160MHZ) && + /* XXX 2 means "160MHz and 80+80MHz", 1 means "160MHz" */ + (MS(vap->iv_vhtcaps, + IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) >= 1) && + (vap->iv_flags_vht & 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" */ + (MS(vap->iv_vhtcaps, + IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) == 2) && + (vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80P80)) { + vhtflags = IEEE80211_CHAN_VHT80_80; + /* 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_flags_vht & 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_flags_vht & IEEE80211_FVHT_USEVHT40)) { + vhtflags = IEEE80211_CHAN_VHT40U + | IEEE80211_CHAN_HT40U; + } else if (htflags == IEEE80211_CHAN_HT40D && + (vap->iv_flags_vht & 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)) { @@ -1617,14 +1792,16 @@ ieee80211_ht_updateparams(struct ieee80211_node *ni, else if (ni->ni_ht2ndchan == IEEE80211_HTINFO_2NDCHAN_BELOW) htflags = IEEE80211_CHAN_HT40D; } - if (htinfo_update_chw(ni, htflags)) - ret = 1; - 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; + /* + * 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); } @@ -1632,17 +1809,31 @@ ieee80211_ht_updateparams(struct ieee80211_node *ni, /* * 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; - int htflags; ieee80211_parse_htcap(ni, htcapie); if (vap->iv_htcaps & IEEE80211_HTCAP_SMPS) htcap_update_mimo_ps(ni); htcap_update_shortgi(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 */ @@ -1655,7 +1846,14 @@ ieee80211_ht_updatehtcap(struct ieee80211_node *ni, const uint8_t *htcapie) else if (IEEE80211_IS_CHAN_HT40D(vap->iv_bss->ni_chan)) htflags = IEEE80211_CHAN_HT40D; } - (void) htinfo_update_chw(ni, htflags); + /* + * 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); } /* @@ -2135,6 +2333,7 @@ ht_recv_action_ht_txchwidth(struct ieee80211_node *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 */ } @@ -2229,6 +2428,10 @@ ieee80211_ampdu_request(struct ieee80211_node *ni, 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; diff --git a/sys/net80211/ieee80211_ht.h b/sys/net80211/ieee80211_ht.h index dfc7d1c312a0..b34d4edf3895 100644 --- a/sys/net80211/ieee80211_ht.h +++ b/sys/net80211/ieee80211_ht.h @@ -201,9 +201,12 @@ void ieee80211_htprot_update(struct ieee80211com *, int protmode); void ieee80211_ht_timeout(struct ieee80211com *); void ieee80211_parse_htcap(struct ieee80211_node *, const uint8_t *); void ieee80211_parse_htinfo(struct ieee80211_node *, const uint8_t *); -int ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *, +void ieee80211_ht_updateparams(struct ieee80211_node *, const uint8_t *, const uint8_t *); +int ieee80211_ht_updateparams_final(struct ieee80211_node *, + const uint8_t *, const uint8_t *); void ieee80211_ht_updatehtcap(struct ieee80211_node *, const uint8_t *); +void ieee80211_ht_updatehtcap_final(struct ieee80211_node *); int ieee80211_ampdu_request(struct ieee80211_node *, struct ieee80211_tx_ampdu *); void ieee80211_ampdu_stop(struct ieee80211_node *, diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 3f50268bedce..8af041eb8533 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -494,6 +494,8 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, scan->status = 0; /* * beacon/probe response frame format + * + * XXX Update from 802.11-2012 - eg where HT is * [8] time stamp * [2] beacon interval * [2] capability information @@ -508,6 +510,8 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, * [tlv] WPA or RSN * [tlv] HT capabilities * [tlv] HT information + * [tlv] VHT capabilities + * [tlv] VHT information * [tlv] Atheros capabilities * [tlv] Mesh ID * [tlv] Mesh Configuration @@ -585,6 +589,12 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, case IEEE80211_ELEMID_HTCAP: scan->htcap = frm; break; + case IEEE80211_ELEMID_VHT_CAP: + scan->vhtcap = frm; + break; + case IEEE80211_ELEMID_VHT_OPMODE: + scan->vhtopmode = frm; + break; case IEEE80211_ELEMID_RSN: scan->rsn = frm; break; @@ -718,6 +728,19 @@ ieee80211_parse_beacon(struct ieee80211_node *ni, struct mbuf *m, sizeof(struct ieee80211_ie_htinfo)-2, scan->htinfo = NULL); } + + /* Process VHT IEs */ + if (scan->vhtcap != NULL) { + IEEE80211_VERIFY_LENGTH(scan->vhtcap[1], + sizeof(struct ieee80211_ie_vhtcap) - 2, + scan->vhtcap = NULL); + } + if (scan->vhtopmode != NULL) { + IEEE80211_VERIFY_LENGTH(scan->vhtopmode[1], + sizeof(struct ieee80211_ie_vht_operation) - 2, + scan->vhtopmode = NULL); + } + return scan->status; } @@ -838,6 +861,9 @@ ieee80211_parse_action(struct ieee80211_node *ni, struct mbuf *m) } break; #endif + case IEEE80211_ACTION_CAT_VHT: + printf("%s: TODO: VHT handling!\n", __func__); + break; } return 0; } diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index c08051f68272..45899c0b3210 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include @@ -412,7 +413,11 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan) /* XXX TODO: other bits and pieces - eg fast-frames? */ /* If we're an 11n channel then initialise the 11n bits */ - if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { + if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) { + /* XXX what else? */ + ieee80211_ht_node_init(ni); + ieee80211_vht_node_init(ni); + } else if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) { /* XXX what else? */ ieee80211_ht_node_init(ni); } @@ -707,10 +712,43 @@ gethtadjustflags(struct ieee80211com *ic) return flags; } +/* + * Calculate VHT channel promotion flags for all vaps. + * This assumes ni_chan have been setup for each vap. + */ +static int +getvhtadjustflags(struct ieee80211com *ic) +{ + struct ieee80211vap *vap; + int flags; + + flags = 0; + /* XXX locking */ + TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { + if (vap->iv_state < IEEE80211_S_RUN) + continue; + switch (vap->iv_opmode) { + case IEEE80211_M_WDS: + case IEEE80211_M_STA: + case IEEE80211_M_AHDEMO: + case IEEE80211_M_HOSTAP: + case IEEE80211_M_IBSS: + case IEEE80211_M_MBSS: + flags |= ieee80211_vhtchanflags(vap->iv_bss->ni_chan); + break; + default: + break; + } + } + return flags; +} + /* * Check if the current channel needs to change based on whether * any vap's are using HT20/HT40. This is used to sync the state * of ic_curchan after a channel width change on a running vap. + * + * Same applies for VHT. */ void ieee80211_sync_curchan(struct ieee80211com *ic) @@ -718,6 +756,8 @@ ieee80211_sync_curchan(struct ieee80211com *ic) struct ieee80211_channel *c; c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic)); + c = ieee80211_vht_adjust_channel(ic, c, getvhtadjustflags(ic)); + if (c != ic->ic_curchan) { ic->ic_curchan = c; ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); @@ -743,10 +783,23 @@ ieee80211_setupcurchan(struct ieee80211com *ic, struct ieee80211_channel *c) * set of running vap's. This assumes we are called * after ni_chan is setup for each vap. */ + /* XXX VHT? */ /* NB: this assumes IEEE80211_FHT_USEHT40 > IEEE80211_FHT_HT */ if (flags > ieee80211_htchanflags(c)) c = ieee80211_ht_adjust_channel(ic, c, flags); } + + /* + * VHT promotion - this will at least promote to VHT20/40 + * based on what HT has done; it may further promote the + * channel to VHT80 or above. + */ + if (ic->ic_vhtcaps != 0) { + int flags = getvhtadjustflags(ic); + if (flags > ieee80211_vhtchanflags(c)) + c = ieee80211_vht_adjust_channel(ic, c, flags); + } + ic->ic_bsschan = ic->ic_curchan = c; ic->ic_curmode = ieee80211_chan2mode(ic->ic_curchan); ic->ic_rt = ieee80211_get_ratetable(ic->ic_curchan); @@ -849,6 +902,7 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, { struct ieee80211com *ic = vap->iv_ic; struct ieee80211_node *ni; + int do_ht = 0; ni = ieee80211_alloc_node(&ic->ic_sta, vap, se->se_macaddr); if (ni == NULL) { @@ -896,9 +950,13 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, if (ni->ni_ies.tdma_ie != NULL) ieee80211_parse_tdma(ni, ni->ni_ies.tdma_ie); #endif + if (ni->ni_ies.vhtcap_ie != NULL) + ieee80211_parse_vhtcap(ni, ni->ni_ies.vhtcap_ie); + if (ni->ni_ies.vhtopmode_ie != NULL) + ieee80211_parse_vhtopmode(ni, ni->ni_ies.vhtopmode_ie); - /* XXX parse VHT IEs */ /* XXX parse BSSLOAD IE */ + /* XXX parse TXPWRENV IE */ /* XXX parse APCHANREP IE */ } @@ -926,10 +984,43 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, ieee80211_ht_updateparams(ni, ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie); + do_ht = 1; + } + + /* + * Setup VHT state for this node if it's available. + * Same as the above. + * + * For now, don't allow 2GHz VHT operation. + */ + if (ni->ni_ies.vhtopmode_ie != NULL && + ni->ni_ies.vhtcap_ie != NULL && + vap->iv_flags_vht & IEEE80211_FVHT_VHT) { + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { + printf("%s: BSS %6D: 2GHz channel, VHT info; ignoring\n", + __func__, + ni->ni_macaddr, + ":"); + } else { + ieee80211_vht_node_init(ni); + ieee80211_vht_updateparams(ni, + ni->ni_ies.vhtcap_ie, + ni->ni_ies.vhtopmode_ie); + ieee80211_setup_vht_rates(ni, ni->ni_ies.vhtcap_ie, + ni->ni_ies.vhtopmode_ie); + do_ht = 1; + } + } + + /* Finally do the node channel change */ + if (do_ht) { + ieee80211_ht_updateparams_final(ni, ni->ni_ies.htcap_ie, + ni->ni_ies.htinfo_ie); ieee80211_setup_htrates(ni, ni->ni_ies.htcap_ie, IEEE80211_F_JOIN | IEEE80211_F_DOBRS); ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie); } + /* XXX else check for ath FF? */ /* XXX QoS? Difficult given that WME config is specific to a master */ @@ -1102,8 +1193,10 @@ node_cleanup(struct ieee80211_node *ni) "power save mode off, %u sta's in ps mode", vap->iv_ps_sta); } /* - * Cleanup any HT-related state. + * Cleanup any VHT and HT-related state. */ + if (ni->ni_flags & IEEE80211_NODE_VHT) + ieee80211_vht_node_cleanup(ni); if (ni->ni_flags & IEEE80211_NODE_HT) ieee80211_ht_node_cleanup(ni); #ifdef IEEE80211_SUPPORT_SUPERG @@ -1423,6 +1516,7 @@ ieee80211_node_create_wds(struct ieee80211vap *vap, if (vap->iv_flags & IEEE80211_F_FF) ni->ni_flags |= IEEE80211_NODE_FF; #endif + /* XXX VHT */ if ((ic->ic_htcaps & IEEE80211_HTC_HT) && (vap->iv_flags_ht & IEEE80211_FHT_HT)) { /* @@ -1431,6 +1525,9 @@ ieee80211_node_create_wds(struct ieee80211vap *vap, * ni_chan will be adjusted to an HT channel. */ ieee80211_ht_wds_init(ni); + if (vap->iv_flags_vht & IEEE80211_FVHT_VHT) { + printf("%s: TODO: vht_wds_init\n", __func__); + } } else { struct ieee80211_channel *c = ni->ni_chan; /* @@ -1638,7 +1735,7 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, const struct ieee80211_frame *wh, const struct ieee80211_scanparams *sp) { - int do_ht_setup = 0; + int do_ht_setup = 0, do_vht_setup = 0; ni->ni_esslen = sp->ssid[1]; memcpy(ni->ni_essid, sp->ssid + 2, sp->ssid[1]); @@ -1670,11 +1767,23 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, if (ni->ni_ies.htinfo_ie != NULL) ieee80211_parse_htinfo(ni, ni->ni_ies.htinfo_ie); + if (ni->ni_ies.vhtcap_ie != NULL) + ieee80211_parse_vhtcap(ni, ni->ni_ies.vhtcap_ie); + if (ni->ni_ies.vhtopmode_ie != NULL) + ieee80211_parse_vhtopmode(ni, ni->ni_ies.vhtopmode_ie); + if ((ni->ni_ies.htcap_ie != NULL) && (ni->ni_ies.htinfo_ie != NULL) && (ni->ni_vap->iv_flags_ht & IEEE80211_FHT_HT)) { do_ht_setup = 1; } + + if ((ni->ni_ies.vhtcap_ie != NULL) && + (ni->ni_ies.vhtopmode_ie != NULL) && + (ni->ni_vap->iv_flags_vht & IEEE80211_FVHT_VHT)) { + do_vht_setup = 1; + } + } /* NB: must be after ni_chan is setup */ @@ -1692,15 +1801,40 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, ieee80211_ht_updateparams(ni, ni->ni_ies.htcap_ie, ni->ni_ies.htinfo_ie); + + if (do_vht_setup) { + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { + printf("%s: BSS %6D: 2GHz channel, VHT info; ignoring\n", + __func__, + ni->ni_macaddr, + ":"); + } else { + ieee80211_vht_node_init(ni); + ieee80211_vht_updateparams(ni, + ni->ni_ies.vhtcap_ie, + ni->ni_ies.vhtopmode_ie); + ieee80211_setup_vht_rates(ni, + ni->ni_ies.vhtcap_ie, + ni->ni_ies.vhtopmode_ie); + } + } + + /* + * Finally do the channel upgrade/change based + * on the HT/VHT configuration. + */ + ieee80211_ht_updateparams_final(ni, ni->ni_ies.htcap_ie, + ni->ni_ies.htinfo_ie); ieee80211_setup_htrates(ni, ni->ni_ies.htcap_ie, IEEE80211_F_JOIN | IEEE80211_F_DOBRS); ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie); + ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); - /* Reassociate; we're now 11n */ + /* Reassociate; we're now 11n/11ac */ /* * XXX TODO: this is the wrong thing to do - * we're calling it with isnew=1 so the ath(4) @@ -2365,6 +2499,7 @@ ieee80211_node_timeout(void *arg) IEEE80211_LOCK(ic); ieee80211_erp_timeout(ic); ieee80211_ht_timeout(ic); + ieee80211_vht_timeout(ic); IEEE80211_UNLOCK(ic); } callout_reset(&ic->ic_inact, IEEE80211_INACT_WAIT*hz, @@ -2462,8 +2597,12 @@ ieee80211_dump_node(struct ieee80211_node_table *nt, struct ieee80211_node *ni) printf("\thtcap %x htparam %x htctlchan %u ht2ndchan %u\n", ni->ni_htcap, ni->ni_htparam, ni->ni_htctlchan, ni->ni_ht2ndchan); - printf("\thtopmode %x htstbc %x chw %u\n", + printf("\thtopmode %x htstbc %x htchw %u\n", ni->ni_htopmode, ni->ni_htstbc, ni->ni_chw); + printf("\tvhtcap %x freq1 %d freq2 %d vhtbasicmcs %x\n", + ni->ni_vhtcap, (int) ni->ni_vht_chan1, (int) ni->ni_vht_chan2, + (int) ni->ni_vht_basicmcs); + /* XXX VHT state */ } void @@ -2594,6 +2733,8 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp) if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) ieee80211_ht_node_join(ni); + if (IEEE80211_IS_CHAN_VHT(ic->ic_bsschan)) + ieee80211_vht_node_join(ni); if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && IEEE80211_IS_CHAN_FULL(ic->ic_bsschan)) ieee80211_node_join_11g(ni); @@ -2603,6 +2744,9 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp) } else newassoc = 0; + /* + * XXX VHT - should log VHT channel width, etc + */ IEEE80211_NOTE(vap, IEEE80211_MSG_ASSOC | IEEE80211_MSG_DEBUG, ni, "station associated at aid %d: %s preamble, %s slot time%s%s%s%s%s%s%s%s", IEEE80211_NODE_AID(ni), @@ -2610,6 +2754,7 @@ ieee80211_node_join(struct ieee80211_node *ni, int resp) ic->ic_flags & IEEE80211_F_SHSLOT ? "short" : "long", ic->ic_flags & IEEE80211_F_USEPROT ? ", protection" : "", ni->ni_flags & IEEE80211_NODE_QOS ? ", QoS" : "", + /* XXX update for VHT string */ ni->ni_flags & IEEE80211_NODE_HT ? (ni->ni_chw == 40 ? ", HT40" : ", HT20") : "", ni->ni_flags & IEEE80211_NODE_AMPDU ? " (+AMPDU)" : "", @@ -2774,6 +2919,8 @@ ieee80211_node_leave(struct ieee80211_node *ni) vap->iv_sta_assoc--; ic->ic_sta_assoc--; + if (IEEE80211_IS_CHAN_VHT(ic->ic_bsschan)) + ieee80211_vht_node_leave(ni); if (IEEE80211_IS_CHAN_HT(ic->ic_bsschan)) ieee80211_ht_node_leave(ni); if (IEEE80211_IS_CHAN_ANYG(ic->ic_bsschan) && diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index c71f8541085b..09a9b00668a0 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #endif #include #include +#include #if defined(INET) || defined(INET6) #include @@ -764,6 +765,16 @@ ieee80211_send_setup( } *(uint16_t *)&wh->i_dur[0] = 0; + /* + * XXX TODO: this is what the TX lock is for. + * Here we're incrementing sequence numbers, and they + * need to be in lock-step with what the driver is doing + * both in TX ordering and crypto encap (IV increment.) + * + * If the driver does seqno itself, then we can skip + * assigning sequence numbers here, and we can avoid + * requiring the TX lock. + */ tap = &ni->ni_tx_ampdu[tid]; if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) m->m_flags |= M_AMPDU_MPDU; @@ -1542,6 +1553,11 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, if (is_amsdu) qos[0] |= IEEE80211_QOS_AMSDU; + /* + * XXX TODO TX lock is needed for atomic updates of sequence + * numbers. If the driver does it, then don't do it here; + * and we don't need the TX lock held. + */ if ((m->m_flags & M_AMPDU_MPDU) == 0) { /* * NB: don't assign a sequence # to potential @@ -1561,6 +1577,11 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni, M_SEQNO_SET(m, seqno); } } else { + /* + * XXX TODO TX lock is needed for atomic updates of sequence + * numbers. If the driver does it, then don't do it here; + * and we don't need the TX lock held. + */ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; *(uint16_t *)wh->i_seq = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); @@ -2065,6 +2086,7 @@ ieee80211_add_qos(uint8_t *frm, const struct ieee80211_node *ni) * Send a probe request frame with the specified ssid * and any optional information element data. */ +/* XXX VHT? */ int ieee80211_send_probereq(struct ieee80211_node *ni, const uint8_t sa[IEEE80211_ADDR_LEN], @@ -2111,6 +2133,7 @@ ieee80211_send_probereq(struct ieee80211_node *ni, * [tlv] RSN (optional) * [tlv] extended supported rates * [tlv] HT cap (optional) + * [tlv] VHT cap (optional) * [tlv] WPA (optional) * [tlv] user-specified ie's */ @@ -2119,7 +2142,8 @@ ieee80211_send_probereq(struct ieee80211_node *ni, 2 + IEEE80211_NWID_LEN + 2 + IEEE80211_RATE_SIZE + sizeof(struct ieee80211_ie_htcap) - + sizeof(struct ieee80211_ie_htinfo) + + sizeof(struct ieee80211_ie_vhtcap) + + sizeof(struct ieee80211_ie_htinfo) /* XXX not needed? */ + sizeof(struct ieee80211_ie_wpa) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_wpa) @@ -2159,6 +2183,21 @@ ieee80211_send_probereq(struct ieee80211_node *ni, frm = ieee80211_add_htcap_ch(frm, vap, c); } + /* + * XXX TODO: need to figure out what/how to update the + * VHT channel. + */ +#if 0 + (vap->iv_flags_vht & IEEE80211_FVHT_VHT) { + struct ieee80211_channel *c; + + c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, + vap->iv_flags_ht); + c = ieee80211_vht_adjust_channel(ic, c, vap->iv_flags_vht); + frm = ieee80211_add_vhtcap_ch(frm, vap, c); + } +#endif + frm = ieee80211_add_wpa(frm, vap); if (vap->iv_appie_probereq != NULL) frm = add_appie(frm, vap->iv_appie_probereq); @@ -2357,6 +2396,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) case IEEE80211_FC0_SUBTYPE_ASSOC_REQ: case IEEE80211_FC0_SUBTYPE_REASSOC_REQ: + /* XXX VHT? */ /* * asreq frame format * [2] capability information @@ -2368,6 +2408,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) * [4] power capability (optional) * [28] supported channels (optional) * [tlv] HT capabilities + * [tlv] VHT capabilities * [tlv] WME (optional) * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Atheros capabilities (if negotiated) @@ -2385,6 +2426,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) + 2 + 26 + sizeof(struct ieee80211_wme_info) + sizeof(struct ieee80211_ie_htcap) + + sizeof(struct ieee80211_ie_vhtcap) + 4 + sizeof(struct ieee80211_ie_htcap) #ifdef IEEE80211_SUPPORT_SUPERG + sizeof(struct ieee80211_ath_ie) @@ -2449,6 +2491,14 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) ni->ni_ies.htcap_ie[0] == IEEE80211_ELEMID_HTCAP) { frm = ieee80211_add_htcap(frm, ni); } + + if ((vap->iv_flags_vht & IEEE80211_FVHT_VHT) && + IEEE80211_IS_CHAN_VHT(ni->ni_chan) && + ni->ni_ies.vhtcap_ie != NULL && + ni->ni_ies.vhtcap_ie[0] == IEEE80211_ELEMID_VHT_CAP) { + frm = ieee80211_add_vhtcap(frm, ni); + } + frm = ieee80211_add_wpa(frm, vap); if ((ic->ic_flags & IEEE80211_F_WME) && ni->ni_ies.wme_ie != NULL) @@ -2492,6 +2542,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) * [tlv] extended supported rates * [tlv] HT capabilities (standard, if STA enabled) * [tlv] HT information (standard, if STA enabled) + * [tlv] VHT capabilities (standard, if STA enabled) + * [tlv] VHT information (standard, if STA enabled) * [tlv] WME (if configured and STA enabled) * [tlv] HT capabilities (vendor OUI, if STA enabled) * [tlv] HT information (vendor OUI, if STA enabled) @@ -2507,6 +2559,8 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) + 2 + (IEEE80211_RATE_MAXSIZE - IEEE80211_RATE_SIZE) + sizeof(struct ieee80211_ie_htcap) + 4 + sizeof(struct ieee80211_ie_htinfo) + 4 + + sizeof(struct ieee80211_ie_vhtcap) + + sizeof(struct ieee80211_ie_vht_operation) + sizeof(struct ieee80211_wme_param) #ifdef IEEE80211_SUPPORT_SUPERG + sizeof(struct ieee80211_ath_ie) @@ -2545,6 +2599,10 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } + if (ni->ni_flags & IEEE80211_NODE_VHT) { + frm = ieee80211_add_vhtcap(frm, ni); + frm = ieee80211_add_vhtinfo(frm, ni); + } #ifdef IEEE80211_SUPPORT_SUPERG if (IEEE80211_ATH_CAP(vap, ni, IEEE80211_F_ATHEROS)) frm = ieee80211_add_ath(frm, @@ -2600,6 +2658,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg) * Space is left to prepend and 802.11 header at the * front but it's left to the caller to fill in. */ +/* XXX VHT? */ struct mbuf * ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy) { @@ -2934,6 +2993,7 @@ ieee80211_tx_mgt_cb(struct ieee80211_node *ni, void *arg, int status) } } +/* XXX VHT? */ static void ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, struct ieee80211_node *ni) @@ -2946,6 +3006,10 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, /* * beacon frame format + * + * TODO: update to 802.11-2012; a lot of stuff has changed; + * vendor extensions should be at the end, etc. + * * [8] time stamp * [2] beacon interval * [2] cabability information @@ -2957,11 +3021,34 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, * [tlv] country (optional) * [3] power control (optional) * [5] channel switch announcement (CSA) (optional) + * XXX TODO: Quiet + * XXX TODO: IBSS DFS + * XXX TODO: TPC report * [tlv] extended rate phy (ERP) * [tlv] extended supported rates * [tlv] RSN parameters + * XXX TODO: BSSLOAD + * (XXX EDCA parameter set, QoS capability?) + * XXX TODO: AP channel report + * * [tlv] HT capabilities * [tlv] HT information + * XXX TODO: 20/40 BSS coexistence + * Mesh: + * XXX TODO: Meshid + * XXX TODO: mesh config + * XXX TODO: mesh awake window + * XXX TODO: beacon timing (mesh, etc) + * XXX TODO: MCCAOP Advertisement Overview + * XXX TODO: MCCAOP Advertisement + * XXX TODO: Mesh channel switch parameters + * VHT: + * XXX TODO: VHT capabilities + * XXX TODO: VHT operation + * XXX TODO: VHT transmit power envelope + * XXX TODO: channel switch wrapper element + * XXX TODO: extended BSS load element + * * XXX Vendor-specific OIDs (e.g. Atheros) * [tlv] WPA parameters * [tlv] WME parameters @@ -3055,6 +3142,16 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, bo->bo_htinfo = frm; frm = ieee80211_add_htinfo(frm, ni); } + + if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) { + frm = ieee80211_add_vhtcap(frm, ni); + bo->bo_vhtinfo = frm; + frm = ieee80211_add_vhtinfo(frm, ni); + /* Transmit power envelope */ + /* Channel switch wrapper element */ + /* Extended bss load element */ + } + frm = ieee80211_add_wpa(frm, vap); if (vap->iv_flags & IEEE80211_F_WME) { bo->bo_wme = frm; @@ -3065,6 +3162,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, frm = ieee80211_add_htcap_vendor(frm, ni); frm = ieee80211_add_htinfo_vendor(frm, ni); } + #ifdef IEEE80211_SUPPORT_SUPERG if (vap->iv_flags & IEEE80211_F_ATHEROS) { bo->bo_ath = frm; @@ -3082,6 +3180,8 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, bo->bo_appie_len = vap->iv_appie_beacon->ie_len; frm = add_appie(frm, vap->iv_appie_beacon); } + + /* XXX TODO: move meshid/meshconf up to before vendor extensions? */ #ifdef IEEE80211_SUPPORT_MESH if (vap->iv_opmode == IEEE80211_M_MBSS) { frm = ieee80211_add_meshid(frm, vap); @@ -3097,6 +3197,7 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm, /* * Allocate a beacon frame and fillin the appropriate bits. */ +/* XXX VHT? */ struct mbuf * ieee80211_beacon_alloc(struct ieee80211_node *ni) { @@ -3110,6 +3211,9 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni) /* * beacon frame format + * + * Note: This needs updating for 802.11-2012. + * * [8] time stamp * [2] beacon interval * [2] cabability information @@ -3126,6 +3230,8 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni) * [tlv] RSN parameters * [tlv] HT capabilities * [tlv] HT information + * [tlv] VHT capabilities + * [tlv] VHT operation * [tlv] Vendor OUI HT capabilities (optional) * [tlv] Vendor OUI HT information (optional) * XXX Vendor-specific OIDs (e.g. Atheros) @@ -3138,6 +3244,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni) * NB: we allocate the max space required for the TIM bitmap. * XXX how big is this? */ + /* XXX VHT? */ pktlen = 8 /* time stamp */ + sizeof(uint16_t) /* beacon interval */ + sizeof(uint16_t) /* capabilities */ @@ -3157,6 +3264,8 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni) /* XXX conditional? */ + 4+2*sizeof(struct ieee80211_ie_htcap)/* HT caps */ + 4+2*sizeof(struct ieee80211_ie_htinfo)/* HT info */ + + sizeof(struct ieee80211_ie_vhtcap)/* VHT caps */ + + sizeof(struct ieee80211_ie_vht_operation)/* VHT info */ + (vap->iv_caps & IEEE80211_C_WME ? /* WME */ sizeof(struct ieee80211_wme_param) : 0) #ifdef IEEE80211_SUPPORT_SUPERG @@ -3242,6 +3351,15 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) } wh = mtod(m, struct ieee80211_frame *); + + /* + * XXX TODO Strictly speaking this should be incremented with the TX + * lock held so as to serialise access to the non-qos TID sequence + * number space. + * + * If the driver identifies it does its own TX seqno management then + * we can skip this (and still not do the TX seqno.) + */ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++; *(uint16_t *)&wh->i_seq[0] = htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT); @@ -3347,6 +3465,10 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) timoff = 0; timlen = 1; } + + /* + * TODO: validate this! + */ if (timlen != bo->bo_tim_len) { /* copy up/down trailer */ int adjust = tie->tim_bitmap+timlen @@ -3357,6 +3479,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) bo->bo_tim_trailer += adjust; bo->bo_erp += adjust; bo->bo_htinfo += adjust; + bo->bo_vhtinfo += adjust; #ifdef IEEE80211_SUPPORT_SUPERG bo->bo_ath += adjust; #endif @@ -3411,6 +3534,7 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast) memmove(&csa[1], csa, bo->bo_csa_trailer_len); bo->bo_erp += sizeof(*csa); bo->bo_htinfo += sizeof(*csa); + bo->bo_vhtinfo += sizeof(*csa); bo->bo_wme += sizeof(*csa); #ifdef IEEE80211_SUPPORT_SUPERG bo->bo_ath += sizeof(*csa); diff --git a/sys/net80211/ieee80211_scan_sta.c b/sys/net80211/ieee80211_scan_sta.c index 82d65c6ecf73..29e4db849a90 100644 --- a/sys/net80211/ieee80211_scan_sta.c +++ b/sys/net80211/ieee80211_scan_sta.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include #endif #include +#include #include @@ -325,14 +326,33 @@ sta_add(struct ieee80211_scan_state *ss, } } else ise->se_chan = curchan; + + /* VHT demotion */ + if (IEEE80211_IS_CHAN_VHT(ise->se_chan) && sp->vhtcap == NULL) { + IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N, + "%s: demoting VHT->HT %d/0x%08x\n", + __func__, ise->se_chan->ic_freq, ise->se_chan->ic_flags); + /* Demote legacy networks to a non-VHT channel. */ + c = ieee80211_find_channel(ic, ise->se_chan->ic_freq, + ise->se_chan->ic_flags & ~IEEE80211_CHAN_VHT); + KASSERT(c != NULL, + ("no non-VHT channel %u", ise->se_chan->ic_ieee)); + ise->se_chan = c; + } + + /* HT demotion */ if (IEEE80211_IS_CHAN_HT(ise->se_chan) && sp->htcap == NULL) { /* Demote legacy networks to a non-HT channel. */ + IEEE80211_DPRINTF(vap, IEEE80211_MSG_11N, + "%s: demoting HT->legacy %d/0x%08x\n", + __func__, ise->se_chan->ic_freq, ise->se_chan->ic_flags); c = ieee80211_find_channel(ic, ise->se_chan->ic_freq, ise->se_chan->ic_flags & ~IEEE80211_CHAN_HT); KASSERT(c != NULL, ("no legacy channel %u", ise->se_chan->ic_ieee)); ise->se_chan = c; } + ise->se_fhdwell = sp->fhdwell; ise->se_fhindex = sp->fhindex; ise->se_erp = sp->erp; @@ -531,10 +551,11 @@ sweepchannels(struct ieee80211_scan_state *ss, struct ieee80211vap *vap, /* * Ignore dynamic turbo channels; we scan them * in normal mode (i.e. not boosted). Likewise - * for HT channels, they get scanned using + * for HT/VHT channels, they get scanned using * legacy rates. */ - if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c)) + if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c) || + IEEE80211_IS_CHAN_VHT(c)) continue; /* @@ -819,6 +840,9 @@ maxrate(const struct ieee80211_scan_entry *se) * that we assume compatibility/usability has already been checked * so we don't need to (e.g. validate whether privacy is supported). * Used to select the best scan candidate for association in a BSS. + * + * TODO: should we take 11n, 11ac into account when selecting the + * best? Right now it just compares frequency band and RSSI. */ static int sta_compare(const struct sta_entry *a, const struct sta_entry *b) @@ -1626,6 +1650,8 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) */ chan = ieee80211_ht_adjust_channel(ic, chan, vap->iv_flags_ht); + chan = ieee80211_vht_adjust_channel(ic, + chan, vap->iv_flags_vht); ieee80211_create_ibss(vap, chan); return 1; } @@ -1657,6 +1683,8 @@ adhoc_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) */ chan = ieee80211_ht_adjust_channel(ic, chan, vap->iv_flags_ht); + chan = ieee80211_vht_adjust_channel(ic, + chan, vap->iv_flags_vht); if (!ieee80211_sta_join(vap, chan, &selbs->base)) goto notfound; return 1; /* terminate scan */ @@ -1776,7 +1804,7 @@ static int ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) { struct ieee80211com *ic = vap->iv_ic; - struct ieee80211_channel *bestchan; + struct ieee80211_channel *bestchan, *chan; KASSERT(vap->iv_opmode == IEEE80211_M_HOSTAP, ("wrong opmode %u", vap->iv_opmode)); @@ -1808,8 +1836,10 @@ ap_end(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) ss->ss_flags &= ~IEEE80211_SCAN_NOPICK; return 1; } - ieee80211_create_ibss(vap, - ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ht)); + chan = ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ht); + chan = ieee80211_vht_adjust_channel(ic, chan, vap->iv_flags_vht); + ieee80211_create_ibss(vap, chan); + return 1; } @@ -1881,10 +1911,14 @@ mesh_pick_bss(struct ieee80211_scan_state *ss, struct ieee80211vap *vap) IEEE80211_IS_CHAN_RADAR(vap->iv_des_chan)) { struct ieee80211com *ic = vap->iv_ic; + /* XXX VHT */ chan = adhoc_pick_channel(ss, 0); - if (chan != NULL) + if (chan != NULL) { chan = ieee80211_ht_adjust_channel(ic, chan, vap->iv_flags_ht); + chan = ieee80211_vht_adjust_channel(ic, + chan, vap->iv_flags_vht); + } } else chan = vap->iv_des_chan; if (chan != NULL) { diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index dd9ef6e4d21a..f64232a2b804 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$"); #endif #include #include +#include #define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2) @@ -1330,8 +1331,9 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, struct ieee80211_frame *wh; uint8_t *frm, *efrm; uint8_t *rates, *xrates, *wme, *htcap, *htinfo; + uint8_t *vhtcap, *vhtopmode; uint8_t rate; - int ht_state_change = 0; + int ht_state_change = 0, do_ht = 0; wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; @@ -1430,10 +1432,23 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, if (scan.htcap != NULL && scan.htinfo != NULL && (vap->iv_flags_ht & IEEE80211_FHT_HT)) { /* XXX state changes? */ - if (ieee80211_ht_updateparams(ni, + ieee80211_ht_updateparams(ni, + scan.htcap, scan.htinfo); + do_ht = 1; + } + if (scan.vhtcap != NULL && scan.vhtopmode != NULL && + (vap->iv_flags_vht & IEEE80211_FVHT_VHT)) { + /* XXX state changes? */ + ieee80211_vht_updateparams(ni, + scan.vhtcap, scan.vhtopmode); + do_ht = 1; + } + if (do_ht) { + if (ieee80211_ht_updateparams_final(ni, scan.htcap, scan.htinfo)) ht_state_change = 1; } + if (scan.quiet) ic->ic_set_quiet(ni, scan.quiet); @@ -1660,6 +1675,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, frm += 2; rates = xrates = wme = htcap = htinfo = NULL; + vhtcap = vhtopmode = NULL; while (efrm - frm > 1) { IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return); switch (*frm) { @@ -1693,6 +1709,12 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, } /* XXX Atheros OUI support */ break; + case IEEE80211_ELEMID_VHT_CAP: + vhtcap = frm; + break; + case IEEE80211_ELEMID_VHT_OPMODE: + vhtopmode = frm; + break; } frm += frm[1] + 2; } @@ -1737,9 +1759,30 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, (vap->iv_flags_ht & IEEE80211_FHT_HT)) { ieee80211_ht_node_init(ni); ieee80211_ht_updateparams(ni, htcap, htinfo); + + if ((vhtcap != NULL) && (vhtopmode != NULL) & + (vap->iv_flags_vht & IEEE80211_FVHT_VHT)) { + /* + * Log if we get a VHT assoc/reassoc response. + * We aren't ready for 2GHz VHT support. + */ + if (IEEE80211_IS_CHAN_2GHZ(ni->ni_chan)) { + printf("%s: peer %6D: VHT on 2GHz, ignoring\n", + __func__, + ni->ni_macaddr, + ":"); + } else { + ieee80211_vht_node_init(ni); + ieee80211_vht_updateparams(ni, vhtcap, vhtopmode); + ieee80211_setup_vht_rates(ni, vhtcap, vhtopmode); + } + } + + ieee80211_ht_updateparams_final(ni, htcap, htinfo); ieee80211_setup_htrates(ni, htcap, IEEE80211_F_JOIN | IEEE80211_F_DOBRS); ieee80211_setup_basic_htrates(ni, htinfo); + ieee80211_node_setuptxparms(ni); ieee80211_ratectl_node_init(ni); }