[net80211] Initial VHT node upgrade/downgrade support and initial IE parsing.
This is the bulk of the magic to start enabling VHT channel negotiation. It is absolutely, positively not yet even a complete VHT wave-1 implementation. * parse IEs in scan, assoc req/resp, probe req/resp; * break apart the channel upgrade from the HT IE parsing - do it after the VHT IEs are parsed; * (dirty! sigh) add channel width decision making in ieee80211_ht.c htinfo_update_chw(). This is the main bit where negotiated channel promotion through IEs occur. * Shoehorn in VHT node init ,teardown, rate control, etc calls like the HT versions; * Do VHT channel adjustment where appropriate Tested: * monitor mode, ath10k port * STA mode, ath10k port - VHT20, VHT40, VHT80 modes TODO: * IBSS; * hostap; * (ignore mesh, wds for now); * finish 11n state engine - channel width change, opmode notifications, SMPS, etc; * VHT basic rate negotiation and acceptance criteria when scanning, associating, etc; * VHT control/management frame handling (group managment and operating mode being the two big ones); * Verify TX/RX VHT rate negotiation is actually working correctly. Whilst here, add some comments about seqno allocation and locking. To achieve the full VHT rates I need to push seqno allocation into the drivers and finally remove the IEEE80211_TX_LOCK() I added years ago to fix issues. :/
This commit is contained in:
parent
67f4aa3878
commit
51172f62a7
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=312015
@ -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);
|
||||
|
@ -62,6 +62,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <net80211/ieee80211_superg.h>
|
||||
#endif
|
||||
#include <net80211/ieee80211_wds.h>
|
||||
#include <net80211/ieee80211_vht.h>
|
||||
|
||||
#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);
|
||||
|
@ -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;
|
||||
|
@ -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 *,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <net80211/ieee80211_wds.h>
|
||||
#include <net80211/ieee80211_mesh.h>
|
||||
#include <net80211/ieee80211_ratectl.h>
|
||||
#include <net80211/ieee80211_vht.h>
|
||||
|
||||
#include <net/bpf.h>
|
||||
|
||||
@ -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) &&
|
||||
|
@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
#include <net80211/ieee80211_wds.h>
|
||||
#include <net80211/ieee80211_mesh.h>
|
||||
#include <net80211/ieee80211_vht.h>
|
||||
|
||||
#if defined(INET) || defined(INET6)
|
||||
#include <netinet/in.h>
|
||||
@ -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);
|
||||
|
@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <net80211/ieee80211_mesh.h>
|
||||
#endif
|
||||
#include <net80211/ieee80211_ratectl.h>
|
||||
#include <net80211/ieee80211_vht.h>
|
||||
|
||||
#include <net/bpf.h>
|
||||
|
||||
@ -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) {
|
||||
|
@ -64,6 +64,7 @@ __FBSDID("$FreeBSD$");
|
||||
#endif
|
||||
#include <net80211/ieee80211_ratectl.h>
|
||||
#include <net80211/ieee80211_sta.h>
|
||||
#include <net80211/ieee80211_vht.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user