summaryrefslogtreecommitdiffstats
path: root/freebsd/sys/net80211
diff options
context:
space:
mode:
authorSebastian Huber <sebastian.huber@embedded-brains.de>2017-04-04 09:36:57 +0200
committerSebastian Huber <sebastian.huber@embedded-brains.de>2017-04-04 14:46:23 +0200
commitde8a76da2f374792594ce03a203b3f30e4889f6f (patch)
tree12b5e1e59358005c3c522955c08aee4795e4829c /freebsd/sys/net80211
parentEnable bridging by default (diff)
downloadrtems-libbsd-de8a76da2f374792594ce03a203b3f30e4889f6f.tar.bz2
Update to FreeBSD head 2017-04-04
Git mirror commit 642b174daddbd0efd9bb5f242c43f4ab4db6869f.
Diffstat (limited to 'freebsd/sys/net80211')
-rw-r--r--freebsd/sys/net80211/_ieee80211.h124
-rw-r--r--freebsd/sys/net80211/ieee80211.c327
-rw-r--r--freebsd/sys/net80211/ieee80211.h48
-rw-r--r--freebsd/sys/net80211/ieee80211_adhoc.c6
-rw-r--r--freebsd/sys/net80211/ieee80211_freebsd.c26
-rw-r--r--freebsd/sys/net80211/ieee80211_freebsd.h93
-rw-r--r--freebsd/sys/net80211/ieee80211_hostap.c41
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.c409
-rw-r--r--freebsd/sys/net80211/ieee80211_ht.h8
-rw-r--r--freebsd/sys/net80211/ieee80211_input.c26
-rw-r--r--freebsd/sys/net80211/ieee80211_input.h13
-rw-r--r--freebsd/sys/net80211/ieee80211_ioctl.c50
-rw-r--r--freebsd/sys/net80211/ieee80211_node.c169
-rw-r--r--freebsd/sys/net80211/ieee80211_node.h6
-rw-r--r--freebsd/sys/net80211/ieee80211_output.c300
-rw-r--r--freebsd/sys/net80211/ieee80211_proto.c44
-rw-r--r--freebsd/sys/net80211/ieee80211_proto.h2
-rw-r--r--freebsd/sys/net80211/ieee80211_scan_sta.c46
-rw-r--r--freebsd/sys/net80211/ieee80211_sta.c88
-rw-r--r--freebsd/sys/net80211/ieee80211_superg.c22
-rw-r--r--freebsd/sys/net80211/ieee80211_superg.h2
-rw-r--r--freebsd/sys/net80211/ieee80211_tdma.c2
-rw-r--r--freebsd/sys/net80211/ieee80211_var.h37
-rw-r--r--freebsd/sys/net80211/ieee80211_vht.c855
-rw-r--r--freebsd/sys/net80211/ieee80211_vht.h68
25 files changed, 2530 insertions, 282 deletions
diff --git a/freebsd/sys/net80211/_ieee80211.h b/freebsd/sys/net80211/_ieee80211.h
index 13155ea3..9434f3a6 100644
--- a/freebsd/sys/net80211/_ieee80211.h
+++ b/freebsd/sys/net80211/_ieee80211.h
@@ -313,6 +313,12 @@ struct ieee80211_channel {
#define IEEE80211_IS_CHAN_VHT(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_VHT) != 0)
+#define IEEE80211_IS_CHAN_VHT_2GHZ(_c) \
+ (IEEE80211_IS_CHAN_2GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_VHT) != 0)
+#define IEEE80211_IS_CHAN_VHT_5GHZ(_c) \
+ (IEEE80211_IS_CHAN_5GHZ(_c) && \
+ ((_c)->ic_flags & IEEE80211_CHAN_VHT) != 0)
#define IEEE80211_IS_CHAN_VHT20(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_VHT20) != 0)
#define IEEE80211_IS_CHAN_VHT40(_c) \
@@ -437,17 +443,26 @@ struct ieee80211_regdomain {
/*
* MIMO antenna/radio state.
*/
-
+#define IEEE80211_MAX_CHAINS 4
/*
- * XXX This doesn't yet export both ctl/ext chain details
- * XXX TODO: IEEE80211_MAX_CHAINS is defined in _freebsd.h, not here;
- * figure out how to pull it in!
+ * This is the number of sub-channels for a channel.
+ * 0 - pri20
+ * 1 - sec20 (HT40, VHT40)
+ * 2 - sec40 (VHT80)
+ * 3 - sec80 (VHT80+80, VHT160)
*/
+#define IEEE80211_MAX_CHAIN_PRISEC 4
+#define IEEE80211_MAX_EVM_DWORDS 16 /* 16 pilots, 4 chains */
+#define IEEE80211_MAX_EVM_PILOTS 16 /* 468 subcarriers, 16 pilots */
+
+struct ieee80211_mimo_chan_info {
+ int8_t rssi[IEEE80211_MAX_CHAIN_PRISEC];
+ int8_t noise[IEEE80211_MAX_CHAIN_PRISEC];
+};
+
struct ieee80211_mimo_info {
- int8_t rssi[3]; /* per-antenna rssi */
- int8_t noise[3]; /* per-antenna noise floor */
- uint8_t pad[2];
- uint32_t evm[3]; /* EVM data */
+ struct ieee80211_mimo_chan_info ch[IEEE80211_MAX_CHAINS];
+ uint32_t evm[IEEE80211_MAX_EVM_DWORDS];
};
/*
@@ -511,9 +526,100 @@ struct ieee80211_mimo_info {
#define IEEE80211_HTC_RXMCS32 0x00400000 /* CAPABILITY: MCS32 support */
#define IEEE80211_HTC_TXUNEQUAL 0x00800000 /* CAPABILITY: TX unequal MCS */
#define IEEE80211_HTC_TXMCS32 0x01000000 /* CAPABILITY: MCS32 support */
+#define IEEE80211_HTC_TXLDPC 0x02000000 /* CAPABILITY: TX using LDPC */
#define IEEE80211_C_HTCAP_BITS \
"\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
- "\21AMPDU\22AMSDU\23HT\24SMPS\25RIFS"
+ "\21AMPDU\22AMSDU\23HT\24SMPS\25RIFS\32TXLDPC"
+
+/*
+ * RX status notification - which fields are valid.
+ */
+#define IEEE80211_R_NF 0x00000001 /* global NF value valid */
+#define IEEE80211_R_RSSI 0x00000002 /* global RSSI value valid */
+#define IEEE80211_R_C_CHAIN 0x00000004 /* RX chain count valid */
+#define IEEE80211_R_C_NF 0x00000008 /* per-chain NF value valid */
+#define IEEE80211_R_C_RSSI 0x00000010 /* per-chain RSSI value valid */
+#define IEEE80211_R_C_EVM 0x00000020 /* per-chain EVM valid */
+#define IEEE80211_R_C_HT40 0x00000040 /* RX'ed packet is 40mhz, pilots 4,5 valid */
+#define IEEE80211_R_FREQ 0x00000080 /* Freq value populated, MHz */
+#define IEEE80211_R_IEEE 0x00000100 /* IEEE value populated */
+#define IEEE80211_R_BAND 0x00000200 /* Frequency band populated */
+#define IEEE80211_R_TSF32 0x00004000 /* 32 bit TSF */
+#define IEEE80211_R_TSF64 0x00008000 /* 64 bit TSF */
+#define IEEE80211_R_TSF_START 0x00010000 /* TSF is sampled at start of frame */
+#define IEEE80211_R_TSF_END 0x00020000 /* TSF is sampled at end of frame */
+
+/*
+ * RX status notification - describe the packet.
+ */
+#define IEEE80211_RX_F_STBC 0x00000001
+#define IEEE80211_RX_F_LDPC 0x00000002
+#define IEEE80211_RX_F_AMSDU 0x00000004 /* This is the start of an decap AMSDU list */
+#define IEEE80211_RX_F_AMSDU_MORE 0x00000008 /* This is another decap AMSDU frame in the batch */
+#define IEEE80211_RX_F_AMPDU 0x00000010 /* This is the start of an decap AMPDU list */
+#define IEEE80211_RX_F_AMPDU_MORE 0x00000020 /* This is another decap AMPDU frame in the batch */
+#define IEEE80211_RX_F_FAIL_FCSCRC 0x00000040 /* Failed CRC/FCS */
+#define IEEE80211_RX_F_FAIL_MIC 0x00000080 /* Failed MIC check */
+#define IEEE80211_RX_F_DECRYPTED 0x00000100 /* Hardware decrypted */
+#define IEEE80211_RX_F_IV_STRIP 0x00000200 /* Decrypted; IV stripped */
+#define IEEE80211_RX_F_MMIC_STRIP 0x00000400 /* Decrypted; MMIC stripped */
+#define IEEE80211_RX_F_SHORTGI 0x00000800 /* This is a short-GI frame */
+#define IEEE80211_RX_F_CCK 0x00001000
+#define IEEE80211_RX_F_OFDM 0x00002000
+#define IEEE80211_RX_F_HT 0x00004000
+#define IEEE80211_RX_F_VHT 0x00008000
+
+/* Channel width */
+#define IEEE80211_RX_FW_20MHZ 1
+#define IEEE80211_RX_FW_40MHZ 2
+#define IEEE80211_RX_FW_80MHZ 3
+
+/* PHY type */
+#define IEEE80211_RX_FP_11B 1
+#define IEEE80211_RX_FP_11G 2
+#define IEEE80211_RX_FP_11A 3
+#define IEEE80211_RX_FP_11NA 4
+#define IEEE80211_RX_FP_11NG 5
+
+struct ieee80211_rx_stats {
+ uint32_t r_flags; /* IEEE80211_R_* flags */
+ uint32_t c_pktflags; /* IEEE80211_RX_F_* flags */
+
+ uint64_t c_rx_tsf; /* 32 or 64 bit TSF */
+
+ /* All DWORD aligned */
+ int16_t c_nf_ctl[IEEE80211_MAX_CHAINS]; /* per-chain NF */
+ int16_t c_nf_ext[IEEE80211_MAX_CHAINS]; /* per-chain NF */
+ int16_t c_rssi_ctl[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
+ int16_t c_rssi_ext[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
+
+ /* 32 bits */
+ uint8_t c_nf; /* global NF */
+ uint8_t c_rssi; /* global RSSI */
+ uint8_t c_chain; /* number of RX chains involved */
+ uint8_t c_rate; /* legacy; 11n rate code; VHT MCS */
+
+ /* 32 bits */
+ uint16_t c_freq; /* Frequency, MHz */
+ uint8_t c_ieee; /* Channel */
+ uint8_t c_width; /* channel width, FW flags above */
+
+ /* Force alignment to DWORD */
+ union {
+ uint8_t evm[IEEE80211_MAX_CHAINS][IEEE80211_MAX_EVM_PILOTS];
+ /* per-chain, per-pilot EVM values */
+ uint32_t __aln[8];
+ } evm;
+
+ /* 32 bits */
+ uint8_t c_phytype; /* PHY type, FW flags above */
+ uint8_t c_vhtnss; /* VHT - number of spatial streams */
+ uint8_t c_pad2[2];
+};
+
+struct ieee80211_rx_params {
+ struct ieee80211_rx_stats params;
+};
#endif /* _NET80211__IEEE80211_H_ */
diff --git a/freebsd/sys/net80211/ieee80211.c b/freebsd/sys/net80211/ieee80211.c
index 7fcd3dcd..9e9be3d7 100644
--- a/freebsd/sys/net80211/ieee80211.c
+++ b/freebsd/sys/net80211/ieee80211.c
@@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_superg.h>
#endif
#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_vht.h>
#include <net/bpf.h>
@@ -72,6 +73,8 @@ const char *ieee80211_phymode_name[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_QUARTER] = "quarter",
[IEEE80211_MODE_11NA] = "11na",
[IEEE80211_MODE_11NG] = "11ng",
+ [IEEE80211_MODE_VHT_2GHZ] = "11acg",
+ [IEEE80211_MODE_VHT_5GHZ] = "11ac",
};
/* map ieee80211_opmode to the corresponding capability bit */
const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = {
@@ -119,6 +122,8 @@ static const struct ieee80211_rateset ieee80211_rateset_11g =
{ 12, { B(2), B(4), B(11), B(22), 12, 18, 24, 36, 48, 72, 96, 108 } };
#undef B
+static int set_vht_extchan(struct ieee80211_channel *c);
+
/*
* Fill in 802.11 available channel set, mark
* all available channels as active, and pick
@@ -150,10 +155,23 @@ ieee80211_chan_init(struct ieee80211com *ic)
*/
if (c->ic_ieee == 0)
c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags);
+
+ /*
+ * Setup the HT40/VHT40 upper/lower bits.
+ * The VHT80 math is done elsewhere.
+ */
if (IEEE80211_IS_CHAN_HT40(c) && c->ic_extieee == 0)
c->ic_extieee = ieee80211_mhz2ieee(c->ic_freq +
(IEEE80211_IS_CHAN_HT40U(c) ? 20 : -20),
c->ic_flags);
+
+ /* Update VHT math */
+ /*
+ * XXX VHT again, note that this assumes VHT80 channels
+ * are legit already
+ */
+ set_vht_extchan(c);
+
/* default max tx power to max regulatory */
if (c->ic_maxpower == 0)
c->ic_maxpower = 2*c->ic_maxregpower;
@@ -183,6 +201,10 @@ ieee80211_chan_init(struct ieee80211com *ic)
setbit(ic->ic_modecaps, IEEE80211_MODE_11NA);
if (IEEE80211_IS_CHAN_HTG(c))
setbit(ic->ic_modecaps, IEEE80211_MODE_11NG);
+ if (IEEE80211_IS_CHAN_VHTA(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_VHT_5GHZ);
+ if (IEEE80211_IS_CHAN_VHTG(c))
+ setbit(ic->ic_modecaps, IEEE80211_MODE_VHT_2GHZ);
}
/* initialize candidate channels to all available */
memcpy(ic->ic_chan_active, ic->ic_chan_avail,
@@ -210,6 +232,8 @@ ieee80211_chan_init(struct ieee80211com *ic)
DEFAULTRATES(IEEE80211_MODE_QUARTER, ieee80211_rateset_quarter);
DEFAULTRATES(IEEE80211_MODE_11NA, ieee80211_rateset_11a);
DEFAULTRATES(IEEE80211_MODE_11NG, ieee80211_rateset_11g);
+ DEFAULTRATES(IEEE80211_MODE_VHT_2GHZ, ieee80211_rateset_11g);
+ DEFAULTRATES(IEEE80211_MODE_VHT_5GHZ, ieee80211_rateset_11a);
/*
* Setup required information to fill the mcsset field, if driver did
@@ -220,6 +244,8 @@ ieee80211_chan_init(struct ieee80211com *ic)
if (ic->ic_txstream == 0)
ic->ic_txstream = 2;
+ ieee80211_init_suphtrates(ic);
+
/*
* Set auto mode to reset active channel state and any desired channel.
*/
@@ -337,6 +363,7 @@ ieee80211_ifattach(struct ieee80211com *ic)
ieee80211_superg_attach(ic);
#endif
ieee80211_ht_attach(ic);
+ ieee80211_vht_attach(ic);
ieee80211_scan_attach(ic);
ieee80211_regdomain_attach(ic);
ieee80211_dfs_attach(ic);
@@ -380,6 +407,7 @@ ieee80211_ifdetach(struct ieee80211com *ic)
#ifdef IEEE80211_SUPPORT_SUPERG
ieee80211_superg_detach(ic);
#endif
+ ieee80211_vht_detach(ic);
ieee80211_ht_detach(ic);
/* NB: must be called before ieee80211_node_detach */
ieee80211_proto_detach(ic);
@@ -509,8 +537,15 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
vap->iv_flags_ext = ic->ic_flags_ext;
vap->iv_flags_ven = ic->ic_flags_ven;
vap->iv_caps = ic->ic_caps &~ IEEE80211_C_OPMODE;
+
+ /* 11n capabilities - XXX methodize */
vap->iv_htcaps = ic->ic_htcaps;
vap->iv_htextcaps = ic->ic_htextcaps;
+
+ /* 11ac capabilities - XXX methodize */
+ vap->iv_vhtcaps = ic->ic_vhtcaps;
+ vap->iv_vhtextcaps = ic->ic_vhtextcaps;
+
vap->iv_opmode = opmode;
vap->iv_caps |= ieee80211_opcap[opmode];
IEEE80211_ADDR_COPY(vap->iv_myaddr, ic->ic_macaddr);
@@ -595,6 +630,7 @@ ieee80211_vap_setup(struct ieee80211com *ic, struct ieee80211vap *vap,
ieee80211_superg_vattach(vap);
#endif
ieee80211_ht_vattach(vap);
+ ieee80211_vht_vattach(vap);
ieee80211_scan_vattach(vap);
ieee80211_regdomain_vattach(vap);
ieee80211_radiotap_vattach(vap);
@@ -693,6 +729,8 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
*/
ieee80211_draintask(ic, &vap->iv_nstate_task);
ieee80211_draintask(ic, &vap->iv_swbmiss_task);
+ ieee80211_draintask(ic, &vap->iv_wme_task);
+ ieee80211_draintask(ic, &ic->ic_parent_task);
/* XXX band-aid until ifnet handles this for us */
taskqueue_drain(taskqueue_swi, &ifp->if_linktask);
@@ -731,6 +769,7 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
#ifdef IEEE80211_SUPPORT_SUPERG
ieee80211_superg_vdetach(vap);
#endif
+ ieee80211_vht_vdetach(vap);
ieee80211_ht_vdetach(vap);
/* NB: must be before ieee80211_node_vdetach */
ieee80211_proto_vdetach(vap);
@@ -1075,6 +1114,110 @@ set_extchan(struct ieee80211_channel *c)
c->ic_extieee = 0;
}
+/*
+ * Populate the freq1/freq2 fields as appropriate for VHT channels.
+ *
+ * This for now uses a hard-coded list of 80MHz wide channels.
+ *
+ * For HT20/HT40, freq1 just is the centre frequency of the 40MHz
+ * wide channel we've already decided upon.
+ *
+ * For VHT80 and VHT160, there are only a small number of fixed
+ * 80/160MHz wide channels, so we just use those.
+ *
+ * This is all likely very very wrong - both the regulatory code
+ * and this code needs to ensure that all four channels are
+ * available and valid before the VHT80 (and eight for VHT160) channel
+ * is created.
+ */
+
+struct vht_chan_range {
+ uint16_t freq_start;
+ uint16_t freq_end;
+};
+
+struct vht_chan_range vht80_chan_ranges[] = {
+ { 5170, 5250 },
+ { 5250, 5330 },
+ { 5490, 5570 },
+ { 5570, 5650 },
+ { 5650, 5730 },
+ { 5735, 5815 },
+ { 0, 0, }
+};
+
+static int
+set_vht_extchan(struct ieee80211_channel *c)
+{
+ int i;
+
+ if (! IEEE80211_IS_CHAN_VHT(c)) {
+ return (0);
+ }
+
+ if (IEEE80211_IS_CHAN_VHT20(c)) {
+ c->ic_vht_ch_freq1 = c->ic_ieee;
+ return (1);
+ }
+
+ if (IEEE80211_IS_CHAN_VHT40(c)) {
+ if (IEEE80211_IS_CHAN_HT40U(c))
+ c->ic_vht_ch_freq1 = c->ic_ieee + 2;
+ else if (IEEE80211_IS_CHAN_HT40D(c))
+ c->ic_vht_ch_freq1 = c->ic_ieee - 2;
+ else
+ return (0);
+ return (1);
+ }
+
+ if (IEEE80211_IS_CHAN_VHT80(c)) {
+ for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) {
+ if (c->ic_freq >= vht80_chan_ranges[i].freq_start &&
+ c->ic_freq < vht80_chan_ranges[i].freq_end) {
+ int midpoint;
+
+ midpoint = vht80_chan_ranges[i].freq_start + 40;
+ c->ic_vht_ch_freq1 =
+ ieee80211_mhz2ieee(midpoint, c->ic_flags);
+ c->ic_vht_ch_freq2 = 0;
+#if 0
+ printf("%s: %d, freq=%d, midpoint=%d, freq1=%d, freq2=%d\n",
+ __func__, c->ic_ieee, c->ic_freq, midpoint,
+ c->ic_vht_ch_freq1, c->ic_vht_ch_freq2);
+#endif
+ return (1);
+ }
+ }
+ return (0);
+ }
+
+ printf("%s: unknown VHT channel type (ieee=%d, flags=0x%08x)\n",
+ __func__,
+ c->ic_ieee,
+ c->ic_flags);
+
+ return (0);
+}
+
+/*
+ * Return whether the current channel could possibly be a part of
+ * a VHT80 channel.
+ *
+ * This doesn't check that the whole range is in the allowed list
+ * according to regulatory.
+ */
+static int
+is_vht80_valid_freq(uint16_t freq)
+{
+ int i;
+ for (i = 0; vht80_chan_ranges[i].freq_start != 0; i++) {
+ if (freq >= vht80_chan_ranges[i].freq_start &&
+ freq < vht80_chan_ranges[i].freq_end)
+ return (1);
+ }
+ return (0);
+}
+
static int
addchan(struct ieee80211_channel chans[], int maxchans, int *nchans,
uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags)
@@ -1084,13 +1227,25 @@ addchan(struct ieee80211_channel chans[], int maxchans, int *nchans,
if (*nchans >= maxchans)
return (ENOBUFS);
+#if 0
+ printf("%s: %d: ieee=%d, freq=%d, flags=0x%08x\n",
+ __func__,
+ *nchans,
+ ieee,
+ freq,
+ flags);
+#endif
+
c = &chans[(*nchans)++];
c->ic_ieee = ieee;
c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags);
c->ic_maxregpower = maxregpower;
c->ic_maxpower = 2 * maxregpower;
c->ic_flags = flags;
+ c->ic_vht_ch_freq1 = 0;
+ c->ic_vht_ch_freq2 = 0;
set_extchan(c);
+ set_vht_extchan(c);
return (0);
}
@@ -1106,14 +1261,27 @@ copychan_prev(struct ieee80211_channel chans[], int maxchans, int *nchans,
if (*nchans >= maxchans)
return (ENOBUFS);
+#if 0
+ printf("%s: %d: flags=0x%08x\n",
+ __func__,
+ *nchans,
+ flags);
+#endif
+
c = &chans[(*nchans)++];
c[0] = c[-1];
c->ic_flags = flags;
+ c->ic_vht_ch_freq1 = 0;
+ c->ic_vht_ch_freq2 = 0;
set_extchan(c);
+ set_vht_extchan(c);
return (0);
}
+/*
+ * XXX VHT-2GHz
+ */
static void
getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40)
{
@@ -1134,35 +1302,73 @@ getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40)
}
static void
-getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40)
+getflags_5ghz(const uint8_t bands[], uint32_t flags[], int ht40, int vht80)
{
int nmodes;
+ /*
+ * the addchan_list function seems to expect the flags array to
+ * be in channel width order, so the VHT bits are interspersed
+ * as appropriate to maintain said order.
+ *
+ * It also assumes HT40U is before HT40D.
+ */
nmodes = 0;
+
+ /* 20MHz */
if (isset(bands, IEEE80211_MODE_11A))
flags[nmodes++] = IEEE80211_CHAN_A;
if (isset(bands, IEEE80211_MODE_11NA))
flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20;
+ if (isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+ flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 |
+ IEEE80211_CHAN_VHT20;
+ }
+
+ /* 40MHz */
if (ht40) {
flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U;
+ }
+ if (ht40 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+ flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U
+ | IEEE80211_CHAN_VHT40U;
+ }
+ if (ht40) {
flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D;
}
+ if (ht40 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+ flags[nmodes++] = IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D
+ | IEEE80211_CHAN_VHT40D;
+ }
+
+ /* 80MHz */
+ if (vht80 && isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
+ flags[nmodes++] = IEEE80211_CHAN_A |
+ IEEE80211_CHAN_HT40U | IEEE80211_CHAN_VHT80;
+ flags[nmodes++] = IEEE80211_CHAN_A |
+ IEEE80211_CHAN_HT40D | IEEE80211_CHAN_VHT80;
+ }
+
+ /* XXX VHT80+80 */
+ /* XXX VHT160 */
flags[nmodes] = 0;
}
static void
-getflags(const uint8_t bands[], uint32_t flags[], int ht40)
+getflags(const uint8_t bands[], uint32_t flags[], int ht40, int vht80)
{
flags[0] = 0;
if (isset(bands, IEEE80211_MODE_11A) ||
- isset(bands, IEEE80211_MODE_11NA)) {
+ isset(bands, IEEE80211_MODE_11NA) ||
+ isset(bands, IEEE80211_MODE_VHT_5GHZ)) {
if (isset(bands, IEEE80211_MODE_11B) ||
isset(bands, IEEE80211_MODE_11G) ||
- isset(bands, IEEE80211_MODE_11NG))
+ isset(bands, IEEE80211_MODE_11NG) ||
+ isset(bands, IEEE80211_MODE_VHT_2GHZ))
return;
- getflags_5ghz(bands, flags, ht40);
+ getflags_5ghz(bands, flags, ht40, vht80);
} else
getflags_2ghz(bands, flags, ht40);
}
@@ -1170,6 +1376,7 @@ getflags(const uint8_t bands[], uint32_t flags[], int ht40)
/*
* Add one 20 MHz channel into specified channel list.
*/
+/* XXX VHT */
int
ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans,
int *nchans, uint8_t ieee, uint16_t freq, int8_t maxregpower,
@@ -1178,7 +1385,7 @@ ieee80211_add_channel(struct ieee80211_channel chans[], int maxchans,
uint32_t flags[IEEE80211_MODE_MAX];
int i, error;
- getflags(bands, flags, 0);
+ getflags(bands, flags, 0, 0);
KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
error = addchan(chans, maxchans, nchans, ieee, freq, maxregpower,
@@ -1212,6 +1419,7 @@ findchannel(struct ieee80211_channel chans[], int nchans, uint16_t freq,
/*
* Add 40 MHz channel pair into specified channel list.
*/
+/* XXX VHT */
int
ieee80211_add_channel_ht40(struct ieee80211_channel chans[], int maxchans,
int *nchans, uint8_t ieee, int8_t maxregpower, uint32_t flags)
@@ -1269,11 +1477,17 @@ ieee80211_get_channel_center_freq(const struct ieee80211_channel *c)
* For 80+80MHz channels this will be the centre of the primary
* 80MHz channel; the secondary 80MHz channel will be center_freq2().
*/
-
uint32_t
ieee80211_get_channel_center_freq1(const struct ieee80211_channel *c)
{
+ /*
+ * VHT - use the pre-calculated centre frequency
+ * of the given channel.
+ */
+ if (IEEE80211_IS_CHAN_VHT(c))
+ return (ieee80211_ieee2mhz(c->ic_vht_ch_freq1, c->ic_flags));
+
if (IEEE80211_IS_CHAN_HT40U(c)) {
return (c->ic_freq + 10);
}
@@ -1285,12 +1499,15 @@ ieee80211_get_channel_center_freq1(const struct ieee80211_channel *c)
}
/*
- * For now, no 80+80 support; this is zero.
+ * For now, no 80+80 support; it will likely always return 0.
*/
uint32_t
ieee80211_get_channel_center_freq2(const struct ieee80211_channel *c)
{
+ if (IEEE80211_IS_CHAN_VHT(c) && (c->ic_vht_ch_freq2 != 0))
+ return (ieee80211_ieee2mhz(c->ic_vht_ch_freq2, c->ic_flags));
+
return (0);
}
@@ -1304,16 +1521,70 @@ add_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans,
{
uint16_t freq;
int i, j, error;
+ int is_vht;
for (i = 0; i < nieee; i++) {
freq = ieee80211_ieee2mhz(ieee[i], flags[0]);
for (j = 0; flags[j] != 0; j++) {
+ /*
+ * Notes:
+ * + HT40 and VHT40 channels occur together, so
+ * we need to be careful that we actually allow that.
+ * + VHT80, VHT160 will coexist with HT40/VHT40, so
+ * make sure it's not skipped because of the overlap
+ * check used for (V)HT40.
+ */
+ is_vht = !! (flags[j] & IEEE80211_CHAN_VHT);
+
+ /*
+ * Test for VHT80.
+ * XXX This is all very broken right now.
+ * What we /should/ do is:
+ *
+ * + check that the frequency is in the list of
+ * allowed VHT80 ranges; and
+ * + the other 3 channels in the list are actually
+ * also available.
+ */
+ if (is_vht && flags[j] & IEEE80211_CHAN_VHT80)
+ if (! is_vht80_valid_freq(freq))
+ continue;
+
+ /*
+ * Test for (V)HT40.
+ *
+ * This is also a fall through from VHT80; as we only
+ * allow a VHT80 channel if the VHT40 combination is
+ * also valid. If the VHT40 form is not valid then
+ * we certainly can't do VHT80..
+ */
if (flags[j] & IEEE80211_CHAN_HT40D)
+ /*
+ * Can't have a "lower" channel if we are the
+ * first channel.
+ *
+ * Can't have a "lower" channel if it's below/
+ * within 20MHz of the first channel.
+ *
+ * Can't have a "lower" channel if the channel
+ * below it is not 20MHz away.
+ */
if (i == 0 || ieee[i] < ieee[0] + 4 ||
freq - 20 !=
ieee80211_ieee2mhz(ieee[i] - 4, flags[j]))
continue;
if (flags[j] & IEEE80211_CHAN_HT40U)
+ /*
+ * Can't have an "upper" channel if we are
+ * the last channel.
+ *
+ * Can't have an "upper" channel be above the
+ * last channel in the list.
+ *
+ * Can't have an "upper" channel if the next
+ * channel according to the math isn't 20MHz
+ * away. (Likely for channel 13/14.)
+ */
if (i == nieee - 1 ||
ieee[i] + 4 > ieee[nieee - 1] ||
freq + 20 !=
@@ -1342,6 +1613,7 @@ ieee80211_add_channel_list_2ghz(struct ieee80211_channel chans[], int maxchans,
{
uint32_t flags[IEEE80211_MODE_MAX];
+ /* XXX no VHT for now */
getflags_2ghz(bands, flags, ht40);
KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
@@ -1354,8 +1626,15 @@ ieee80211_add_channel_list_5ghz(struct ieee80211_channel chans[], int maxchans,
int ht40)
{
uint32_t flags[IEEE80211_MODE_MAX];
+ int vht80 = 0;
+
+ /*
+ * For now, assume VHT == VHT80 support as a minimum.
+ */
+ if (isset(bands, IEEE80211_MODE_VHT_5GHZ))
+ vht80 = 1;
- getflags_5ghz(bands, flags, ht40);
+ getflags_5ghz(bands, flags, ht40, vht80);
KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
return (add_chanlist(chans, maxchans, nchans, ieee, nieee, flags));
@@ -1494,6 +1773,8 @@ addmedia(struct ifmedia *media, int caps, int addsta, int mode, int mword)
[IEEE80211_MODE_QUARTER] = IFM_IEEE80211_11A, /* XXX */
[IEEE80211_MODE_11NA] = IFM_IEEE80211_11NA,
[IEEE80211_MODE_11NG] = IFM_IEEE80211_11NG,
+ [IEEE80211_MODE_VHT_2GHZ] = IFM_IEEE80211_VHT2G,
+ [IEEE80211_MODE_VHT_5GHZ] = IFM_IEEE80211_VHT5G,
};
u_int mopt;
@@ -1606,6 +1887,19 @@ ieee80211_media_setup(struct ieee80211com *ic,
if (rate > maxrate)
maxrate = rate;
}
+
+ /*
+ * Add VHT media.
+ */
+ for (; mode <= IEEE80211_MODE_VHT_5GHZ; mode++) {
+ if (isclr(ic->ic_modecaps, mode))
+ continue;
+ addmedia(media, caps, addsta, mode, IFM_AUTO);
+ addmedia(media, caps, addsta, mode, IFM_IEEE80211_VHT);
+
+ /* XXX TODO: VHT maxrate */
+ }
+
return maxrate;
}
@@ -1617,6 +1911,14 @@ ieee80211_get_suprates(struct ieee80211com *ic, const struct ieee80211_channel *
return &ic->ic_sup_rates[ieee80211_chan2mode(c)];
}
+/* XXX inline or eliminate? */
+const struct ieee80211_htrateset *
+ieee80211_get_suphtrates(struct ieee80211com *ic,
+ const struct ieee80211_channel *c)
+{
+ return &ic->ic_sup_htrates;
+}
+
void
ieee80211_announce(struct ieee80211com *ic)
{
@@ -1641,6 +1943,7 @@ ieee80211_announce(struct ieee80211com *ic)
printf("\n");
}
ieee80211_ht_announce(ic);
+ ieee80211_vht_announce(ic);
}
void
@@ -1885,7 +2188,11 @@ enum ieee80211_phymode
ieee80211_chan2mode(const struct ieee80211_channel *chan)
{
- if (IEEE80211_IS_CHAN_HTA(chan))
+ if (IEEE80211_IS_CHAN_VHT_2GHZ(chan))
+ return IEEE80211_MODE_VHT_2GHZ;
+ else if (IEEE80211_IS_CHAN_VHT_5GHZ(chan))
+ return IEEE80211_MODE_VHT_5GHZ;
+ else if (IEEE80211_IS_CHAN_HTA(chan))
return IEEE80211_MODE_11NA;
else if (IEEE80211_IS_CHAN_HTG(chan))
return IEEE80211_MODE_11NG;
diff --git a/freebsd/sys/net80211/ieee80211.h b/freebsd/sys/net80211/ieee80211.h
index aa2ddb09..9fef8c44 100644
--- a/freebsd/sys/net80211/ieee80211.h
+++ b/freebsd/sys/net80211/ieee80211.h
@@ -165,6 +165,12 @@ struct ieee80211_qosframe_addr4 {
#define IEEE80211_IS_MGMT(wh) \
(!! (((wh)->i_fc[0] & IEEE80211_FC0_TYPE_MASK) \
== IEEE80211_FC0_TYPE_MGT))
+#define IEEE80211_IS_CTL(wh) \
+ (!! (((wh)->i_fc[0] & IEEE80211_FC0_TYPE_MASK) \
+ == IEEE80211_FC0_TYPE_CTL))
+#define IEEE80211_IS_DATA(wh) \
+ (!! (((wh)->i_fc[0] & IEEE80211_FC0_TYPE_MASK) \
+ == IEEE80211_FC0_TYPE_DATA))
#define IEEE80211_FC0_QOSDATA \
(IEEE80211_FC0_TYPE_DATA|IEEE80211_FC0_SUBTYPE_QOS|IEEE80211_FC0_VERSION_0)
@@ -611,7 +617,7 @@ struct ieee80211_ie_htcap {
} __packed;
/* HT capability flags (ht_cap) */
-#define IEEE80211_HTCAP_LDPC 0x0001 /* LDPC supported */
+#define IEEE80211_HTCAP_LDPC 0x0001 /* LDPC rx supported */
#define IEEE80211_HTCAP_CHWIDTH40 0x0002 /* 20/40 supported */
#define IEEE80211_HTCAP_SMPS 0x000c /* SM Power Save mode */
#define IEEE80211_HTCAP_SMPS_OFF 0x000c /* disabled */
@@ -798,37 +804,73 @@ struct ieee80211_ie_vht_operation {
#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991 0x00000001
#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 0x00000002
#define IEEE80211_VHTCAP_MAX_MPDU_MASK 0x00000003
-#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ 0x00000004
-#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ 0x00000008
+#define IEEE80211_VHTCAP_MAX_MPDU_MASK_S 0
+
#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK 0x0000000C
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK_S 2
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_NONE 0
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160MHZ 1
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_160_80P80MHZ 2
+#define IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_RESERVED 3
+
#define IEEE80211_VHTCAP_RXLDPC 0x00000010
+#define IEEE80211_VHTCAP_RXLDPC_S 4
+
#define IEEE80211_VHTCAP_SHORT_GI_80 0x00000020
+#define IEEE80211_VHTCAP_SHORT_GI_80_S 5
+
#define IEEE80211_VHTCAP_SHORT_GI_160 0x00000040
+#define IEEE80211_VHTCAP_SHORT_GI_160_S 6
+
#define IEEE80211_VHTCAP_TXSTBC 0x00000080
+#define IEEE80211_VHTCAP_TXSTBC_S 7
+
#define IEEE80211_VHTCAP_RXSTBC_1 0x00000100
#define IEEE80211_VHTCAP_RXSTBC_2 0x00000200
#define IEEE80211_VHTCAP_RXSTBC_3 0x00000300
#define IEEE80211_VHTCAP_RXSTBC_4 0x00000400
#define IEEE80211_VHTCAP_RXSTBC_MASK 0x00000700
+#define IEEE80211_VHTCAP_RXSTBC_MASK_S 8
+
#define IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE 0x00000800
+#define IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE_S 11
+
#define IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE 0x00001000
+#define IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE_S 12
+
#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT 13
#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK \
(7 << IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT)
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK_S 13
+
#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT 16
#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK \
(7 << IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_SHIFT)
+#define IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK_S 16
+
#define IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE 0x00080000
+#define IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE_S 19
#define IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE 0x00100000
+#define IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE_S 20
#define IEEE80211_VHTCAP_VHT_TXOP_PS 0x00200000
+#define IEEE80211_VHTCAP_VHT_TXOP_PS_S 21
#define IEEE80211_VHTCAP_HTC_VHT 0x00400000
+#define IEEE80211_VHTCAP_HTC_VHT_S 22
+
#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT 23
#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK \
(7 << IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT)
+#define IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK_S 23
+
+#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK 0x0c000000
#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB 0x08000000
#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB 0x0c000000
+#define IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK_S 26
+
#define IEEE80211_VHTCAP_RX_ANTENNA_PATTERN 0x10000000
+#define IEEE80211_VHTCAP_RX_ANTENNA_PATTERN_S 28
#define IEEE80211_VHTCAP_TX_ANTENNA_PATTERN 0x20000000
+#define IEEE80211_VHTCAP_TX_ANTENNA_PATTERN_S 29
/*
* XXX TODO: add the rest of the bits
diff --git a/freebsd/sys/net80211/ieee80211_adhoc.c b/freebsd/sys/net80211/ieee80211_adhoc.c
index 834c84cb..15a037f6 100644
--- a/freebsd/sys/net80211/ieee80211_adhoc.c
+++ b/freebsd/sys/net80211/ieee80211_adhoc.c
@@ -824,10 +824,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/freebsd/sys/net80211/ieee80211_freebsd.c b/freebsd/sys/net80211/ieee80211_freebsd.c
index 8c90c2f3..61c0b81d 100644
--- a/freebsd/sys/net80211/ieee80211_freebsd.c
+++ b/freebsd/sys/net80211/ieee80211_freebsd.c
@@ -182,6 +182,26 @@ ieee80211_sysctl_radar(SYSCTL_HANDLER_ARGS)
return 0;
}
+/*
+ * For now, just restart everything.
+ *
+ * Later on, it'd be nice to have a separate VAP restart to
+ * full-device restart.
+ */
+static int
+ieee80211_sysctl_vap_restart(SYSCTL_HANDLER_ARGS)
+{
+ struct ieee80211vap *vap = arg1;
+ int t = 0, error;
+
+ error = sysctl_handle_int(oidp, &t, 0, req);
+ if (error || !req->newptr)
+ return error;
+
+ ieee80211_restart_all(vap->iv_ic);
+ return 0;
+}
+
void
ieee80211_sysctl_attach(struct ieee80211com *ic)
{
@@ -261,6 +281,12 @@ ieee80211_sysctl_vattach(struct ieee80211vap *vap)
&vap->iv_ampdu_mintraffic[WME_AC_VI], 0,
"VI traffic tx aggr threshold (pps)");
}
+
+ SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
+ "force_restart", CTLTYPE_INT | CTLFLAG_RW, vap, 0,
+ ieee80211_sysctl_vap_restart, "I",
+ "force a VAP restart");
+
if (vap->iv_caps & IEEE80211_C_DFS) {
SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(oid), OID_AUTO,
"radar", CTLTYPE_INT | CTLFLAG_RW, vap->iv_ic, 0,
diff --git a/freebsd/sys/net80211/ieee80211_freebsd.h b/freebsd/sys/net80211/ieee80211_freebsd.h
index 49549e7b..01b9a8f6 100644
--- a/freebsd/sys/net80211/ieee80211_freebsd.h
+++ b/freebsd/sys/net80211/ieee80211_freebsd.h
@@ -622,98 +622,9 @@ int ieee80211_add_xmit_params(struct mbuf *m,
int ieee80211_get_xmit_params(struct mbuf *m,
struct ieee80211_bpf_params *);
-/*
- * Note: this is fine for 3x3 (and 4x4) 11n HT40;
- * but getting EVM information for VHT80, VHT160
- * will involve more than 6 EVM pilots.
- */
-#define IEEE80211_MAX_CHAINS 4
-#define IEEE80211_MAX_EVM_PILOTS 6
-
-#define IEEE80211_R_NF 0x00000001 /* global NF value valid */
-#define IEEE80211_R_RSSI 0x00000002 /* global RSSI value valid */
-#define IEEE80211_R_C_CHAIN 0x00000004 /* RX chain count valid */
-#define IEEE80211_R_C_NF 0x00000008 /* per-chain NF value valid */
-#define IEEE80211_R_C_RSSI 0x00000010 /* per-chain RSSI value valid */
-#define IEEE80211_R_C_EVM 0x00000020 /* per-chain EVM valid */
-#define IEEE80211_R_C_HT40 0x00000040 /* RX'ed packet is 40mhz, pilots 4,5 valid */
-#define IEEE80211_R_FREQ 0x00000080 /* Freq value populated, MHz */
-#define IEEE80211_R_IEEE 0x00000100 /* IEEE value populated */
-#define IEEE80211_R_BAND 0x00000200 /* Frequency band populated */
-#define IEEE80211_R_TSF32 0x00004000 /* 32 bit TSF */
-#define IEEE80211_R_TSF64 0x00008000 /* 64 bit TSF */
-#define IEEE80211_R_TSF_START 0x00010000 /* TSF is sampled at start of frame */
-#define IEEE80211_R_TSF_END 0x00020000 /* TSF is sampled at end of frame */
-
-/* RX packet flags - describe the kind of frame */
-#define IEEE80211_RX_F_STBC 0x00000001
-#define IEEE80211_RX_F_LDPC 0x00000002
-#define IEEE80211_RX_F_AMSDU 0x00000004 /* This is the start of an decap AMSDU list */
-#define IEEE80211_RX_F_AMSDU_MORE 0x00000008 /* This is another decap AMSDU frame in the batch */
-#define IEEE80211_RX_F_AMPDU 0x00000010 /* This is the start of an decap AMPDU list */
-#define IEEE80211_RX_F_AMPDU_MORE 0x00000020 /* This is another decap AMPDU frame in the batch */
-#define IEEE80211_RX_F_FAIL_FCSCRC 0x00000040 /* Failed CRC/FCS */
-#define IEEE80211_RX_F_FAIL_MIC 0x00000080 /* Failed MIC check */
-#define IEEE80211_RX_F_DECRYPTED 0x00000100 /* Hardware decrypted */
-#define IEEE80211_RX_F_IV_STRIP 0x00000200 /* Decrypted; IV stripped */
-#define IEEE80211_RX_F_MMIC_STRIP 0x00000400 /* Decrypted; MMIC stripped */
-#define IEEE80211_RX_F_SHORTGI 0x00000800 /* This is a short-GI frame */
-#define IEEE80211_RX_F_CCK 0x00001000
-#define IEEE80211_RX_F_OFDM 0x00002000
-#define IEEE80211_RX_F_HT 0x00004000
-#define IEEE80211_RX_F_VHT 0x00008000
-
-/* Channel width */
-#define IEEE80211_RX_FW_20MHZ 1
-#define IEEE80211_RX_FW_40MHZ 2
-#define IEEE80211_RX_FW_80MHZ 3
-
-/* PHY type */
-#define IEEE80211_RX_FP_11B 1
-#define IEEE80211_RX_FP_11G 2
-#define IEEE80211_RX_FP_11A 3
-#define IEEE80211_RX_FP_11NA 4
-#define IEEE80211_RX_FP_11NG 5
-
-struct ieee80211_rx_stats {
- uint32_t r_flags; /* IEEE80211_R_* flags */
- uint32_t c_pktflags; /* IEEE80211_RX_F_* flags */
-
- uint64_t c_rx_tsf; /* 32 or 64 bit TSF */
-
- /* All DWORD aligned */
- int16_t c_nf_ctl[IEEE80211_MAX_CHAINS]; /* per-chain NF */
- int16_t c_nf_ext[IEEE80211_MAX_CHAINS]; /* per-chain NF */
- int16_t c_rssi_ctl[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
- int16_t c_rssi_ext[IEEE80211_MAX_CHAINS]; /* per-chain RSSI */
-
- /* 32 bits */
- uint8_t c_nf; /* global NF */
- uint8_t c_rssi; /* global RSSI */
- uint8_t c_chain; /* number of RX chains involved */
- uint8_t c_rate; /* legacy; 11n rate code; VHT MCS */
-
- /* 32 bits */
- uint16_t c_freq; /* Frequency, MHz */
- uint8_t c_ieee; /* Channel */
- uint8_t c_width; /* channel width, FW flags above */
-
- /* Force alignment to DWORD */
- union {
- uint8_t evm[IEEE80211_MAX_CHAINS][IEEE80211_MAX_EVM_PILOTS];
- /* per-chain, per-pilot EVM values */
- uint32_t __aln[8];
- } evm;
-
- /* 32 bits */
- uint8_t c_phytype; /* PHY type, FW flags above */
- uint8_t c_vhtnss; /* VHT - number of spatial streams */
- uint8_t c_pad2[2];
-};
+struct ieee80211_rx_params;
+struct ieee80211_rx_stats;
-struct ieee80211_rx_params {
- struct ieee80211_rx_stats params;
-};
int ieee80211_add_rx_params(struct mbuf *m,
const struct ieee80211_rx_stats *rxs);
int ieee80211_get_rx_params(struct mbuf *m,
diff --git a/freebsd/sys/net80211/ieee80211_hostap.c b/freebsd/sys/net80211/ieee80211_hostap.c
index 8905c53c..6009ead7 100644
--- a/freebsd/sys/net80211/ieee80211_hostap.c
+++ b/freebsd/sys/net80211/ieee80211_hostap.c
@@ -64,6 +64,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)
@@ -1747,6 +1748,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;
@@ -2044,6 +2046,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);
@@ -2063,6 +2066,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;
@@ -2094,6 +2103,18 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
return); /* XXX just NULL out? */
}
+ /* Validate VHT IEs */
+ if (vhtcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(vhtcap[1],
+ sizeof(struct ieee80211_ie_vhtcap) - 2,
+ return);
+ }
+ if (vhtinfo != NULL) {
+ IEEE80211_VERIFY_LENGTH(vhtinfo[1],
+ sizeof(struct ieee80211_ie_vht_operation) - 2,
+ return);
+ }
+
if ((vap->iv_flags & IEEE80211_F_WPA) &&
!wpa_assocreq(ni, &rsnparms, wh, wpa, rsn, capinfo))
return;
@@ -2137,10 +2158,24 @@ 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) &&
+ vhtcap != NULL &&
+ vhtinfo != NULL) {
+ /* 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 |
@@ -2155,6 +2190,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/freebsd/sys/net80211/ieee80211_ht.c b/freebsd/sys/net80211/ieee80211_ht.c
index beeddda4..28093ed1 100644
--- a/freebsd/sys/net80211/ieee80211_ht.c
+++ b/freebsd/sys/net80211/ieee80211_ht.c
@@ -300,6 +300,11 @@ ieee80211_ht_vattach(struct ieee80211vap *vap)
vap->iv_flags_ht |= IEEE80211_FHT_STBC_TX;
if (vap->iv_htcaps & IEEE80211_HTCAP_RXSTBC)
vap->iv_flags_ht |= IEEE80211_FHT_STBC_RX;
+
+ if (vap->iv_htcaps & IEEE80211_HTCAP_LDPC)
+ vap->iv_flags_ht |= IEEE80211_FHT_LDPC_RX;
+ if (vap->iv_htcaps & IEEE80211_HTC_TXLDPC)
+ vap->iv_flags_ht |= IEEE80211_FHT_LDPC_TX;
}
/* NB: disable default legacy WDS, too many issues right now */
if (vap->iv_flags_ext & IEEE80211_FEXT_WDSLEGACY)
@@ -418,19 +423,17 @@ ieee80211_ht_announce(struct ieee80211com *ic)
ht_announce(ic, IEEE80211_MODE_11NG);
}
-static struct ieee80211_htrateset htrateset;
-
-const struct ieee80211_htrateset *
-ieee80211_get_suphtrates(struct ieee80211com *ic,
- const struct ieee80211_channel *c)
+void
+ieee80211_init_suphtrates(struct ieee80211com *ic)
{
#define ADDRATE(x) do { \
- htrateset.rs_rates[htrateset.rs_nrates] = x; \
- htrateset.rs_nrates++; \
+ htrateset->rs_rates[htrateset->rs_nrates] = x; \
+ htrateset->rs_nrates++; \
} while (0)
+ struct ieee80211_htrateset *htrateset = &ic->ic_sup_htrates;
int i;
- memset(&htrateset, 0, sizeof(struct ieee80211_htrateset));
+ memset(htrateset, 0, sizeof(struct ieee80211_htrateset));
for (i = 0; i < ic->ic_txstream * 8; i++)
ADDRATE(i);
if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) &&
@@ -450,7 +453,6 @@ ieee80211_get_suphtrates(struct ieee80211com *ic,
ADDRATE(i);
}
}
- return &htrateset;
#undef ADDRATE
}
@@ -644,6 +646,40 @@ ampdu_dispatch(struct ieee80211_node *ni, struct mbuf *m)
(void) ieee80211_input(ni, m, 0, 0);
}
+static void
+ampdu_rx_moveup(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni,
+ int i, int winstart)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if (rap->rxa_qframes != 0) {
+ int n = rap->rxa_qframes, j;
+
+ if (winstart != -1) {
+ /*
+ * NB: in window-sliding mode, loop assumes i > 0
+ * and/or rxa_m[0] is NULL
+ */
+ KASSERT(rap->rxa_m[0] == NULL,
+ ("%s: BA window slot 0 occupied", __func__));
+ }
+ for (j = i+1; j < rap->rxa_wnd; j++) {
+ if (rap->rxa_m[j] != NULL) {
+ rap->rxa_m[j-i] = rap->rxa_m[j];
+ rap->rxa_m[j] = NULL;
+ if (--n == 0)
+ break;
+ }
+ }
+ KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d "
+ "BA win <%d:%d> winstart %d",
+ __func__, n, rap->rxa_qframes, i, rap->rxa_start,
+ IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
+ winstart));
+ vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
+ }
+}
+
/*
* Dispatch as many frames as possible from the re-order queue.
* Frames will always be "at the front"; we process all frames
@@ -674,19 +710,8 @@ ampdu_rx_dispatch(struct ieee80211_rx_ampdu *rap, struct ieee80211_node *ni)
* If frames remain, copy the mbuf pointers down so
* they correspond to the offsets in the new window.
*/
- if (rap->rxa_qframes != 0) {
- int n = rap->rxa_qframes, j;
- for (j = i+1; j < rap->rxa_wnd; j++) {
- if (rap->rxa_m[j] != NULL) {
- rap->rxa_m[j-i] = rap->rxa_m[j];
- rap->rxa_m[j] = NULL;
- if (--n == 0)
- break;
- }
- }
- KASSERT(n == 0, ("lost %d frames", n));
- vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
- }
+ ampdu_rx_moveup(rap, ni, i, -1);
+
/*
* Adjust the start of the BA window to
* reflect the frames just dispatched.
@@ -761,27 +786,8 @@ ampdu_rx_flush_upto(struct ieee80211_node *ni,
* If frames remain, copy the mbuf pointers down so
* they correspond to the offsets in the new window.
*/
- if (rap->rxa_qframes != 0) {
- int n = rap->rxa_qframes, j;
+ ampdu_rx_moveup(rap, ni, i, winstart);
- /* NB: this loop assumes i > 0 and/or rxa_m[0] is NULL */
- KASSERT(rap->rxa_m[0] == NULL,
- ("%s: BA window slot 0 occupied", __func__));
- for (j = i+1; j < rap->rxa_wnd; j++) {
- if (rap->rxa_m[j] != NULL) {
- rap->rxa_m[j-i] = rap->rxa_m[j];
- rap->rxa_m[j] = NULL;
- if (--n == 0)
- break;
- }
- }
- KASSERT(n == 0, ("%s: lost %d frames, qframes %d off %d "
- "BA win <%d:%d> winstart %d",
- __func__, n, rap->rxa_qframes, i, rap->rxa_start,
- IEEE80211_SEQ_ADD(rap->rxa_start, rap->rxa_wnd-1),
- winstart));
- vap->iv_stats.is_ampdu_rx_copy += rap->rxa_qframes;
- }
/*
* Move the start of the BA window; we use the
* sequence number of the last MSDU that was
@@ -824,6 +830,16 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m)
*/
return PROCESS;
}
+
+ /*
+ * 802.11-2012 9.3.2.10 - Duplicate detection and recovery.
+ *
+ * Multicast QoS data frames are checked against a different
+ * counter, not the per-TID counter.
+ */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ return PROCESS;
+
if (IEEE80211_IS_DSTODS(wh))
tid = ((struct ieee80211_qosframe_addr4 *)wh)->i_qos[0];
else
@@ -1492,52 +1508,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 */
+ }
+
+ /* 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;
}
- /* update node's tx channel width */
+ /* 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,30 +1668,155 @@ htcap_update_shortgi(struct ieee80211_node *ni)
}
/*
+ * Update LDPC state according to received htcap
+ * and local settings.
+ */
+static __inline void
+htcap_update_ldpc(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+
+ if ((ni->ni_htcap & IEEE80211_HTCAP_LDPC) &&
+ (vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX))
+ ni->ni_flags |= IEEE80211_NODE_LDPC;
+}
+
+/*
* Parse and update HT-related state extracted from
* the HT cap and info ie's.
+ *
+ * This is called from the STA management path and
+ * the ieee80211_node_join() path. It will take into
+ * account the IEs discovered during scanning and
+ * adjust things accordingly.
*/
-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)
htcap_update_mimo_ps(ni);
htcap_update_shortgi(ni);
+ htcap_update_ldpc(ni);
if (htinfoie[0] == IEEE80211_ELEMID_VENDOR)
htinfoie += 4;
htinfo = (const struct ieee80211_ie_htinfo *) htinfoie;
htinfo_parse(ni, htinfo);
+ /*
+ * Defer the node channel change; we need to now
+ * update VHT parameters before we do it.
+ */
+
+ if ((htinfo->hi_byte1 & IEEE80211_HTINFO_RIFSMODE_PERM) &&
+ (vap->iv_flags_ht & IEEE80211_FHT_RIFS))
+ ni->ni_flags |= IEEE80211_NODE_RIFS;
+ else
+ ni->ni_flags &= ~IEEE80211_NODE_RIFS;
+}
+
+static uint32_t
+ieee80211_vht_get_vhtflags(struct ieee80211_node *ni, uint32_t htflags)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ uint32_t vhtflags = 0;
+
+ vhtflags = 0;
+ if (ni->ni_flags & IEEE80211_NODE_VHT && vap->iv_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)) {
@@ -1619,14 +1825,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);
}
@@ -1634,17 +1842,32 @@ 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);
+ htcap_update_ldpc(ni);
+}
+
+/*
+ * Called once HT and VHT capabilities are parsed in hostap mode -
+ * this will adjust the channel configuration of the given node
+ * based on the configuration and capabilities.
+ */
+void
+ieee80211_ht_updatehtcap_final(struct ieee80211_node *ni)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+ int htflags;
+ int vhtflags;
/* NB: honor operating mode constraint */
/* XXX 40 MHz intolerant */
@@ -1657,7 +1880,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);
}
/*
@@ -2137,6 +2367,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 */
}
@@ -2231,6 +2462,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;
@@ -2826,7 +3061,9 @@ ieee80211_add_htcap_body(uint8_t *frm, struct ieee80211_node *ni)
if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0)
caps &= ~IEEE80211_HTCAP_RXSTBC;
- /* XXX TODO: adjust LDPC based on receive capabilities */
+ /* adjust LDPC based on receive capabilites */
+ if ((vap->iv_flags_ht & IEEE80211_FHT_LDPC_RX) == 0)
+ caps &= ~IEEE80211_HTCAP_LDPC;
ADDSHORT(frm, caps);
diff --git a/freebsd/sys/net80211/ieee80211_ht.h b/freebsd/sys/net80211/ieee80211_ht.h
index dfc7d1c3..5b818a28 100644
--- a/freebsd/sys/net80211/ieee80211_ht.h
+++ b/freebsd/sys/net80211/ieee80211_ht.h
@@ -177,8 +177,7 @@ struct ieee80211_mcs_rates {
uint16_t ht40_rate_400ns;
};
extern const struct ieee80211_mcs_rates ieee80211_htrates[];
-const struct ieee80211_htrateset *ieee80211_get_suphtrates(
- struct ieee80211com *, const struct ieee80211_channel *);
+void ieee80211_init_suphtrates(struct ieee80211com *);
struct ieee80211_node;
int ieee80211_setup_htrates(struct ieee80211_node *,
@@ -201,9 +200,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/freebsd/sys/net80211/ieee80211_input.c b/freebsd/sys/net80211/ieee80211_input.c
index 0e427f84..22d9a565 100644
--- a/freebsd/sys/net80211/ieee80211_input.c
+++ b/freebsd/sys/net80211/ieee80211_input.c
@@ -496,6 +496,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
@@ -510,6 +512,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
@@ -587,6 +591,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;
@@ -720,6 +730,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;
}
@@ -840,6 +863,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/freebsd/sys/net80211/ieee80211_input.h b/freebsd/sys/net80211/ieee80211_input.h
index 6fb0d707..0ae8dd08 100644
--- a/freebsd/sys/net80211/ieee80211_input.h
+++ b/freebsd/sys/net80211/ieee80211_input.h
@@ -149,6 +149,12 @@ ishtinfooui(const uint8_t *frm)
* (as the seqnum wraps), handle that special case so packets aren't
* incorrectly dropped - ie, if the next packet is sequence number 0
* but a retransmit since the initial packet didn't make it.
+ *
+ * XXX TODO: handle sequence number space wrapping with dropped frames;
+ * especially in high interference conditions under high traffic load
+ * The RX AMPDU reorder code also needs it.
+ *
+ * XXX TODO: update for 802.11-2012 9.3.2.10 Duplicate Detection and Recovery.
*/
static __inline int
ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh,
@@ -175,6 +181,13 @@ ieee80211_check_rxseq(struct ieee80211_node *ni, struct ieee80211_frame *wh,
if (! IEEE80211_HAS_SEQ(type, subtype))
return 1;
+ /*
+ * Always allow multicast frames for now - QoS (any TID)
+ * or not.
+ */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ return 1;
+
tid = ieee80211_gettid(wh);
/*
diff --git a/freebsd/sys/net80211/ieee80211_ioctl.c b/freebsd/sys/net80211/ieee80211_ioctl.c
index c0813a78..4b874574 100644
--- a/freebsd/sys/net80211/ieee80211_ioctl.c
+++ b/freebsd/sys/net80211/ieee80211_ioctl.c
@@ -1138,6 +1138,13 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd,
if (vap->iv_flags_ht & IEEE80211_FHT_STBC_RX)
ireq->i_val |= 2;
break;
+ case IEEE80211_IOC_LDPC:
+ ireq->i_val = 0;
+ if (vap->iv_flags_ht & IEEE80211_FHT_LDPC_TX)
+ ireq->i_val |= 1;
+ if (vap->iv_flags_ht & IEEE80211_FHT_LDPC_RX)
+ ireq->i_val |= 2;
+ break;
/* VHT */
case IEEE80211_IOC_VHTCONF:
@@ -2221,13 +2228,19 @@ checkrate(const struct ieee80211_rateset *rs, int rate)
}
static int
-checkmcs(int mcs)
+checkmcs(const struct ieee80211_htrateset *rs, int mcs)
{
+ int rate_val = IEEE80211_RV(mcs);
+ int i;
+
if (mcs == IEEE80211_FIXED_RATE_NONE)
return 1;
if ((mcs & IEEE80211_RATE_MCS) == 0) /* MCS always have 0x80 set */
return 0;
- return (mcs & 0x7f) <= 15; /* XXX could search ht rate set */
+ for (i = 0; i < rs->rs_nrates; i++)
+ if (IEEE80211_RV(rs->rs_rates[i]) == rate_val)
+ return 1;
+ return 0;
}
static int
@@ -2237,6 +2250,7 @@ ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_txparams_req parms; /* XXX stack use? */
struct ieee80211_txparam *src, *dst;
+ const struct ieee80211_htrateset *rs_ht;
const struct ieee80211_rateset *rs;
int error, mode, changed, is11n, nmodes;
@@ -2255,23 +2269,24 @@ ieee80211_ioctl_settxparams(struct ieee80211vap *vap,
src = &parms.params[mode];
dst = &vap->iv_txparms[mode];
rs = &ic->ic_sup_rates[mode]; /* NB: 11n maps to legacy */
+ rs_ht = &ic->ic_sup_htrates;
is11n = (mode == IEEE80211_MODE_11NA ||
mode == IEEE80211_MODE_11NG);
if (src->ucastrate != dst->ucastrate) {
if (!checkrate(rs, src->ucastrate) &&
- (!is11n || !checkmcs(src->ucastrate)))
+ (!is11n || !checkmcs(rs_ht, src->ucastrate)))
return EINVAL;
changed++;
}
if (src->mcastrate != dst->mcastrate) {
if (!checkrate(rs, src->mcastrate) &&
- (!is11n || !checkmcs(src->mcastrate)))
+ (!is11n || !checkmcs(rs_ht, src->mcastrate)))
return EINVAL;
changed++;
}
if (src->mgmtrate != dst->mgmtrate) {
if (!checkrate(rs, src->mgmtrate) &&
- (!is11n || !checkmcs(src->mgmtrate)))
+ (!is11n || !checkmcs(rs_ht, src->mgmtrate)))
return EINVAL;
changed++;
}
@@ -3374,6 +3389,31 @@ ieee80211_ioctl_set80211(struct ieee80211vap *vap, u_long cmd, struct ieee80211r
if (isvapht(vap))
error = ERESTART;
break;
+ case IEEE80211_IOC_LDPC:
+ /* Check if we can do LDPC TX/RX before changing the setting */
+ if ((ireq->i_val & 1) &&
+ (vap->iv_htcaps & IEEE80211_HTC_TXLDPC) == 0)
+ return EOPNOTSUPP;
+ if ((ireq->i_val & 2) &&
+ (vap->iv_htcaps & IEEE80211_HTCAP_LDPC) == 0)
+ return EOPNOTSUPP;
+
+ /* TX */
+ if (ireq->i_val & 1)
+ vap->iv_flags_ht |= IEEE80211_FHT_LDPC_TX;
+ else
+ vap->iv_flags_ht &= ~IEEE80211_FHT_LDPC_TX;
+
+ /* RX */
+ if (ireq->i_val & 2)
+ vap->iv_flags_ht |= IEEE80211_FHT_LDPC_RX;
+ else
+ vap->iv_flags_ht &= ~IEEE80211_FHT_LDPC_RX;
+
+ /* NB: reset only if we're operating on an 11n channel */
+ if (isvapht(vap))
+ error = ERESTART;
+ break;
/* VHT */
case IEEE80211_IOC_VHTCONF:
diff --git a/freebsd/sys/net80211/ieee80211_node.c b/freebsd/sys/net80211/ieee80211_node.c
index c9c6df96..b15a782f 100644
--- a/freebsd/sys/net80211/ieee80211_node.c
+++ b/freebsd/sys/net80211/ieee80211_node.c
@@ -55,6 +55,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>
@@ -350,7 +351,6 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan)
ni->ni_fhindex = 1;
}
if (vap->iv_opmode == IEEE80211_M_IBSS) {
- vap->iv_flags |= IEEE80211_F_SIBSS;
ni->ni_capinfo |= IEEE80211_CAPINFO_IBSS; /* XXX */
if (vap->iv_flags & IEEE80211_F_DESBSSID)
IEEE80211_ADDR_COPY(ni->ni_bssid, vap->iv_des_bssid);
@@ -414,7 +414,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);
}
@@ -710,9 +714,42 @@ gethtadjustflags(struct ieee80211com *ic)
}
/*
+ * 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)
@@ -720,6 +757,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);
@@ -745,10 +784,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);
@@ -851,6 +903,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) {
@@ -898,9 +951,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 */
}
@@ -928,10 +985,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 */
@@ -1104,8 +1194,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
@@ -1228,15 +1320,16 @@ node_getmimoinfo(const struct ieee80211_node *ni,
bzero(info, sizeof(*info));
- for (i = 0; i < ni->ni_mimo_chains; i++) {
+ for (i = 0; i < MIN(IEEE80211_MAX_CHAINS, ni->ni_mimo_chains); i++) {
+ /* Note: for now, just pri20 channel info */
avgrssi = ni->ni_mimo_rssi_ctl[i];
if (avgrssi == IEEE80211_RSSI_DUMMY_MARKER) {
- info->rssi[i] = 0;
+ info->ch[i].rssi[0] = 0;
} else {
rssi = IEEE80211_RSSI_GET(avgrssi);
- info->rssi[i] = rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
+ info->ch[i].rssi[0] = rssi < 0 ? 0 : rssi > 127 ? 127 : rssi;
}
- info->noise[i] = ni->ni_mimo_noise_ctl[i];
+ info->ch[i].noise[0] = ni->ni_mimo_noise_ctl[i];
}
/* XXX ext radios? */
@@ -1425,6 +1518,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)) {
/*
@@ -1433,6 +1527,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;
/*
@@ -1640,7 +1737,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]);
@@ -1672,11 +1769,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 */
@@ -1694,15 +1803,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)
@@ -2367,6 +2501,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,
@@ -2464,8 +2599,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
@@ -2596,6 +2735,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);
@@ -2605,6 +2746,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),
@@ -2612,6 +2756,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)" : "",
@@ -2776,6 +2921,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/freebsd/sys/net80211/ieee80211_node.h b/freebsd/sys/net80211/ieee80211_node.h
index 7ca24c18..26c05567 100644
--- a/freebsd/sys/net80211/ieee80211_node.h
+++ b/freebsd/sys/net80211/ieee80211_node.h
@@ -143,6 +143,7 @@ struct ieee80211_node {
#define IEEE80211_NODE_AMSDU_RX 0x040000 /* AMSDU rx enabled */
#define IEEE80211_NODE_AMSDU_TX 0x080000 /* AMSDU tx enabled */
#define IEEE80211_NODE_VHT 0x100000 /* VHT enabled */
+#define IEEE80211_NODE_LDPC 0x200000 /* LDPC enabled */
uint16_t ni_associd; /* association ID */
uint16_t ni_vlan; /* vlan tag */
uint16_t ni_txpower; /* current transmit power */
@@ -248,6 +249,11 @@ struct ieee80211_node {
struct ieee80211vap *ni_wdsvap; /* associated WDS vap */
void *ni_rctls; /* private ratectl state */
+
+ /* quiet time IE state for the given node */
+ uint32_t ni_quiet_ie_set; /* Quiet time IE was seen */
+ struct ieee80211_quiet_ie ni_quiet_ie; /* last seen quiet IE */
+
uint64_t ni_spare[3];
};
MALLOC_DECLARE(M_80211_NODE);
diff --git a/freebsd/sys/net80211/ieee80211_output.c b/freebsd/sys/net80211/ieee80211_output.c
index c9251796..bb42f945 100644
--- a/freebsd/sys/net80211/ieee80211_output.c
+++ b/freebsd/sys/net80211/ieee80211_output.c
@@ -60,6 +60,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>
@@ -123,9 +124,7 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m,
{
struct ieee80211com *ic = vap->iv_ic;
struct ifnet *ifp = vap->iv_ifp;
-#ifdef IEEE80211_SUPPORT_SUPERG
int mcast;
-#endif
if ((ni->ni_flags & IEEE80211_NODE_PWR_MGT) &&
(m->m_flags & M_PWR_SAV) == 0) {
@@ -165,9 +164,7 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m,
* interface it (might have been) received on.
*/
m->m_pkthdr.rcvif = (void *)ni;
-#ifdef IEEE80211_SUPPORT_SUPERG
mcast = (m->m_flags & (M_MCAST | M_BCAST)) ? 1: 0;
-#endif
BPF_MTAP(ifp, m); /* 802.3 tx */
@@ -182,10 +179,15 @@ ieee80211_vap_pkt_send_dest(struct ieee80211vap *vap, struct mbuf *m,
* The default ic_ampdu_enable routine handles staggering
* ADDBA requests in case the receiver NAK's us or we are
* otherwise unable to establish a BA stream.
+ *
+ * Don't treat group-addressed frames as candidates for aggregation;
+ * net80211 doesn't support 802.11aa-2012 and so group addressed
+ * frames will always have sequence numbers allocated from the NON_QOS
+ * TID.
*/
if ((ni->ni_flags & IEEE80211_NODE_AMPDU_TX) &&
(vap->iv_flags_ht & IEEE80211_FHT_AMPDU_TX)) {
- if ((m->m_flags & M_EAPOL) == 0) {
+ if ((m->m_flags & M_EAPOL) == 0 && (! mcast)) {
int tid = WME_AC_TO_TID(M_WME_GETAC(m));
struct ieee80211_tx_ampdu *tap = &ni->ni_tx_ampdu[tid];
@@ -766,13 +768,31 @@ 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))
+ if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap)) {
m->m_flags |= M_AMPDU_MPDU;
- else {
+ } else {
if (IEEE80211_HAS_SEQ(type & IEEE80211_FC0_TYPE_MASK,
type & IEEE80211_FC0_SUBTYPE_MASK))
- seqno = ni->ni_txseqs[tid]++;
+ /*
+ * 802.11-2012 9.3.2.10 - QoS multicast frames
+ * come out of a different seqno space.
+ */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1)) {
+ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
+ } else {
+ seqno = ni->ni_txseqs[tid]++;
+ }
else
seqno = 0;
@@ -1230,7 +1250,7 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
struct ieee80211_frame *wh;
struct ieee80211_key *key;
struct llc *llc;
- int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr;
+ int hdrsize, hdrspace, datalen, addqos, txfrag, is4addr, is_mcast;
ieee80211_seq seqno;
int meshhdrsize, meshae;
uint8_t *qos;
@@ -1238,6 +1258,8 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
IEEE80211_TX_LOCK_ASSERT(ic);
+ is_mcast = !! (m->m_flags & (M_MCAST | M_BCAST));
+
/*
* Copy existing Ethernet header to a safe place. The
* rest of the code assumes it's ok to strip it when
@@ -1282,11 +1304,19 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
* ap's require all data frames to be QoS-encapsulated
* once negotiated in which case we'll need to make this
* configurable.
- * NB: mesh data frames are QoS.
+ *
+ * Don't send multicast QoS frames.
+ * Technically multicast frames can be QoS if all stations in the
+ * BSS are also QoS.
+ *
+ * NB: mesh data frames are QoS, including multicast frames.
*/
- addqos = ((ni->ni_flags & (IEEE80211_NODE_QOS|IEEE80211_NODE_HT)) ||
+ addqos =
+ (((is_mcast == 0) && (ni->ni_flags &
+ (IEEE80211_NODE_QOS|IEEE80211_NODE_HT))) ||
(vap->iv_opmode == IEEE80211_M_MBSS)) &&
(m->m_flags & M_EAPOL) == 0;
+
if (addqos)
hdrsize = sizeof(struct ieee80211_qosframe);
else
@@ -1544,8 +1574,29 @@ 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) {
/*
+ * 802.11-2012 9.3.2.10 -
+ *
+ * If this is a multicast frame then we need
+ * to ensure that the sequence number comes from
+ * a separate seqno space and not the TID space.
+ *
+ * Otherwise multicast frames may actually cause
+ * holes in the TX blockack window space and
+ * upset various things.
+ */
+ if (IEEE80211_IS_MULTICAST(wh->i_addr1))
+ seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
+ else
+ seqno = ni->ni_txseqs[tid]++;
+
+ /*
* NB: don't assign a sequence # to potential
* aggregates; we expect this happens at the
* point the frame comes off any aggregation q
@@ -1563,6 +1614,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);
@@ -1577,12 +1633,20 @@ ieee80211_encap(struct ieee80211vap *vap, struct ieee80211_node *ni,
__func__);
}
+ /*
+ * Check if xmit fragmentation is required.
+ *
+ * If the hardware does fragmentation offload, then don't bother
+ * doing it here.
+ */
+ if (IEEE80211_CONF_FRAG_OFFLOAD(ic))
+ txfrag = 0;
+ else
+ txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold &&
+ !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
+ (vap->iv_caps & IEEE80211_C_TXFRAG) &&
+ (m->m_flags & (M_FF | M_AMPDU_MPDU)) == 0);
- /* check if xmit fragmentation is required */
- txfrag = (m->m_pkthdr.len > vap->iv_fragthreshold &&
- !IEEE80211_IS_MULTICAST(wh->i_addr1) &&
- (vap->iv_caps & IEEE80211_C_TXFRAG) &&
- (m->m_flags & (M_FF | M_AMPDU_MPDU)) == 0);
if (key != NULL) {
/*
* IEEE 802.1X: send EAPOL frames always in the clear.
@@ -1962,16 +2026,23 @@ ieee80211_add_supportedchannels(uint8_t *frm, struct ieee80211com *ic)
* Add an 11h Quiet time element to a frame.
*/
static uint8_t *
-ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap)
+ieee80211_add_quiet(uint8_t *frm, struct ieee80211vap *vap, int update)
{
struct ieee80211_quiet_ie *quiet = (struct ieee80211_quiet_ie *) frm;
quiet->quiet_ie = IEEE80211_ELEMID_QUIET;
quiet->len = 6;
- if (vap->iv_quiet_count_value == 1)
- vap->iv_quiet_count_value = vap->iv_quiet_count;
- else if (vap->iv_quiet_count_value > 1)
- vap->iv_quiet_count_value--;
+
+ /*
+ * Only update every beacon interval - otherwise probe responses
+ * would update the quiet count value.
+ */
+ if (update) {
+ if (vap->iv_quiet_count_value == 1)
+ vap->iv_quiet_count_value = vap->iv_quiet_count;
+ else if (vap->iv_quiet_count_value > 1)
+ vap->iv_quiet_count_value--;
+ }
if (vap->iv_quiet_count_value == 0) {
/* value 0 is reserved as per 802.11h standerd */
@@ -2113,6 +2184,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
*/
@@ -2121,7 +2193,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)
@@ -2161,6 +2234,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);
@@ -2370,6 +2458,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)
@@ -2387,6 +2476,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)
@@ -2451,6 +2541,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)
@@ -2494,6 +2592,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)
@@ -2509,6 +2609,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)
@@ -2547,6 +2649,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,
@@ -2629,6 +2735,8 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
* [tlv] RSN (optional)
* [tlv] HT capabilities
* [tlv] HT information
+ * [tlv] VHT capabilities
+ * [tlv] VHT information
* [tlv] WPA (optional)
* [tlv] WME (optional)
* [tlv] Vendor OUI HT capabilities (optional)
@@ -2659,6 +2767,8 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
+ sizeof(struct ieee80211_wme_param)
+ 4 + sizeof(struct ieee80211_ie_htcap)
+ 4 + sizeof(struct ieee80211_ie_htinfo)
+ + sizeof(struct ieee80211_ie_vhtcap)
+ + sizeof(struct ieee80211_ie_vht_operation)
#ifdef IEEE80211_SUPPORT_SUPERG
+ sizeof(struct ieee80211_ath_ie)
#endif
@@ -2720,7 +2830,7 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
(vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
if (vap->iv_quiet)
- frm = ieee80211_add_quiet(frm, vap);
+ frm = ieee80211_add_quiet(frm, vap, 0);
}
}
if (IEEE80211_IS_CHAN_ANYG(bss->ni_chan))
@@ -2738,6 +2848,11 @@ ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
frm = ieee80211_add_htcap(frm, bss);
frm = ieee80211_add_htinfo(frm, bss);
}
+ if (IEEE80211_IS_CHAN_VHT(bss->ni_chan) &&
+ legacy != IEEE80211_SEND_LEGACY_11B) {
+ frm = ieee80211_add_vhtcap(frm, bss);
+ frm = ieee80211_add_vhtinfo(frm, bss);
+ }
frm = ieee80211_add_wpa(frm, vap);
if (vap->iv_flags & IEEE80211_F_WME)
frm = ieee80211_add_wme_param(frm, &ic->ic_wme);
@@ -2948,6 +3063,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
@@ -2959,11 +3078,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
@@ -3036,15 +3178,23 @@ ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
} else
bo->bo_csa = frm;
+ bo->bo_quiet = NULL;
if (vap->iv_flags & IEEE80211_F_DOTH) {
- bo->bo_quiet = frm;
if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
- (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
- if (vap->iv_quiet)
- frm = ieee80211_add_quiet(frm,vap);
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS) &&
+ (vap->iv_quiet == 1)) {
+ /*
+ * We only insert the quiet IE offset if
+ * the quiet IE is enabled. Otherwise don't
+ * put it here or we'll just overwrite
+ * some other beacon contents.
+ */
+ if (vap->iv_quiet) {
+ bo->bo_quiet = frm;
+ frm = ieee80211_add_quiet(frm,vap, 0);
+ }
}
- } else
- bo->bo_quiet = frm;
+ }
if (IEEE80211_IS_CHAN_ANYG(ni->ni_chan)) {
bo->bo_erp = frm;
@@ -3057,6 +3207,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;
@@ -3067,6 +3227,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;
@@ -3084,6 +3245,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);
@@ -3111,7 +3274,18 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni)
uint8_t *frm;
/*
+ * Update the "We're putting the quiet IE in the beacon" state.
+ */
+ if (vap->iv_quiet == 1)
+ vap->iv_flags_ext |= IEEE80211_FEXT_QUIET_IE;
+ else if (vap->iv_quiet == 0)
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_QUIET_IE;
+
+ /*
* beacon frame format
+ *
+ * Note: This needs updating for 802.11-2012.
+ *
* [8] time stamp
* [2] beacon interval
* [2] cabability information
@@ -3128,6 +3302,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)
@@ -3159,6 +3335,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
@@ -3243,7 +3421,52 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast)
return 1; /* just assume length changed */
}
+ /*
+ * Handle the quiet time element being added and removed.
+ * Again, for now we just cheat and reconstruct the whole
+ * beacon - that way the gap is provided as appropriate.
+ *
+ * So, track whether we have already added the IE versus
+ * whether we want to be adding the IE.
+ */
+ if ((vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE) &&
+ (vap->iv_quiet == 0)) {
+ /*
+ * Quiet time beacon IE enabled, but it's disabled;
+ * recalc
+ */
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_QUIET_IE;
+ ieee80211_beacon_construct(m,
+ mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni);
+ /* XXX do WME aggressive mode processing? */
+ IEEE80211_UNLOCK(ic);
+ return 1; /* just assume length changed */
+ }
+
+ if (((vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE) == 0) &&
+ (vap->iv_quiet == 1)) {
+ /*
+ * Quiet time beacon IE disabled, but it's now enabled;
+ * recalc
+ */
+ vap->iv_flags_ext |= IEEE80211_FEXT_QUIET_IE;
+ ieee80211_beacon_construct(m,
+ mtod(m, uint8_t*) + sizeof(struct ieee80211_frame), ni);
+ /* XXX do WME aggressive mode processing? */
+ IEEE80211_UNLOCK(ic);
+ return 1; /* just assume length changed */
+ }
+
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);
@@ -3349,6 +3572,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
@@ -3359,6 +3586,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
@@ -3413,6 +3641,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);
@@ -3436,10 +3665,17 @@ ieee80211_beacon_update(struct ieee80211_node *ni, struct mbuf *m, int mcast)
vap->iv_csa_count++;
/* NB: don't clear IEEE80211_BEACON_CSA */
}
+
+ /*
+ * Only add the quiet time IE if we've enabled it
+ * as appropriate.
+ */
if (IEEE80211_IS_CHAN_DFS(ic->ic_bsschan) &&
- (vap->iv_flags_ext & IEEE80211_FEXT_DFS) ){
- if (vap->iv_quiet)
- ieee80211_add_quiet(bo->bo_quiet, vap);
+ (vap->iv_flags_ext & IEEE80211_FEXT_DFS)) {
+ if (vap->iv_quiet &&
+ (vap->iv_flags_ext & IEEE80211_FEXT_QUIET_IE)) {
+ ieee80211_add_quiet(bo->bo_quiet, vap, 1);
+ }
}
if (isset(bo->bo_flags, IEEE80211_BEACON_ERP)) {
/*
diff --git a/freebsd/sys/net80211/ieee80211_proto.c b/freebsd/sys/net80211/ieee80211_proto.c
index 99a8ac99..8389404b 100644
--- a/freebsd/sys/net80211/ieee80211_proto.c
+++ b/freebsd/sys/net80211/ieee80211_proto.c
@@ -243,7 +243,7 @@ static void update_mcast(void *, int);
static void update_promisc(void *, int);
static void update_channel(void *, int);
static void update_chw(void *, int);
-static void update_wme(void *, int);
+static void vap_update_wme(void *, int);
static void restart_vaps(void *, int);
static void ieee80211_newstate_cb(void *, int);
@@ -282,7 +282,6 @@ ieee80211_proto_attach(struct ieee80211com *ic)
TASK_INIT(&ic->ic_chan_task, 0, update_channel, ic);
TASK_INIT(&ic->ic_bmiss_task, 0, beacon_miss, ic);
TASK_INIT(&ic->ic_chw_task, 0, update_chw, ic);
- TASK_INIT(&ic->ic_wme_task, 0, update_wme, ic);
TASK_INIT(&ic->ic_restart_task, 0, restart_vaps, ic);
ic->ic_wme.wme_hipri_switch_hysteresis =
@@ -340,6 +339,7 @@ ieee80211_proto_vattach(struct ieee80211vap *vap)
callout_init(&vap->iv_mgtsend, 1);
TASK_INIT(&vap->iv_nstate_task, 0, ieee80211_newstate_cb, vap);
TASK_INIT(&vap->iv_swbmiss_task, 0, beacon_swmiss, vap);
+ TASK_INIT(&vap->iv_wme_task, 0, vap_update_wme, vap);
/*
* Install default tx rate handling: no fixed rate, lowest
* supported rate for mgmt and multicast frames. Default
@@ -844,6 +844,9 @@ setbasicrates(struct ieee80211_rateset *rs,
[IEEE80211_MODE_11NA] = { 3, { 12, 24, 48 } },
/* NB: mixed b/g */
[IEEE80211_MODE_11NG] = { 4, { 2, 4, 11, 22 } },
+ /* NB: mixed b/g */
+ [IEEE80211_MODE_VHT_2GHZ] = { 4, { 2, 4, 11, 22 } },
+ [IEEE80211_MODE_VHT_5GHZ] = { 3, { 12, 24, 48 } },
};
int i, j;
@@ -908,6 +911,8 @@ static const struct phyParamType phyParamForAC_BE[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_QUARTER]= { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11NA] = { 3, 4, 6, 0, 0 },
[IEEE80211_MODE_11NG] = { 3, 4, 6, 0, 0 },
+ [IEEE80211_MODE_VHT_2GHZ] = { 3, 4, 6, 0, 0 },
+ [IEEE80211_MODE_VHT_5GHZ] = { 3, 4, 6, 0, 0 },
};
static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 7, 4, 10, 0, 0 },
@@ -922,6 +927,8 @@ static const struct phyParamType phyParamForAC_BK[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_QUARTER]= { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11NA] = { 7, 4, 10, 0, 0 },
[IEEE80211_MODE_11NG] = { 7, 4, 10, 0, 0 },
+ [IEEE80211_MODE_VHT_2GHZ] = { 7, 4, 10, 0, 0 },
+ [IEEE80211_MODE_VHT_5GHZ] = { 7, 4, 10, 0, 0 },
};
static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 1, 3, 4, 94, 0 },
@@ -936,6 +943,8 @@ static const struct phyParamType phyParamForAC_VI[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_QUARTER]= { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_11NA] = { 1, 3, 4, 94, 0 },
[IEEE80211_MODE_11NG] = { 1, 3, 4, 94, 0 },
+ [IEEE80211_MODE_VHT_2GHZ] = { 1, 3, 4, 94, 0 },
+ [IEEE80211_MODE_VHT_5GHZ] = { 1, 3, 4, 94, 0 },
};
static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_AUTO] = { 1, 2, 3, 47, 0 },
@@ -950,6 +959,8 @@ static const struct phyParamType phyParamForAC_VO[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_QUARTER]= { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_11NA] = { 1, 2, 3, 47, 0 },
[IEEE80211_MODE_11NG] = { 1, 2, 3, 47, 0 },
+ [IEEE80211_MODE_VHT_2GHZ] = { 1, 2, 3, 47, 0 },
+ [IEEE80211_MODE_VHT_5GHZ] = { 1, 2, 3, 47, 0 },
};
static const struct phyParamType bssPhyParamForAC_BE[IEEE80211_MODE_MAX] = {
@@ -1125,6 +1136,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
[IEEE80211_MODE_QUARTER] = { 2, 4, 10, 64, 0 },
[IEEE80211_MODE_11NA] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/
[IEEE80211_MODE_11NG] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/
+ [IEEE80211_MODE_VHT_2GHZ] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/
+ [IEEE80211_MODE_VHT_5GHZ] = { 2, 4, 10, 64, 0 }, /* XXXcheck*/
};
struct ieee80211com *ic = vap->iv_ic;
struct ieee80211_wme_state *wme = &ic->ic_wme;
@@ -1245,6 +1258,8 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
[IEEE80211_MODE_QUARTER] = 3,
[IEEE80211_MODE_11NA] = 3,
[IEEE80211_MODE_11NG] = 3,
+ [IEEE80211_MODE_VHT_2GHZ] = 3,
+ [IEEE80211_MODE_VHT_5GHZ] = 3,
};
chanp = &wme->wme_chanParams.cap_wmeParams[WME_AC_BE];
bssp = &wme->wme_bssChanParams.cap_wmeParams[WME_AC_BE];
@@ -1272,7 +1287,7 @@ ieee80211_wme_updateparams_locked(struct ieee80211vap *vap)
}
/* schedule the deferred WME update */
- ieee80211_runtask(ic, &ic->ic_wme_task);
+ ieee80211_runtask(ic, &vap->iv_wme_task);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_WME,
"%s: WME params updated, cap_info 0x%x\n", __func__,
@@ -1337,15 +1352,25 @@ update_chw(void *arg, int npending)
ic->ic_update_chw(ic);
}
+/*
+ * Deferred WME update.
+ *
+ * In preparation for per-VAP WME configuration, call the VAP
+ * method if the VAP requires it. Otherwise, just call the
+ * older global method. There isn't a per-VAP WME configuration
+ * just yet so for now just use the global configuration.
+ */
static void
-update_wme(void *arg, int npending)
+vap_update_wme(void *arg, int npending)
{
- struct ieee80211com *ic = arg;
+ struct ieee80211vap *vap = arg;
+ struct ieee80211com *ic = vap->iv_ic;
- /*
- * XXX should we defer the WME configuration update until now?
- */
- ic->ic_wme.wme_update(ic);
+ if (vap->iv_wme_update != NULL)
+ vap->iv_wme_update(vap,
+ ic->ic_wme.wme_chanParams.cap_wmeParams);
+ else
+ ic->ic_wme.wme_update(ic);
}
static void
@@ -1372,7 +1397,6 @@ ieee80211_waitfor_parent(struct ieee80211com *ic)
ieee80211_draintask(ic, &ic->ic_chan_task);
ieee80211_draintask(ic, &ic->ic_bmiss_task);
ieee80211_draintask(ic, &ic->ic_chw_task);
- ieee80211_draintask(ic, &ic->ic_wme_task);
taskqueue_unblock(ic->ic_tq);
}
diff --git a/freebsd/sys/net80211/ieee80211_proto.h b/freebsd/sys/net80211/ieee80211_proto.h
index c4d5e7b0..784179fd 100644
--- a/freebsd/sys/net80211/ieee80211_proto.h
+++ b/freebsd/sys/net80211/ieee80211_proto.h
@@ -391,6 +391,8 @@ enum {
IEEE80211_BEACON_TDMA = 9, /* TDMA Info */
IEEE80211_BEACON_ATH = 10, /* ATH parameters */
IEEE80211_BEACON_MESHCONF = 11, /* Mesh Configuration */
+ IEEE80211_BEACON_QUIET = 12, /* Quiet time IE */
+ IEEE80211_BEACON_VHTINFO = 13, /* VHT information */
};
int ieee80211_beacon_update(struct ieee80211_node *,
struct mbuf *, int mcast);
diff --git a/freebsd/sys/net80211/ieee80211_scan_sta.c b/freebsd/sys/net80211/ieee80211_scan_sta.c
index faaaf8a3..cbef5cd8 100644
--- a/freebsd/sys/net80211/ieee80211_scan_sta.c
+++ b/freebsd/sys/net80211/ieee80211_scan_sta.c
@@ -57,6 +57,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_mesh.h>
#endif
#include <net80211/ieee80211_ratectl.h>
+#include <net80211/ieee80211_vht.h>
#include <net/bpf.h>
@@ -327,14 +328,33 @@ found:
}
} 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;
@@ -533,10 +553,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;
/*
@@ -821,6 +842,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)
@@ -1628,6 +1652,8 @@ notfound:
*/
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;
}
@@ -1659,6 +1685,8 @@ notfound:
*/
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 */
@@ -1778,7 +1806,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));
@@ -1810,8 +1838,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;
}
@@ -1883,10 +1913,14 @@ notfound:
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/freebsd/sys/net80211/ieee80211_sta.c b/freebsd/sys/net80211/ieee80211_sta.c
index 68441806..68bbb36b 100644
--- a/freebsd/sys/net80211/ieee80211_sta.c
+++ b/freebsd/sys/net80211/ieee80211_sta.c
@@ -66,6 +66,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)
@@ -1320,6 +1321,27 @@ startbgscan(struct ieee80211vap *vap)
ieee80211_time_after(ticks, ic->ic_lastdata + vap->iv_bgscanidle)));
}
+#ifdef notyet
+/*
+ * Compare two quiet IEs and return if they are equivalent.
+ *
+ * The tbttcount isnt checked - that's not part of the configuration.
+ */
+static int
+compare_quiet_ie(const struct ieee80211_quiet_ie *q1,
+ const struct ieee80211_quiet_ie *q2)
+{
+
+ if (q1->period != q2->period)
+ return (0);
+ if (le16dec(&q1->duration) != le16dec(&q2->duration))
+ return (0);
+ if (le16dec(&q1->offset) != le16dec(&q2->offset))
+ return (0);
+ return (1);
+}
+#endif
+
static void
sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype,
const struct ieee80211_rx_stats *rxs,
@@ -1332,8 +1354,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];
@@ -1432,12 +1455,43 @@ 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)
+
+ /*
+ * If we have a quiet time IE then report it up to
+ * the driver.
+ *
+ * Otherwise, inform the driver that the quiet time
+ * IE has disappeared - only do that once rather than
+ * spamming it each time.
+ */
+ if (scan.quiet) {
ic->ic_set_quiet(ni, scan.quiet);
+ ni->ni_quiet_ie_set = 1;
+ memcpy(&ni->ni_quiet_ie, scan.quiet,
+ sizeof(struct ieee80211_quiet_ie));
+ } else {
+ if (ni->ni_quiet_ie_set == 1)
+ ic->ic_set_quiet(ni, NULL);
+ ni->ni_quiet_ie_set = 0;
+ bzero(&ni->ni_quiet_ie,
+ sizeof(struct ieee80211_quiet_ie));
+ }
if (scan.tim != NULL) {
struct ieee80211_tim_ie *tim =
@@ -1662,6 +1716,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) {
@@ -1695,6 +1750,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;
}
@@ -1739,9 +1800,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);
}
diff --git a/freebsd/sys/net80211/ieee80211_superg.c b/freebsd/sys/net80211/ieee80211_superg.c
index 2ca3e61a..4a5f68d5 100644
--- a/freebsd/sys/net80211/ieee80211_superg.c
+++ b/freebsd/sys/net80211/ieee80211_superg.c
@@ -96,6 +96,15 @@ SYSCTL_PROC(_net_wlan, OID_AUTO, ffagemax, CTLTYPE_INT | CTLFLAG_RW,
&ieee80211_ffagemax, 0, ieee80211_sysctl_msecs_ticks, "I",
"max hold time for fast-frame staging (ms)");
+static void
+ff_age_all(void *arg, int npending)
+{
+ struct ieee80211com *ic = arg;
+
+ /* XXX cache timer value somewhere (racy) */
+ ieee80211_ff_age_all(ic, ieee80211_ffagemax + 1);
+}
+
void
ieee80211_superg_attach(struct ieee80211com *ic)
{
@@ -111,6 +120,7 @@ ieee80211_superg_attach(struct ieee80211com *ic)
__func__);
return;
}
+ TIMEOUT_TASK_INIT(ic->ic_tq, &sg->ff_qtimer, 0, ff_age_all, ic);
ic->ic_superg = sg;
/*
@@ -124,12 +134,16 @@ ieee80211_superg_attach(struct ieee80211com *ic)
void
ieee80211_superg_detach(struct ieee80211com *ic)
{
- IEEE80211_FF_LOCK_DESTROY(ic);
if (ic->ic_superg != NULL) {
+ struct timeout_task *qtask = &ic->ic_superg->ff_qtimer;
+
+ while (taskqueue_cancel_timeout(ic->ic_tq, qtask, NULL) != 0)
+ taskqueue_drain_timeout(ic->ic_tq, qtask);
IEEE80211_FREE(ic->ic_superg, M_80211_VAP);
ic->ic_superg = NULL;
}
+ IEEE80211_FF_LOCK_DESTROY(ic);
}
void
@@ -670,8 +684,12 @@ stageq_add(struct ieee80211com *ic, struct ieee80211_stageq *sq, struct mbuf *m)
if (sq->tail != NULL) {
sq->tail->m_nextpkt = m;
age -= M_AGE_GET(sq->head);
- } else
+ } else {
sq->head = m;
+
+ struct timeout_task *qtask = &ic->ic_superg->ff_qtimer;
+ taskqueue_enqueue_timeout(ic->ic_tq, qtask, age);
+ }
KASSERT(age >= 0, ("age %d", age));
M_AGE_SET(m, age);
m->m_nextpkt = NULL;
diff --git a/freebsd/sys/net80211/ieee80211_superg.h b/freebsd/sys/net80211/ieee80211_superg.h
index 2f8628c3..2c8a6a0b 100644
--- a/freebsd/sys/net80211/ieee80211_superg.h
+++ b/freebsd/sys/net80211/ieee80211_superg.h
@@ -66,6 +66,8 @@ struct ieee80211_stageq {
struct ieee80211_superg {
/* fast-frames staging q */
struct ieee80211_stageq ff_stageq[WME_NUM_AC];
+ /* flush queues automatically */
+ struct timeout_task ff_qtimer;
};
void ieee80211_superg_attach(struct ieee80211com *);
diff --git a/freebsd/sys/net80211/ieee80211_tdma.c b/freebsd/sys/net80211/ieee80211_tdma.c
index c14ccab5..45c71f20 100644
--- a/freebsd/sys/net80211/ieee80211_tdma.c
+++ b/freebsd/sys/net80211/ieee80211_tdma.c
@@ -178,6 +178,8 @@ ieee80211_tdma_vattach(struct ieee80211vap *vap)
settxparms(vap, IEEE80211_MODE_11NG, TDMA_TXRATE_11NG_DEFAULT);
settxparms(vap, IEEE80211_MODE_HALF, TDMA_TXRATE_HALF_DEFAULT);
settxparms(vap, IEEE80211_MODE_QUARTER, TDMA_TXRATE_QUARTER_DEFAULT);
+ settxparms(vap, IEEE80211_MODE_VHT_2GHZ, TDMA_TXRATE_11NG_DEFAULT);
+ settxparms(vap, IEEE80211_MODE_VHT_5GHZ, TDMA_TXRATE_11NA_DEFAULT);
setackpolicy(vap->iv_ic, 1); /* disable ACK's */
diff --git a/freebsd/sys/net80211/ieee80211_var.h b/freebsd/sys/net80211/ieee80211_var.h
index 1b73d392..1d806a92 100644
--- a/freebsd/sys/net80211/ieee80211_var.h
+++ b/freebsd/sys/net80211/ieee80211_var.h
@@ -93,7 +93,13 @@
* says that VHT is supported - and then this macro can be
* changed.
*/
-#define IEEE80211_CONF_VHT(ic) ((ic)->ic_vhtcaps != 0)
+#define IEEE80211_CONF_VHT(ic) \
+ ((ic)->ic_flags_ext & IEEE80211_FEXT_VHT)
+
+#define IEEE80211_CONF_SEQNO_OFFLOAD(ic) \
+ ((ic)->ic_flags_ext & IEEE80211_FEXT_SEQNO_OFFLOAD)
+#define IEEE80211_CONF_FRAG_OFFLOAD(ic) \
+ ((ic)->ic_flags_ext & IEEE80211_FEXT_FRAG_OFFLOAD)
/*
* 802.11 control state is split into a common portion that maps
@@ -143,7 +149,6 @@ struct ieee80211com {
struct task ic_chan_task; /* deferred channel change */
struct task ic_bmiss_task; /* deferred beacon miss hndlr */
struct task ic_chw_task; /* deferred HT CHW update */
- struct task ic_wme_task; /* deferred WME update */
struct task ic_restart_task; /* deferred device restart */
counter_u64_t ic_ierrors; /* input errors */
@@ -169,6 +174,7 @@ struct ieee80211com {
uint16_t ic_holdover; /* PM hold over duration */
uint16_t ic_txpowlimit; /* global tx power limit */
struct ieee80211_rateset ic_sup_rates[IEEE80211_MODE_MAX];
+ struct ieee80211_htrateset ic_sup_htrates;
/*
* Channel state:
@@ -550,6 +556,10 @@ struct ieee80211vap {
int (*iv_output)(struct ifnet *, struct mbuf *,
const struct sockaddr *, struct route *);
+ int (*iv_wme_update)(struct ieee80211vap *,
+ const struct wmeParams *wme_params);
+ struct task iv_wme_task; /* deferred VAP WME update */
+
uint64_t iv_spare[6];
};
MALLOC_DECLARE(M_80211_VAP);
@@ -566,8 +576,7 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_F_PRIVACY 0x00000010 /* CONF: privacy enabled */
#define IEEE80211_F_PUREG 0x00000020 /* CONF: 11g w/o 11b sta's */
#define IEEE80211_F_SCAN 0x00000080 /* STATUS: scanning */
-#define IEEE80211_F_ASCAN 0x00000100 /* STATUS: active scan */
-#define IEEE80211_F_SIBSS 0x00000200 /* STATUS: start IBSS */
+/* 0x00000300 reserved */
/* NB: this is intentionally setup to be IEEE80211_CAPINFO_SHORT_SLOTTIME */
#define IEEE80211_F_SHSLOT 0x00000400 /* STATUS: use short slot time*/
#define IEEE80211_F_PMGTON 0x00000800 /* CONF: Power mgmt enable */
@@ -575,8 +584,7 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_F_WME 0x00002000 /* CONF: enable WME use */
#define IEEE80211_F_BGSCAN 0x00004000 /* CONF: bg scan enabled (???)*/
#define IEEE80211_F_SWRETRY 0x00008000 /* CONF: sw tx retry enabled */
-#define IEEE80211_F_TXPOW_FIXED 0x00010000 /* TX Power: fixed rate */
-#define IEEE80211_F_IBSSON 0x00020000 /* CONF: IBSS creation enable */
+/* 0x00030000 reserved */
#define IEEE80211_F_SHPREAMBLE 0x00040000 /* STATUS: use short preamble */
#define IEEE80211_F_DATAPAD 0x00080000 /* CONF: do alignment pad */
#define IEEE80211_F_USEPROT 0x00100000 /* STATUS: protection enabled */
@@ -594,9 +602,9 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_F_DWDS 0x80000000 /* CONF: Dynamic WDS enabled */
#define IEEE80211_F_BITS \
- "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN\11ASCAN\12SIBSS" \
- "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY\21TXPOW_FIXED" \
- "\22IBSSON\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \
+ "\20\1TURBOP\2COMP\3FF\4BURST\5PRIVACY\6PUREG\10SCAN" \
+ "\13SHSLOT\14PMGTON\15DESBSSID\16WME\17BGSCAN\20SWRETRY" \
+ "\23SHPREAMBLE\24DATAPAD\25USEPROT\26USERBARKER\27CSAPENDING" \
"\30WPA1\31WPA2\32DROPUNENC\33COUNTERM\34HIDESSID\35NOBRIDG\36PCF" \
"\37DOTH\40DWDS"
@@ -629,14 +637,21 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_FEXT_PROBECHAN 0x00020000 /* CONF: probe passive channel*/
#define IEEE80211_FEXT_UNIQMAC 0x00040000 /* CONF: user or computed mac */
#define IEEE80211_FEXT_SCAN_OFFLOAD 0x00080000 /* CONF: scan is fully offloaded */
+#define IEEE80211_FEXT_SEQNO_OFFLOAD 0x00100000 /* CONF: driver does seqno insertion/allocation */
+#define IEEE80211_FEXT_FRAG_OFFLOAD 0x00200000 /* CONF: hardware does 802.11 fragmentation + assignment */
+#define IEEE80211_FEXT_VHT 0x00400000 /* CONF: VHT support */
+#define IEEE80211_FEXT_QUIET_IE 0x00800000 /* STATUS: quiet IE in a beacon has been added */
#define IEEE80211_FEXT_BITS \
"\20\2INACT\3SCANWAIT\4BGSCAN\5WPS\6TSN\7SCANREQ\10RESUME" \
"\0114ADDR\12NONEPR_PR\13SWBMISS\14DFS\15DOTD\16STATEWAIT\17REINIT" \
- "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC\24SCAN_OFFLOAD"
+ "\20BPF\21WDSLEGACY\22PROBECHAN\23UNIQMAC\24SCAN_OFFLOAD\25SEQNO_OFFLOAD" \
+ "\26VHT\27QUIET_IE"
/* ic_flags_ht/iv_flags_ht */
#define IEEE80211_FHT_NONHT_PR 0x00000001 /* STATUS: non-HT sta present */
+#define IEEE80211_FHT_LDPC_TX 0x00010000 /* CONF: LDPC tx enabled */
+#define IEEE80211_FHT_LDPC_RX 0x00020000 /* CONF: LDPC rx enabled */
#define IEEE80211_FHT_GF 0x00040000 /* CONF: Greenfield enabled */
#define IEEE80211_FHT_HT 0x00080000 /* CONF: HT supported */
#define IEEE80211_FHT_AMPDU_TX 0x00100000 /* CONF: A-MPDU tx supported */
@@ -681,6 +696,8 @@ int ieee80211_vap_attach(struct ieee80211vap *,
void ieee80211_vap_detach(struct ieee80211vap *);
const struct ieee80211_rateset *ieee80211_get_suprates(struct ieee80211com *ic,
const struct ieee80211_channel *);
+const struct ieee80211_htrateset *ieee80211_get_suphtrates(
+ struct ieee80211com *, const struct ieee80211_channel *);
void ieee80211_announce(struct ieee80211com *);
void ieee80211_announce_channels(struct ieee80211com *);
void ieee80211_drain(struct ieee80211com *);
diff --git a/freebsd/sys/net80211/ieee80211_vht.c b/freebsd/sys/net80211/ieee80211_vht.c
new file mode 100644
index 00000000..acd939d3
--- /dev/null
+++ b/freebsd/sys/net80211/ieee80211_vht.c
@@ -0,0 +1,855 @@
+#include <machine/rtems-bsd-kernel-space.h>
+
+/*-
+ * Copyright (c) 2017 Adrian Chadd <adrian@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11ac-2013 protocol support.
+ */
+
+#include <rtems/bsd/local/opt_inet.h>
+#include <rtems/bsd/local/opt_wlan.h>
+
+#include <rtems/bsd/sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/endian.h>
+
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_var.h>
+#include <net80211/ieee80211_action.h>
+#include <net80211/ieee80211_input.h>
+#include <net80211/ieee80211_vht.h>
+
+/* define here, used throughout file */
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+#define SM(_v, _f) (((_v) << _f##_S) & _f)
+
+#define ADDSHORT(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = (v) >> 8; \
+ frm += 2; \
+} while (0)
+#define ADDWORD(frm, v) do { \
+ frm[0] = (v) & 0xff; \
+ frm[1] = ((v) >> 8) & 0xff; \
+ frm[2] = ((v) >> 16) & 0xff; \
+ frm[3] = ((v) >> 24) & 0xff; \
+ frm += 4; \
+} while (0)
+
+/*
+ * Immediate TODO:
+ *
+ * + handle WLAN_ACTION_VHT_OPMODE_NOTIF and other VHT action frames
+ * + ensure vhtinfo/vhtcap parameters correctly use the negotiated
+ * capabilities and ratesets
+ * + group ID management operation
+ */
+
+/*
+ * XXX TODO: handle WLAN_ACTION_VHT_OPMODE_NOTIF
+ *
+ * Look at mac80211/vht.c:ieee80211_vht_handle_opmode() for further details.
+ */
+
+static int
+vht_recv_action_placeholder(struct ieee80211_node *ni,
+ const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+
+#ifdef IEEE80211_DEBUG
+ ieee80211_note(ni->ni_vap, "%s: called; fc=0x%.2x/0x%.2x",
+ __func__,
+ wh->i_fc[0],
+ wh->i_fc[1]);
+#endif
+ return (0);
+}
+
+static int
+vht_send_action_placeholder(struct ieee80211_node *ni,
+ int category, int action, void *arg0)
+{
+
+#ifdef IEEE80211_DEBUG
+ ieee80211_note(ni->ni_vap, "%s: called; category=%d, action=%d",
+ __func__,
+ category,
+ action);
+#endif
+ return (EINVAL);
+}
+
+static void
+ieee80211_vht_init(void)
+{
+
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_VHT,
+ WLAN_ACTION_VHT_COMPRESSED_BF, vht_recv_action_placeholder);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_VHT,
+ WLAN_ACTION_VHT_GROUPID_MGMT, vht_recv_action_placeholder);
+ ieee80211_recv_action_register(IEEE80211_ACTION_CAT_VHT,
+ WLAN_ACTION_VHT_OPMODE_NOTIF, vht_recv_action_placeholder);
+
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_VHT,
+ WLAN_ACTION_VHT_COMPRESSED_BF, vht_send_action_placeholder);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_VHT,
+ WLAN_ACTION_VHT_GROUPID_MGMT, vht_send_action_placeholder);
+ ieee80211_send_action_register(IEEE80211_ACTION_CAT_VHT,
+ WLAN_ACTION_VHT_OPMODE_NOTIF, vht_send_action_placeholder);
+}
+
+SYSINIT(wlan_vht, SI_SUB_DRIVERS, SI_ORDER_FIRST, ieee80211_vht_init, NULL);
+
+void
+ieee80211_vht_attach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_vht_detach(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_vht_vattach(struct ieee80211vap *vap)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ if (! IEEE80211_CONF_VHT(ic))
+ return;
+
+ vap->iv_vhtcaps = ic->ic_vhtcaps;
+ vap->iv_vhtextcaps = ic->ic_vhtextcaps;
+
+ /* XXX assume VHT80 support; should really check vhtcaps */
+ vap->iv_flags_vht =
+ IEEE80211_FVHT_VHT
+ | IEEE80211_FVHT_USEVHT40
+ | IEEE80211_FVHT_USEVHT80;
+ /* XXX TODO: enable VHT80+80, VHT160 capabilities */
+
+ memcpy(&vap->iv_vht_mcsinfo, &ic->ic_vht_mcsinfo,
+ sizeof(struct ieee80211_vht_mcs_info));
+}
+
+void
+ieee80211_vht_vdetach(struct ieee80211vap *vap)
+{
+}
+
+#if 0
+static void
+vht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode)
+{
+}
+#endif
+
+static int
+vht_mcs_to_num(int m)
+{
+
+ switch (m) {
+ case IEEE80211_VHT_MCS_SUPPORT_0_7:
+ return (7);
+ case IEEE80211_VHT_MCS_SUPPORT_0_8:
+ return (8);
+ case IEEE80211_VHT_MCS_SUPPORT_0_9:
+ return (9);
+ default:
+ return (0);
+ }
+}
+
+void
+ieee80211_vht_announce(struct ieee80211com *ic)
+{
+ int i, tx, rx;
+
+ if (! IEEE80211_CONF_VHT(ic))
+ return;
+
+ /* Channel width */
+ ic_printf(ic, "[VHT] Channel Widths: 20MHz, 40MHz, 80MHz");
+ if (MS(ic->ic_vhtcaps, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) == 2)
+ printf(" 80+80MHz");
+ if (MS(ic->ic_vhtcaps, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK) >= 1)
+ printf(" 160MHz");
+ printf("\n");
+
+ /* Features */
+ ic_printf(ic, "[VHT] Features: %b\n", ic->ic_vhtcaps,
+ IEEE80211_VHTCAP_BITS);
+
+ /* For now, just 5GHz VHT. Worry about 2GHz VHT later */
+ for (i = 0; i < 7; i++) {
+ /* Each stream is 2 bits */
+ tx = (ic->ic_vht_mcsinfo.tx_mcs_map >> (2*i)) & 0x3;
+ rx = (ic->ic_vht_mcsinfo.rx_mcs_map >> (2*i)) & 0x3;
+ if (tx == 3 && rx == 3)
+ continue;
+ ic_printf(ic, "[VHT] NSS %d: TX MCS 0..%d, RX MCS 0..%d\n",
+ i + 1,
+ vht_mcs_to_num(tx),
+ vht_mcs_to_num(rx));
+ }
+}
+
+void
+ieee80211_vht_node_init(struct ieee80211_node *ni)
+{
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
+ "%s: called", __func__);
+ ni->ni_flags |= IEEE80211_NODE_VHT;
+}
+
+void
+ieee80211_vht_node_cleanup(struct ieee80211_node *ni)
+{
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
+ "%s: called", __func__);
+ ni->ni_flags &= ~IEEE80211_NODE_VHT;
+ ni->ni_vhtcap = 0;
+ bzero(&ni->ni_vht_mcsinfo, sizeof(struct ieee80211_vht_mcs_info));
+}
+
+/*
+ * Parse an 802.11ac VHT operation IE.
+ */
+void
+ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie)
+{
+ /* vht operation */
+ ni->ni_vht_chanwidth = ie[2];
+ ni->ni_vht_chan1 = ie[3];
+ ni->ni_vht_chan2 = ie[4];
+ ni->ni_vht_basicmcs = le16dec(ie + 5);
+
+#if 0
+ printf("%s: chan1=%d, chan2=%d, chanwidth=%d, basicmcs=0x%04x\n",
+ __func__,
+ ni->ni_vht_chan1,
+ ni->ni_vht_chan2,
+ ni->ni_vht_chanwidth,
+ ni->ni_vht_basicmcs);
+#endif
+}
+
+/*
+ * Parse an 802.11ac VHT capability IE.
+ */
+void
+ieee80211_parse_vhtcap(struct ieee80211_node *ni, const uint8_t *ie)
+{
+
+ /* vht capability */
+ ni->ni_vhtcap = le32dec(ie + 2);
+
+ /* suppmcs */
+ ni->ni_vht_mcsinfo.rx_mcs_map = le16dec(ie + 6);
+ ni->ni_vht_mcsinfo.rx_highest = le16dec(ie + 8);
+ ni->ni_vht_mcsinfo.tx_mcs_map = le16dec(ie + 10);
+ ni->ni_vht_mcsinfo.tx_highest = le16dec(ie + 12);
+}
+
+int
+ieee80211_vht_updateparams(struct ieee80211_node *ni,
+ const uint8_t *vhtcap_ie,
+ const uint8_t *vhtop_ie)
+{
+
+ //printf("%s: called\n", __func__);
+
+ ieee80211_parse_vhtcap(ni, vhtcap_ie);
+ ieee80211_parse_vhtopmode(ni, vhtop_ie);
+ return (0);
+}
+
+void
+ieee80211_setup_vht_rates(struct ieee80211_node *ni,
+ const uint8_t *vhtcap_ie,
+ const uint8_t *vhtop_ie)
+{
+
+ //printf("%s: called\n", __func__);
+ /* XXX TODO */
+}
+
+void
+ieee80211_vht_timeout(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_vht_node_join(struct ieee80211_node *ni)
+{
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
+ "%s: called", __func__);
+}
+
+void
+ieee80211_vht_node_leave(struct ieee80211_node *ni)
+{
+
+ IEEE80211_NOTE(ni->ni_vap, IEEE80211_MSG_11N, ni,
+ "%s: called", __func__);
+}
+
+/*
+ * Calculate the VHTCAP IE for a given node.
+ *
+ * This includes calculating the capability intersection based on the
+ * current operating mode and intersection of the TX/RX MCS maps.
+ *
+ * The standard only makes it clear about MCS rate negotiation
+ * and MCS basic rates (which must be a subset of the general
+ * negotiated rates). It doesn't make it clear that the AP should
+ * figure out the minimum functional overlap with the STA and
+ * support that.
+ *
+ * Note: this is in host order, not in 802.11 endian order.
+ *
+ * TODO: ensure I re-read 9.7.11 Rate Selection for VHT STAs.
+ *
+ * TODO: investigate what we should negotiate for MU-MIMO beamforming
+ * options.
+ *
+ * opmode is '1' for "vhtcap as if I'm a STA", 0 otherwise.
+ */
+void
+ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni,
+ struct ieee80211_ie_vhtcap *vhtcap, int opmode)
+{
+ struct ieee80211vap *vap = ni->ni_vap;
+// struct ieee80211com *ic = vap->iv_ic;
+ uint32_t val, val1, val2;
+ uint32_t new_vhtcap;
+ int i;
+
+ vhtcap->ie = IEEE80211_ELEMID_VHT_CAP;
+ vhtcap->len = sizeof(struct ieee80211_ie_vhtcap) - 2;
+
+ /*
+ * Capabilities - it depends on whether we are a station
+ * or not.
+ */
+ new_vhtcap = 0;
+
+ /*
+ * Station - use our desired configuration based on
+ * local config, local device bits and the already-learnt
+ * vhtcap/vhtinfo IE in the node.
+ */
+
+ /* Limit MPDU size to the smaller of the two */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_MAX_MPDU_MASK);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_MAX_MPDU_MASK);
+ }
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_MAX_MPDU_MASK);
+
+ /* Limit supp channel config */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK);
+ }
+ if ((val2 == 2) &&
+ ((vap->iv_flags_vht & IEEE80211_FVHT_USEVHT80P80) == 0))
+ val2 = 1;
+ if ((val2 == 1) &&
+ ((vap->iv_flags_vht & IEEE80211_FVHT_USEVHT160) == 0))
+ val2 = 0;
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_MASK);
+
+ /* RX LDPC */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_RXLDPC);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_RXLDPC);
+ }
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_RXLDPC);
+
+ /* Short-GI 80 */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_SHORT_GI_80);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_SHORT_GI_80);
+ }
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SHORT_GI_80);
+
+ /* Short-GI 160 */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_SHORT_GI_160);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_SHORT_GI_160);
+ }
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SHORT_GI_160);
+
+ /*
+ * STBC is slightly more complicated.
+ *
+ * In non-STA mode, we just announce our capabilities and that
+ * is that.
+ *
+ * In STA mode, we should calculate our capabilities based on
+ * local capabilities /and/ what the remote says. So:
+ *
+ * + Only TX STBC if we support it and the remote supports RX STBC;
+ * + Only announce RX STBC if we support it and the remote supports
+ * TX STBC;
+ * + RX STBC should be the minimum of local and remote RX STBC;
+ */
+
+ /* TX STBC */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_TXSTBC);
+ if (opmode == 1) {
+ /* STA mode - enable it only if node RXSTBC is non-zero */
+ val2 = !! MS(ni->ni_vhtcap, IEEE80211_VHTCAP_RXSTBC_MASK);
+ }
+ val = MIN(val1, val2);
+ /* XXX For now, use the 11n config flag */
+ if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_TX) == 0)
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_TXSTBC);
+
+ /* RX STBC1..4 */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_RXSTBC_MASK);
+ if (opmode == 1) {
+ /* STA mode - enable it only if node TXSTBC is non-zero */
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_TXSTBC);
+ }
+ val = MIN(val1, val2);
+ /* XXX For now, use the 11n config flag */
+ if ((vap->iv_flags_ht & IEEE80211_FHT_STBC_RX) == 0)
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_RXSTBC_MASK);
+
+ /*
+ * Finally - if RXSTBC is 0, then don't enable TXSTBC.
+ * Strictly speaking a device can TXSTBC and not RXSTBC, but
+ * it would be silly.
+ */
+ if (val == 0)
+ new_vhtcap &= ~IEEE80211_VHTCAP_TXSTBC;
+
+ /*
+ * Some of these fields require other fields to exist.
+ * So before using it, the parent field needs to be checked
+ * otherwise the overridden value may be wrong.
+ *
+ * For example, if SU beamformee is set to 0, then BF STS
+ * needs to be 0.
+ */
+
+ /* SU Beamformer capable */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+ }
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+
+ /* SU Beamformee capable */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+ }
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+
+ /* Beamformee STS capability - only if SU beamformee capable */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK);
+ if (opmode == 1) {
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK);
+ }
+ val = MIN(val1, val2);
+ if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE) == 0)
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK);
+
+ /* Sounding dimensions - only if SU beamformer capable */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK);
+ val = MIN(val1, val2);
+ if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE) == 0)
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SOUNDING_DIMENSIONS_MASK);
+
+ /*
+ * MU Beamformer capable - only if SU BFF capable, MU BFF capable
+ * and STA (not AP)
+ */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_MU_BEAMFORMER_CAPABLE);
+ val = MIN(val1, val2);
+ if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE) == 0)
+ val = 0;
+ if (opmode != 1) /* Only enable for STA mode */
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMER_CAPABLE);
+
+ /*
+ * MU Beamformee capable - only if SU BFE capable, MU BFE capable
+ * and AP (not STA)
+ */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_MU_BEAMFORMEE_CAPABLE);
+ val = MIN(val1, val2);
+ if ((new_vhtcap & IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE) == 0)
+ val = 0;
+ if (opmode != 0) /* Only enable for AP mode */
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_SU_BEAMFORMEE_CAPABLE);
+
+ /* VHT TXOP PS */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_VHT_TXOP_PS);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_VHT_TXOP_PS);
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_VHT_TXOP_PS);
+
+ /* HTC_VHT */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_HTC_VHT);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_HTC_VHT);
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_HTC_VHT);
+
+ /* A-MPDU length max */
+ /* XXX TODO: we need a userland config knob for this */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+ val = MIN(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK);
+
+ /*
+ * Link adaptation is only valid if HTC-VHT capable is 1.
+ * Otherwise, always set it to 0.
+ */
+ val2 = val1 = MS(vap->iv_vhtcaps,
+ IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap,
+ IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK);
+ val = MIN(val1, val2);
+ if ((new_vhtcap & IEEE80211_VHTCAP_HTC_VHT) == 0)
+ val = 0;
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_VHT_LINK_ADAPTATION_VHT_MASK);
+
+ /*
+ * The following two options are 0 if the pattern may change, 1 if it
+ * does not change. So, downgrade to the higher value.
+ */
+
+ /* RX antenna pattern */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN);
+ val = MAX(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_RX_ANTENNA_PATTERN);
+
+ /* TX antenna pattern */
+ val2 = val1 = MS(vap->iv_vhtcaps, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN);
+ if (opmode == 1)
+ val2 = MS(ni->ni_vhtcap, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN);
+ val = MAX(val1, val2);
+ new_vhtcap |= SM(val, IEEE80211_VHTCAP_TX_ANTENNA_PATTERN);
+
+ /*
+ * MCS set - again, we announce what we want to use
+ * based on configuration, device capabilities and
+ * already-learnt vhtcap/vhtinfo IE information.
+ */
+
+ /* MCS set - start with whatever the device supports */
+ vhtcap->supp_mcs.rx_mcs_map = vap->iv_vht_mcsinfo.rx_mcs_map;
+ vhtcap->supp_mcs.rx_highest = 0;
+ vhtcap->supp_mcs.tx_mcs_map = vap->iv_vht_mcsinfo.tx_mcs_map;
+ vhtcap->supp_mcs.tx_highest = 0;
+
+ vhtcap->vht_cap_info = new_vhtcap;
+
+ /*
+ * Now, if we're a STA, mask off whatever the AP doesn't support.
+ * Ie, we continue to state we can receive whatever we can do,
+ * but we only announce that we will transmit rates that meet
+ * the AP requirement.
+ *
+ * Note: 0 - MCS0..7; 1 - MCS0..8; 2 - MCS0..9; 3 = not supported.
+ * We can't just use MIN() because '3' means "no", so special case it.
+ */
+ if (opmode) {
+ for (i = 0; i < 8; i++) {
+ val1 = (vhtcap->supp_mcs.tx_mcs_map >> (i*2)) & 0x3;
+ val2 = (ni->ni_vht_mcsinfo.tx_mcs_map >> (i*2)) & 0x3;
+ val = MIN(val1, val2);
+ if (val1 == 3 || val2 == 3)
+ val = 3;
+ vhtcap->supp_mcs.tx_mcs_map &= ~(0x3 << (i*2));
+ vhtcap->supp_mcs.tx_mcs_map |= (val << (i*2));
+ }
+ }
+}
+
+/*
+ * Add a VHTCAP field.
+ *
+ * If in station mode, we announce what we would like our
+ * desired configuration to be.
+ *
+ * Else, we announce our capabilities based on our current
+ * configuration.
+ */
+uint8_t *
+ieee80211_add_vhtcap(uint8_t *frm, struct ieee80211_node *ni)
+{
+ struct ieee80211_ie_vhtcap vhtcap;
+ int opmode;
+
+ opmode = 0;
+ if (ni->ni_vap->iv_opmode == IEEE80211_M_STA)
+ opmode = 1;
+
+ ieee80211_vht_get_vhtcap_ie(ni, &vhtcap, opmode);
+
+ memset(frm, '\0', sizeof(struct ieee80211_ie_vhtcap));
+
+ frm[0] = IEEE80211_ELEMID_VHT_CAP;
+ frm[1] = sizeof(struct ieee80211_ie_vhtcap) - 2;
+ frm += 2;
+
+ /* 32-bit VHT capability */
+ ADDWORD(frm, vhtcap.vht_cap_info);
+
+ /* suppmcs */
+ ADDSHORT(frm, vhtcap.supp_mcs.rx_mcs_map);
+ ADDSHORT(frm, vhtcap.supp_mcs.rx_highest);
+ ADDSHORT(frm, vhtcap.supp_mcs.tx_mcs_map);
+ ADDSHORT(frm, vhtcap.supp_mcs.tx_highest);
+
+ return (frm);
+}
+
+static uint8_t
+ieee80211_vht_get_chwidth_ie(struct ieee80211_channel *c)
+{
+
+ /*
+ * XXX TODO: look at the node configuration as
+ * well?
+ */
+
+ if (IEEE80211_IS_CHAN_VHT160(c)) {
+ return IEEE80211_VHT_CHANWIDTH_160MHZ;
+ }
+ if (IEEE80211_IS_CHAN_VHT80_80(c)) {
+ return IEEE80211_VHT_CHANWIDTH_80P80MHZ;
+ }
+ if (IEEE80211_IS_CHAN_VHT80(c)) {
+ return IEEE80211_VHT_CHANWIDTH_80MHZ;
+ }
+ if (IEEE80211_IS_CHAN_VHT40(c)) {
+ return IEEE80211_VHT_CHANWIDTH_USE_HT;
+ }
+ if (IEEE80211_IS_CHAN_VHT20(c)) {
+ return IEEE80211_VHT_CHANWIDTH_USE_HT;
+ }
+
+ /* We shouldn't get here */
+ printf("%s: called on a non-VHT channel (freq=%d, flags=0x%08x\n",
+ __func__,
+ (int) c->ic_freq,
+ c->ic_flags);
+ return IEEE80211_VHT_CHANWIDTH_USE_HT;
+}
+
+/*
+ * Note: this just uses the current channel information;
+ * it doesn't use the node info after parsing.
+ *
+ * XXX TODO: need to make the basic MCS set configurable.
+ * XXX TODO: read 802.11-2013 to determine what to set
+ * chwidth to when scanning. I have a feeling
+ * it isn't involved in scanning and we shouldn't
+ * be sending it; and I don't yet know what to set
+ * it to for IBSS or hostap where the peer may be
+ * a completely different channel width to us.
+ */
+uint8_t *
+ieee80211_add_vhtinfo(uint8_t *frm, struct ieee80211_node *ni)
+{
+ memset(frm, '\0', sizeof(struct ieee80211_ie_vht_operation));
+
+ frm[0] = IEEE80211_ELEMID_VHT_OPMODE;
+ frm[1] = sizeof(struct ieee80211_ie_vht_operation) - 2;
+ frm += 2;
+
+ /* 8-bit chanwidth */
+ *frm++ = ieee80211_vht_get_chwidth_ie(ni->ni_chan);
+
+ /* 8-bit freq1 */
+ *frm++ = ni->ni_chan->ic_vht_ch_freq1;
+
+ /* 8-bit freq2 */
+ *frm++ = ni->ni_chan->ic_vht_ch_freq2;
+
+ /* 16-bit basic MCS set - just MCS0..7 for NSS=1 for now */
+ ADDSHORT(frm, 0xfffc);
+
+ return (frm);
+}
+
+void
+ieee80211_vht_update_cap(struct ieee80211_node *ni, const uint8_t *vhtcap_ie,
+ const uint8_t *vhtop_ie)
+{
+
+ ieee80211_parse_vhtcap(ni, vhtcap_ie);
+ ieee80211_parse_vhtopmode(ni, vhtop_ie);
+}
+
+static struct ieee80211_channel *
+findvhtchan(struct ieee80211com *ic, struct ieee80211_channel *c, int vhtflags)
+{
+
+ return (ieee80211_find_channel(ic, c->ic_freq,
+ (c->ic_flags & ~IEEE80211_CHAN_VHT) | vhtflags));
+}
+
+/*
+ * Handle channel promotion to VHT, similar to ieee80211_ht_adjust_channel().
+ */
+struct ieee80211_channel *
+ieee80211_vht_adjust_channel(struct ieee80211com *ic,
+ struct ieee80211_channel *chan, int flags)
+{
+ struct ieee80211_channel *c;
+
+ /* First case - handle channel demotion - if VHT isn't set */
+ if ((flags & IEEE80211_FVHT_VHT) == 0) {
+#if 0
+ printf("%s: demoting channel %d/0x%08x\n", __func__,
+ chan->ic_ieee, chan->ic_flags);
+#endif
+ c = ieee80211_find_channel(ic, chan->ic_freq,
+ chan->ic_flags & ~IEEE80211_CHAN_VHT);
+ if (c == NULL)
+ c = chan;
+#if 0
+ printf("%s: .. to %d/0x%08x\n", __func__,
+ c->ic_ieee, c->ic_flags);
+#endif
+ return (c);
+ }
+
+ /*
+ * We can upgrade to VHT - attempt to do so
+ *
+ * Note: we don't clear the HT flags, these are the hints
+ * for HT40U/HT40D when selecting VHT40 or larger channels.
+ */
+ /* Start with VHT80 */
+ c = NULL;
+ if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT160))
+ c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT80);
+
+ if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT80P80))
+ c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT80_80);
+
+ if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT80))
+ c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT80);
+
+ if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT40))
+ c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT40U);
+ if ((c == NULL) && (flags & IEEE80211_FVHT_USEVHT40))
+ c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT40D);
+ /*
+ * If we get here, VHT20 is always possible because we checked
+ * for IEEE80211_FVHT_VHT above.
+ */
+ if (c == NULL)
+ c = findvhtchan(ic, chan, IEEE80211_CHAN_VHT20);
+
+ if (c != NULL)
+ chan = c;
+
+#if 0
+ printf("%s: selected %d/0x%08x\n", __func__, c->ic_ieee, c->ic_flags);
+#endif
+ return (chan);
+}
+
+/*
+ * Calculate the VHT operation IE for a given node.
+ *
+ * This includes calculating the suitable channel width/parameters
+ * and basic MCS set.
+ *
+ * TODO: ensure I read 9.7.11 Rate Selection for VHT STAs.
+ * TODO: ensure I read 10.39.7 - BSS Basic VHT-MCS and NSS set operation.
+ */
+void
+ieee80211_vht_get_vhtinfo_ie(struct ieee80211_node *ni,
+ struct ieee80211_ie_vht_operation *vhtop, int opmode)
+{
+ printf("%s: called; TODO!\n", __func__);
+}
diff --git a/freebsd/sys/net80211/ieee80211_vht.h b/freebsd/sys/net80211/ieee80211_vht.h
new file mode 100644
index 00000000..791762b1
--- /dev/null
+++ b/freebsd/sys/net80211/ieee80211_vht.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 2016 Adrian Chadd <adrian@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+#ifndef _NET80211_IEEE80211_VHT_H_
+#define _NET80211_IEEE80211_VHT_H_
+
+void ieee80211_vht_attach(struct ieee80211com *);
+void ieee80211_vht_detach(struct ieee80211com *);
+void ieee80211_vht_vattach(struct ieee80211vap *);
+void ieee80211_vht_vdetach(struct ieee80211vap *);
+
+void ieee80211_vht_announce(struct ieee80211com *);
+
+void ieee80211_vht_node_init(struct ieee80211_node *);
+void ieee80211_vht_node_cleanup(struct ieee80211_node *);
+
+void ieee80211_parse_vhtopmode(struct ieee80211_node *, const uint8_t *);
+void ieee80211_parse_vhtcap(struct ieee80211_node *, const uint8_t *);
+
+int ieee80211_vht_updateparams(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+void ieee80211_setup_vht_rates(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+
+void ieee80211_vht_timeout(struct ieee80211com *ic);
+
+void ieee80211_vht_node_join(struct ieee80211_node *ni);
+void ieee80211_vht_node_leave(struct ieee80211_node *ni);
+
+uint8_t * ieee80211_add_vhtcap(uint8_t *frm, struct ieee80211_node *);
+uint8_t * ieee80211_add_vhtinfo(uint8_t *frm, struct ieee80211_node *);
+
+void ieee80211_vht_update_cap(struct ieee80211_node *,
+ const uint8_t *, const uint8_t *);
+
+struct ieee80211_channel *
+ ieee80211_vht_adjust_channel(struct ieee80211com *,
+ struct ieee80211_channel *, int);
+
+void ieee80211_vht_get_vhtcap_ie(struct ieee80211_node *ni,
+ struct ieee80211_ie_vhtcap *, int);
+void ieee80211_vht_get_vhtinfo_ie(struct ieee80211_node *ni,
+ struct ieee80211_ie_vht_operation *, int);
+
+#endif /* _NET80211_IEEE80211_VHT_H_ */