Index: etc/regdomain.xml
===================================================================
--- etc/regdomain.xml (revision 311042)
+++ etc/regdomain.xml (working copy)
@@ -111,6 +111,47 @@
IEEE80211_CHAN_HT40
+
+
+
+
+ 17
+ IEEE80211_CHAN_HT20
+ IEEE80211_CHAN_VHT20
+
+
+
+ 17
+ IEEE80211_CHAN_HT40
+ IEEE80211_CHAN_VHT40
+
+
+
+ 17
+ IEEE80211_CHAN_HT40
+ IEEE80211_CHAN_VHT80
+
+
+
+ 17
+ IEEE80211_CHAN_HT20
+ IEEE80211_CHAN_VHT20
+
+
+
+ 17
+ IEEE80211_CHAN_HT40
+ IEEE80211_CHAN_VHT40
+
+
+
+ 17
+ IEEE80211_CHAN_HT40
+ IEEE80211_CHAN_VHT80
+
+
@@ -1735,6 +1776,21 @@
20 20
IEEE80211_CHAN_A
+
+ 5180 5240
+ 20 20
+ IEEE80211_CHAN_A
+
+
+ 5180 5240
+ 40 20
+ IEEE80211_CHAN_A
+
+
+ 5180 5240
+ 80 20
+ IEEE80211_CHAN_A
+
5180 5240
40 20
@@ -1825,6 +1881,21 @@
20 20
IEEE80211_CHAN_A
+
+ 5745 5805
+ 20 20
+ IEEE80211_CHAN_A
+
+
+ 5745 5805
+ 40 20
+ IEEE80211_CHAN_A
+
+
+ 5745 5805
+ 80 20
+ IEEE80211_CHAN_A
+
5745 5805
40 20
Index: sys/net80211/_ieee80211.h
===================================================================
--- sys/net80211/_ieee80211.h (revision 311042)
+++ sys/net80211/_ieee80211.h (working copy)
@@ -194,6 +194,7 @@
#define IEEE80211_CHAN_VHT80 0x08000000 /* VHT80 channel */
#define IEEE80211_CHAN_VHT80_80 0x10000000 /* VHT80+80 channel */
#define IEEE80211_CHAN_VHT160 0x20000000 /* VHT160 channel */
+/* XXX note: 0x80000000 is used in src/sbin/ifconfig/ifieee80211.c :( */
#define IEEE80211_CHAN_HT40 (IEEE80211_CHAN_HT40U | IEEE80211_CHAN_HT40D)
#define IEEE80211_CHAN_HT (IEEE80211_CHAN_HT20 | IEEE80211_CHAN_HT40)
@@ -206,8 +207,8 @@
#define IEEE80211_CHAN_BITS \
"\20\1PRIV0\2PRIV2\3PRIV3\4PRIV4\5TURBO\6CCK\7OFDM\0102GHZ\0115GHZ" \
"\12PASSIVE\13DYN\14GFSK\15GSM\16STURBO\17HALF\20QUARTER\21HT20" \
- "\22HT40U\23HT40D\24DFS\0254MSXMIT\26NOADHOC\27NOHOSTAP\03011D"
-/* XXX TODO: add VHT bits */
+ "\22HT40U\23HT40D\24DFS\0254MSXMIT\26NOADHOC\27NOHOSTAP\03011D" \
+ "\031VHT20\032VHT40U\033VHT40D\034VHT80\035VHT80_80\036VHT160"
/*
* Useful combinations of channel characteristics.
Index: sys/net80211/ieee80211.c
===================================================================
--- sys/net80211/ieee80211.c (revision 311042)
+++ sys/net80211/ieee80211.c (working copy)
@@ -54,6 +54,7 @@
#include
#endif
#include
+#include
#include
@@ -70,6 +71,8 @@
[IEEE80211_MODE_QUARTER] = "quarter",
[IEEE80211_MODE_11NA] = "11na",
[IEEE80211_MODE_11NG] = "11ng",
+ [IEEE80211_MODE_VHT_2GHZ] = "11ac2",
+ [IEEE80211_MODE_VHT_5GHZ] = "11ac",
};
/* map ieee80211_opmode to the corresponding capability bit */
const int ieee80211_opcap[IEEE80211_OPMODE_MAX] = {
@@ -90,6 +93,7 @@
static void ieee80211_syncflag_locked(struct ieee80211com *ic, int flag);
static void ieee80211_syncflag_ht_locked(struct ieee80211com *ic, int flag);
static void ieee80211_syncflag_ext_locked(struct ieee80211com *ic, int flag);
+static void ieee80211_syncflag_vht_locked(struct ieee80211com *ic, int flag);
static int ieee80211_media_setup(struct ieee80211com *ic,
struct ifmedia *media, int caps, int addsta,
ifm_change_cb_t media_change, ifm_stat_cb_t media_stat);
@@ -116,6 +120,8 @@
{ 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
@@ -147,10 +153,20 @@
*/
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 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;
@@ -180,6 +196,10 @@
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,
@@ -207,6 +227,8 @@
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_5GHZ, ieee80211_rateset_11a);
+ DEFAULTRATES(IEEE80211_MODE_VHT_2GHZ, ieee80211_rateset_11g);
/*
* Setup required information to fill the mcsset field, if driver did
@@ -334,6 +356,7 @@
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);
@@ -377,6 +400,7 @@
#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);
@@ -506,8 +530,15 @@
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);
@@ -592,6 +623,7 @@
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);
@@ -652,6 +684,7 @@
ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT);
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_VHT);
IEEE80211_UNLOCK(ic);
return 1;
@@ -699,6 +732,7 @@
ieee80211_syncflag_locked(ic, IEEE80211_F_BURST);
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_HT);
ieee80211_syncflag_ht_locked(ic, IEEE80211_FHT_USEHT40);
+ ieee80211_syncflag_vht_locked(ic, IEEE80211_FVHT_VHT);
/* NB: this handles the bpfdetach done below */
ieee80211_syncflag_ext_locked(ic, IEEE80211_FEXT_BPF);
if (vap->iv_ifflags & IFF_PROMISC)
@@ -715,6 +749,7 @@
#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);
@@ -853,6 +888,46 @@
}
/*
+ * Synchronize flags_vht bit state in the com structure
+ * according to the state of all vap's. This is used,
+ * for example, to handle state changes via ioctls.
+ */
+static void
+ieee80211_syncflag_vht_locked(struct ieee80211com *ic, int flag)
+{
+ struct ieee80211vap *vap;
+ int bit;
+
+ IEEE80211_LOCK_ASSERT(ic);
+
+ bit = 0;
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if (vap->iv_flags_vht & flag) {
+ bit = 1;
+ break;
+ }
+ if (bit)
+ ic->ic_flags_vht |= flag;
+ else
+ ic->ic_flags_vht &= ~flag;
+}
+
+void
+ieee80211_syncflag_vht(struct ieee80211vap *vap, int flag)
+{
+ struct ieee80211com *ic = vap->iv_ic;
+
+ IEEE80211_LOCK(ic);
+ if (flag < 0) {
+ flag = -flag;
+ vap->iv_flags_vht &= ~flag;
+ } else
+ vap->iv_flags_vht |= flag;
+ ieee80211_syncflag_vht_locked(ic, flag);
+ IEEE80211_UNLOCK(ic);
+}
+
+/*
* Synchronize flags_ext bit state in the com structure
* according to the state of all vap's. This is used,
* for example, to handle state changes via ioctls.
@@ -1019,7 +1094,111 @@
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);
+ printf("%s: %d, freq=%d, midpoint=%d, freq1=%d\n",
+ __func__, c->ic_ieee, c->ic_freq, midpoint,
+ c->ic_vht_ch_freq1);
+ 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);
+}
+
+/*
+ * XXX VHT
+ */
+static int
addchan(struct ieee80211_channel chans[], int maxchans, int *nchans,
uint8_t ieee, uint16_t freq, int8_t maxregpower, uint32_t flags)
{
@@ -1028,6 +1207,13 @@
if (*nchans >= maxchans)
return (ENOBUFS);
+ printf("%s: %d: ieee=%d, freq=%d, flags=0x%08x\n",
+ __func__,
+ *nchans,
+ ieee,
+ freq,
+ flags);
+
c = &chans[(*nchans)++];
c->ic_ieee = ieee;
c->ic_freq = freq != 0 ? freq : ieee80211_ieee2mhz(ieee, flags);
@@ -1034,7 +1220,12 @@
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;
+ /* XXX VHT */
set_extchan(c);
+ set_vht_extchan(c);
+ printf("%s: %d, freq=%d, freq1=%d\n", __func__, c->ic_ieee, c->ic_freq, c->ic_vht_ch_freq1);
return (0);
}
@@ -1050,14 +1241,27 @@
if (*nchans >= maxchans)
return (ENOBUFS);
+
+ printf("%s: %d: flags=0x%08x\n",
+ __func__,
+ *nchans,
+ flags);
+
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);
+ printf("%s: %d, freq=%d, freq1=%d\n", __func__, c->ic_ieee, c->ic_freq, c->ic_vht_ch_freq1);
return (0);
}
+/*
+ * XXX VHT-2GHz
+ */
static void
getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40)
{
@@ -1077,36 +1281,77 @@
flags[nmodes] = 0;
}
+/*
+ * XXX VHT-5GHz
+ */
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);
}
@@ -1122,7 +1367,7 @@
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,
@@ -1156,6 +1401,7 @@
/*
* 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)
@@ -1218,6 +1464,13 @@
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);
}
@@ -1229,12 +1482,15 @@
}
/*
- * 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))
+ return (c->ic_vht_ch_freq2);
+
return (0);
}
@@ -1242,6 +1498,7 @@
* Adds channels into specified channel list (ieee[] array must be sorted).
* Channels are already sorted.
*/
+/* XXX VHT? */
static int
add_chanlist(struct ieee80211_channel chans[], int maxchans, int *nchans,
const uint8_t ieee[], int nieee, uint32_t flags[])
@@ -1248,16 +1505,43 @@
{
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);
+
+ if (is_vht && flags[j] & IEEE80211_CHAN_VHT80)
+ if (! is_vht80_valid_freq(freq))
+ continue;
+
if (flags[j] & IEEE80211_CHAN_HT40D)
+ /*
+ * Can't have a "lower" channel if we are the
+ * first channel.
+ */
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 the upper channel be above the
+ * last channel in the list.
+ *
+ */
if (i == nieee - 1 ||
ieee[i] + 4 > ieee[nieee - 1] ||
freq + 20 !=
@@ -1286,6 +1570,7 @@
{
uint32_t flags[IEEE80211_MODE_MAX];
+ /* XXX no VHT80 for now */
getflags_2ghz(bands, flags, ht40);
KASSERT(flags[0] != 0, ("%s: no correct mode provided\n", __func__));
@@ -1298,8 +1583,15 @@
int ht40)
{
uint32_t flags[IEEE80211_MODE_MAX];
+ int vht80 = 0;
- getflags_5ghz(bands, flags, ht40);
+ /*
+ * For now, assume VHT == VHT80 support.
+ */
+ if (isset(bands, IEEE80211_MODE_VHT_5GHZ))
+ vht80 = 1;
+
+ 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));
@@ -1438,6 +1730,8 @@
[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;
@@ -1550,6 +1844,19 @@
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;
}
@@ -1585,6 +1892,7 @@
printf("\n");
}
ieee80211_ht_announce(ic);
+ ieee80211_vht_announce(ic);
}
void
Index: sys/net80211/ieee80211.h
===================================================================
--- sys/net80211/ieee80211.h (revision 311042)
+++ sys/net80211/ieee80211.h (working copy)
@@ -839,6 +839,11 @@
#define IEEE80211_VHTCAP_RX_ANTENNA_PATTERN 0x10000000
#define IEEE80211_VHTCAP_TX_ANTENNA_PATTERN 0x20000000
+#define IEEE80211_VHTCAP_BITS \
+ "\20\1MPDU7991\2MPDU11454\3CHAN160\4CHAN8080\5RXLDPC\6SHORTGI80" \
+ "\7SHORTGI160\10RXSTBC1\11RXSTBC2\12RXSTBC3\13RXSTBC4\14BFERCAP" \
+ "\15BFEECAP\27VHT"
+
/*
* VHT Transmit Power Envelope element - 802.11ac-2013 8.4.2.164
*
Index: sys/net80211/ieee80211_ht.c
===================================================================
--- sys/net80211/ieee80211_ht.c (revision 311042)
+++ sys/net80211/ieee80211_ht.c (working copy)
@@ -1497,6 +1497,10 @@
* 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).
+ *
+ * XXX TODO VHT: In 11ac mode, what should be done here?
+ * We don't want to fall back to HT only channels when
+ * operating in VHT mode.
*/
static int
htinfo_update_chw(struct ieee80211_node *ni, int htflags)
@@ -1506,6 +1510,12 @@
int chanflags;
int ret = 0;
+ if (ni->ni_chan->ic_flags & IEEE80211_CHAN_VHT) {
+ printf("%s: TODO: handle VHT with chanwidth change!\n",
+ __func__);
+ return (0);
+ }
+
chanflags = (ni->ni_chan->ic_flags &~ IEEE80211_CHAN_HT) | htflags;
if (chanflags != ni->ni_chan->ic_flags) {
/* XXX not right for ht40- */
@@ -2229,6 +2239,10 @@
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;
Index: sys/net80211/ieee80211_input.c
===================================================================
--- sys/net80211/ieee80211_input.c (revision 311042)
+++ sys/net80211/ieee80211_input.c (working copy)
@@ -629,6 +629,12 @@
}
}
break;
+ case IEEE80211_ELEMID_VHT_CAP:
+ scan->vhtcap = frm;
+ break;
+ case IEEE80211_ELEMID_VHT_OPMODE:
+ scan->vhtopmode = frm;
+ break;
default:
IEEE80211_DISCARD_IE(vap, IEEE80211_MSG_ELEMID,
wh, "unhandled",
@@ -718,6 +724,19 @@
sizeof(struct ieee80211_ie_htinfo)-2,
scan->htinfo = NULL);
}
+
+ /* Process VHT IEs */
+ if (scan->vhtcap != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->vhtcap[1],
+ sizeof(struct ieee80211_ie_vhtcap) - 2,
+ scan->vhtcap = NULL);
+ }
+ if (scan->vhtopmode != NULL) {
+ IEEE80211_VERIFY_LENGTH(scan->vhtopmode[1],
+ sizeof(struct ieee80211_ie_vht_operation) - 2,
+ scan->vhtopmode = NULL);
+ }
+
return scan->status;
}
@@ -838,6 +857,9 @@
}
break;
#endif
+ case IEEE80211_ACTION_CAT_VHT:
+ printf("%s: TODO: VHT handling!\n", __func__);
+ break;
}
return 0;
}
Index: sys/net80211/ieee80211_ioctl.c
===================================================================
--- sys/net80211/ieee80211_ioctl.c (revision 311042)
+++ sys/net80211/ieee80211_ioctl.c (working copy)
@@ -710,6 +710,7 @@
dc->dc_drivercaps = ic->ic_caps;
dc->dc_cryptocaps = ic->ic_cryptocaps;
dc->dc_htcaps = ic->ic_htcaps;
+ dc->dc_vhtcaps = ic->ic_vhtcaps;
ci = &dc->dc_chaninfo;
ic->ic_getradiocaps(ic, maxchans, &ci->ic_nchans, ci->ic_chans);
KASSERT(ci->ic_nchans <= maxchans,
@@ -1135,6 +1136,15 @@
if (vap->iv_flags_ht & IEEE80211_FHT_STBC_RX)
ireq->i_val |= 2;
break;
+
+ /* VHT */
+ case IEEE80211_IOC_VHTCONF:
+ if (vap->iv_flags_vht & IEEE80211_FVHT_VHT) {
+ ireq->i_val = 1;
+ } else
+ ireq->i_val = 0;
+ break;
+
default:
error = ieee80211_ioctl_getdefault(vap, ireq);
break;
@@ -1869,6 +1879,7 @@
/* NB: handled specially below */
[IEEE80211_MODE_11NA] = IEEE80211_CHAN_A,
[IEEE80211_MODE_11NG] = IEEE80211_CHAN_G,
+ [IEEE80211_MODE_VHT_5GHZ] = IEEE80211_CHAN_A,
};
u_int modeflags;
int i;
@@ -1893,11 +1904,27 @@
!find11gchannel(ic, i, c->ic_freq))
return c;
} else {
- /* must check HT specially */
+ /* must check VHT specifically */
+ if ((mode == IEEE80211_MODE_VHT_5GHZ ||
+ mode == IEEE80211_MODE_VHT_2GHZ) &&
+ !IEEE80211_IS_CHAN_VHT(c))
+ continue;
+
+ /*
+ * Must check HT specially - only match on HT,
+ * not HT+VHT channels
+ */
if ((mode == IEEE80211_MODE_11NA ||
mode == IEEE80211_MODE_11NG) &&
!IEEE80211_IS_CHAN_HT(c))
continue;
+
+ if ((mode == IEEE80211_MODE_11NA ||
+ mode == IEEE80211_MODE_11NG) &&
+ IEEE80211_IS_CHAN_VHT(c))
+ continue;
+
+ /* Check that the modeflags above match */
if ((c->ic_flags & modeflags) == modeflags)
return c;
}
@@ -2031,6 +2058,8 @@
* 11a channel returned,
* if 11ng is requested, find the ht version of any
* 11g channel returned,
+ * TODO: if 11ac is requested, find the 11ac version
+ * of any 11a/11na channel returned,
* otherwise we should be ok with what we've got.
*/
switch (vap->iv_des_mode) {
@@ -2067,6 +2096,15 @@
c = c2;
}
break;
+ /* XXX TODO: VHT_2GHZ */
+ case IEEE80211_MODE_VHT_5GHZ:
+ if (IEEE80211_IS_CHAN_A(c)) {
+ c2 = findchannel(ic, ireq->i_val,
+ IEEE80211_MODE_VHT_5GHZ);
+ if (c2 != NULL)
+ c = c2;
+ }
+ break;
default: /* NB: no static turboG */
break;
}
@@ -3321,6 +3359,16 @@
if (isvapht(vap))
error = ERESTART;
break;
+
+ /* VHT */
+ case IEEE80211_IOC_VHTCONF:
+ if (ireq->i_val & 1)
+ ieee80211_syncflag_vht(vap, IEEE80211_FVHT_VHT);
+ else
+ ieee80211_syncflag_vht(vap, -IEEE80211_FVHT_VHT);
+ error = ENETRESET;
+ break;
+
default:
error = ieee80211_ioctl_setdefault(vap, ireq);
break;
Index: sys/net80211/ieee80211_ioctl.h
===================================================================
--- sys/net80211/ieee80211_ioctl.h (revision 311042)
+++ sys/net80211/ieee80211_ioctl.h (working copy)
@@ -556,6 +556,7 @@
uint32_t dc_drivercaps; /* general driver caps */
uint32_t dc_cryptocaps; /* hardware crypto support */
uint32_t dc_htcaps; /* HT/802.11n support */
+ uint32_t dc_vhtcaps; /* VHT/802.11ac capabilities */
struct ieee80211req_chaninfo dc_chaninfo;
};
#define IEEE80211_DEVCAPS_SIZE(_nchan) \
@@ -704,6 +705,9 @@
#define IEEE80211_IOC_STBC 113 /* STBC Tx/RX (on, off) */
#define IEEE80211_IOC_LDPC 114 /* LDPC Tx/RX (on, off) */
+/* VHT */
+#define IEEE80211_IOC_VHTCONF 130 /* VHT config (off, on (TBD: widths) */
+
#define IEEE80211_IOC_MESH_ID 170 /* mesh identifier */
#define IEEE80211_IOC_MESH_AP 171 /* accepting peerings */
#define IEEE80211_IOC_MESH_FWRD 172 /* forward frames */
Index: sys/net80211/ieee80211_node.c
===================================================================
--- sys/net80211/ieee80211_node.c (revision 311042)
+++ sys/net80211/ieee80211_node.c (working copy)
@@ -53,6 +53,7 @@
#include
#include
#include
+#include
#include
@@ -412,9 +413,13 @@
/* 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);
}
(void) ieee80211_sta_join1(ieee80211_ref_node(ni));
@@ -717,6 +722,7 @@
{
struct ieee80211_channel *c;
+ /* XXX VHT? */
c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan, gethtadjustflags(ic));
if (c != ic->ic_curchan) {
ic->ic_curchan = c;
@@ -743,6 +749,7 @@
* 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);
@@ -896,9 +903,13 @@
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 */
}
@@ -930,6 +941,22 @@
IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
ieee80211_setup_basic_htrates(ni, ni->ni_ies.htinfo_ie);
}
+
+ /*
+ * Setup VHT state for this node if it's available.
+ * Same as the above.
+ */
+ if (ni->ni_ies.vhtopmode_ie != NULL &&
+ ni->ni_ies.vhtcap_ie != NULL &&
+ vap->iv_flags_vht & IEEE80211_FVHT_VHT) {
+ 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);
+ }
+
/* XXX else check for ath FF? */
/* XXX QoS? Difficult given that WME config is specific to a master */
@@ -1102,8 +1129,10 @@
"power save mode off, %u sta's in ps mode", vap->iv_ps_sta);
}
/*
- * Cleanup any HT-related state.
+ * Cleanup any VHT and HT-related state.
*/
+ if (ni->ni_flags & IEEE80211_NODE_VHT)
+ ieee80211_vht_node_cleanup(ni);
if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_ht_node_cleanup(ni);
#ifdef IEEE80211_SUPPORT_SUPERG
@@ -1423,6 +1452,7 @@
if (vap->iv_flags & IEEE80211_F_FF)
ni->ni_flags |= IEEE80211_NODE_FF;
#endif
+ /* XXX VHT */
if ((ic->ic_htcaps & IEEE80211_HTC_HT) &&
(vap->iv_flags_ht & IEEE80211_FHT_HT)) {
/*
@@ -1431,6 +1461,9 @@
* ni_chan will be adjusted to an HT channel.
*/
ieee80211_ht_wds_init(ni);
+ if (vap->iv_flags_vht & IEEE80211_FVHT_VHT) {
+ printf("%s: TODO: vht_wds_init\n", __func__);
+ }
} else {
struct ieee80211_channel *c = ni->ni_chan;
/*
@@ -1638,7 +1671,7 @@
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]);
@@ -1675,6 +1708,13 @@
(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 */
@@ -1697,6 +1737,17 @@
IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
ieee80211_setup_basic_htrates(ni,
ni->ni_ies.htinfo_ie);
+
+ if (do_vht_setup) {
+ 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);
+ }
+
ieee80211_node_setuptxparms(ni);
ieee80211_ratectl_node_init(ni);
@@ -2365,6 +2416,7 @@
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,
@@ -2594,6 +2646,8 @@
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);
@@ -2774,6 +2828,8 @@
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) &&
Index: sys/net80211/ieee80211_node.h
===================================================================
--- sys/net80211/ieee80211_node.h (revision 311042)
+++ sys/net80211/ieee80211_node.h (working copy)
@@ -142,6 +142,7 @@
#define IEEE80211_NODE_ASSOCID 0x020000 /* xmit requires associd */
#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 */
uint16_t ni_associd; /* association ID */
uint16_t ni_vlan; /* vlan tag */
uint16_t ni_txpower; /* current transmit power */
Index: sys/net80211/ieee80211_output.c
===================================================================
--- sys/net80211/ieee80211_output.c (revision 311042)
+++ sys/net80211/ieee80211_output.c (working copy)
@@ -764,6 +764,16 @@
}
*(uint16_t *)&wh->i_dur[0] = 0;
+ /*
+ * XXX TODO: this is what the TX lock is for.
+ * Here we're incrementing sequence numbers, and they
+ * need to be in lock-step with what the driver is doing
+ * both in TX ordering and crypto encap (IV increment.)
+ *
+ * If the driver does seqno itself, then we can skip
+ * assigning sequence numbers here, and we can avoid
+ * requiring the TX lock.
+ */
tap = &ni->ni_tx_ampdu[tid];
if (tid != IEEE80211_NONQOS_TID && IEEE80211_AMPDU_RUNNING(tap))
m->m_flags |= M_AMPDU_MPDU;
@@ -1542,6 +1552,11 @@
if (is_amsdu)
qos[0] |= IEEE80211_QOS_AMSDU;
+ /*
+ * XXX TODO TX lock is needed for atomic updates of sequence
+ * numbers. If the driver does it, then don't do it here;
+ * and we don't need the TX lock held.
+ */
if ((m->m_flags & M_AMPDU_MPDU) == 0) {
/*
* NB: don't assign a sequence # to potential
@@ -1561,6 +1576,11 @@
M_SEQNO_SET(m, seqno);
}
} else {
+ /*
+ * XXX TODO TX lock is needed for atomic updates of sequence
+ * numbers. If the driver does it, then don't do it here;
+ * and we don't need the TX lock held.
+ */
seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
*(uint16_t *)wh->i_seq =
htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
@@ -2065,6 +2085,7 @@
* Send a probe request frame with the specified ssid
* and any optional information element data.
*/
+/* XXX VHT? */
int
ieee80211_send_probereq(struct ieee80211_node *ni,
const uint8_t sa[IEEE80211_ADDR_LEN],
@@ -2357,6 +2378,7 @@
case IEEE80211_FC0_SUBTYPE_ASSOC_REQ:
case IEEE80211_FC0_SUBTYPE_REASSOC_REQ:
+ /* XXX VHT? */
/*
* asreq frame format
* [2] capability information
@@ -2483,6 +2505,7 @@
case IEEE80211_FC0_SUBTYPE_ASSOC_RESP:
case IEEE80211_FC0_SUBTYPE_REASSOC_RESP:
+ /* XXX VHT? */
/*
* asresp frame format
* [2] capability information
@@ -2600,6 +2623,7 @@
* Space is left to prepend and 802.11 header at the
* front but it's left to the caller to fill in.
*/
+/* XXX VHT? */
struct mbuf *
ieee80211_alloc_proberesp(struct ieee80211_node *bss, int legacy)
{
@@ -2934,6 +2958,7 @@
}
}
+/* XXX VHT? */
static void
ieee80211_beacon_construct(struct mbuf *m, uint8_t *frm,
struct ieee80211_node *ni)
@@ -3097,6 +3122,7 @@
/*
* Allocate a beacon frame and fillin the appropriate bits.
*/
+/* XXX VHT? */
struct mbuf *
ieee80211_beacon_alloc(struct ieee80211_node *ni)
{
@@ -3138,6 +3164,7 @@
* NB: we allocate the max space required for the TIM bitmap.
* XXX how big is this?
*/
+ /* XXX VHT? */
pktlen = 8 /* time stamp */
+ sizeof(uint16_t) /* beacon interval */
+ sizeof(uint16_t) /* capabilities */
@@ -3242,6 +3269,15 @@
}
wh = mtod(m, struct ieee80211_frame *);
+
+ /*
+ * XXX TODO Strictly speaking this should be incremented with the TX
+ * lock held so as to serialise access to the non-qos TID sequence
+ * number space.
+ *
+ * If the driver identifies it does its own TX seqno management then
+ * we can skip this (and still not do the TX seqno.)
+ */
seqno = ni->ni_txseqs[IEEE80211_NONQOS_TID]++;
*(uint16_t *)&wh->i_seq[0] =
htole16(seqno << IEEE80211_SEQ_SEQ_SHIFT);
@@ -3347,7 +3383,12 @@
timoff = 0;
timlen = 1;
}
+
+ /*
+ * TODO: validate this!
+ */
if (timlen != bo->bo_tim_len) {
+ /* XXX VHT? */
/* copy up/down trailer */
int adjust = tie->tim_bitmap+timlen
- bo->bo_tim_trailer;
@@ -3407,6 +3448,7 @@
* drop the count. The actual change happens above
* when the vap's count reaches the target count.
*/
+ /* XXX VHT */
if (vap->iv_csa_count == 0) {
memmove(&csa[1], csa, bo->bo_csa_trailer_len);
bo->bo_erp += sizeof(*csa);
Index: sys/net80211/ieee80211_proto.h
===================================================================
--- sys/net80211/ieee80211_proto.h (revision 311042)
+++ sys/net80211/ieee80211_proto.h (working copy)
@@ -77,6 +77,7 @@
void ieee80211_allmulti(struct ieee80211vap *, bool);
void ieee80211_syncflag(struct ieee80211vap *, int flag);
void ieee80211_syncflag_ht(struct ieee80211vap *, int flag);
+void ieee80211_syncflag_vht(struct ieee80211vap *, int flag);
void ieee80211_syncflag_ext(struct ieee80211vap *, int flag);
#define ieee80211_input(ni, m, rssi, nf) \
@@ -361,7 +362,8 @@
uint8_t *bo_csa; /* start of CSA element */
uint8_t *bo_quiet; /* start of Quiet element */
uint8_t *bo_meshconf; /* start of MESHCONF element */
- uint8_t *bo_spare[3];
+ uint8_t *bo_vhtinfo; /* start of VHT info element (XXX VHTCAP?) */
+ uint8_t *bo_spare[2];
};
struct mbuf *ieee80211_beacon_alloc(struct ieee80211_node *);
Index: sys/net80211/ieee80211_scan.h
===================================================================
--- sys/net80211/ieee80211_scan.h (revision 311042)
+++ sys/net80211/ieee80211_scan.h (working copy)
@@ -250,7 +250,9 @@
uint8_t *quiet;
uint8_t *meshid;
uint8_t *meshconf;
- uint8_t *spare[3];
+ uint8_t *vhtcap;
+ uint8_t *vhtopmode;
+ uint8_t *spare[1];
};
/*
Index: sys/net80211/ieee80211_scan_sta.c
===================================================================
--- sys/net80211/ieee80211_scan_sta.c (revision 311042)
+++ sys/net80211/ieee80211_scan_sta.c (working copy)
@@ -325,6 +325,18 @@
}
} else
ise->se_chan = curchan;
+
+ /* VHT demotion */
+ if (IEEE80211_IS_CHAN_VHT(ise->se_chan) && sp->vhtcap == NULL) {
+ /* 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. */
c = ieee80211_find_channel(ic, ise->se_chan->ic_freq,
@@ -333,6 +345,7 @@
("no legacy channel %u", ise->se_chan->ic_ieee));
ise->se_chan = c;
}
+
ise->se_fhdwell = sp->fhdwell;
ise->se_fhindex = sp->fhindex;
ise->se_erp = sp->erp;
@@ -531,10 +544,11 @@
/*
* Ignore dynamic turbo channels; we scan them
* in normal mode (i.e. not boosted). Likewise
- * for HT channels, they get scanned using
+ * for HT/VHT channels, they get scanned using
* legacy rates.
*/
- if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c))
+ if (IEEE80211_IS_CHAN_DTURBO(c) || IEEE80211_IS_CHAN_HT(c) ||
+ IEEE80211_IS_CHAN_VHT(c))
continue;
/*
@@ -819,6 +833,9 @@
* 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)
@@ -1624,6 +1641,7 @@
* "correct" rate control capabilities being
* negotiated.
*/
+ /* XXX VHT */
chan = ieee80211_ht_adjust_channel(ic,
chan, vap->iv_flags_ht);
ieee80211_create_ibss(vap, chan);
@@ -1655,6 +1673,7 @@
* IBSS node; we can then advertise HT IEs and speak HT
* to any subsequent nodes that support it.
*/
+ /* XXX VHT */
chan = ieee80211_ht_adjust_channel(ic,
chan, vap->iv_flags_ht);
if (!ieee80211_sta_join(vap, chan, &selbs->base))
@@ -1808,6 +1827,7 @@
ss->ss_flags &= ~IEEE80211_SCAN_NOPICK;
return 1;
}
+ /* XXX VHT */
ieee80211_create_ibss(vap,
ieee80211_ht_adjust_channel(ic, bestchan, vap->iv_flags_ht));
return 1;
@@ -1881,6 +1901,7 @@
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)
chan = ieee80211_ht_adjust_channel(ic,
Index: sys/net80211/ieee80211_sta.c
===================================================================
--- sys/net80211/ieee80211_sta.c (revision 311042)
+++ sys/net80211/ieee80211_sta.c (working copy)
@@ -64,6 +64,7 @@
#endif
#include
#include
+#include
#define IEEE80211_RATE2MBS(r) (((r) & IEEE80211_RATE_VAL) / 2)
@@ -1330,6 +1331,7 @@
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;
@@ -1434,6 +1436,14 @@
scan.htcap, scan.htinfo))
ht_state_change = 1;
}
+ if (scan.vhtcap != NULL && scan.vhtopmode != NULL &&
+ (vap->iv_flags_vht & IEEE80211_FVHT_VHT)) {
+ /* XXX state changes? */
+ if (ieee80211_vht_updateparams(ni,
+ scan.vhtcap, scan.vhtopmode))
+ ht_state_change = 1;
+ }
+
if (scan.quiet)
ic->ic_set_quiet(ni, scan.quiet);
@@ -1660,6 +1670,7 @@
frm += 2;
rates = xrates = wme = htcap = htinfo = NULL;
+ vhtcap = vhtopmode = NULL;
while (efrm - frm > 1) {
IEEE80211_VERIFY_LENGTH(efrm - frm, frm[1] + 2, return);
switch (*frm) {
@@ -1693,6 +1704,12 @@
}
/* 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;
}
@@ -1740,6 +1757,14 @@
ieee80211_setup_htrates(ni, htcap,
IEEE80211_F_JOIN | IEEE80211_F_DOBRS);
ieee80211_setup_basic_htrates(ni, htinfo);
+
+ if ((vhtcap != NULL) && (vhtopmode != NULL) &
+ (vap->iv_flags_vht & IEEE80211_FVHT_VHT)) {
+ ieee80211_vht_node_init(ni);
+ ieee80211_vht_updateparams(ni, vhtcap, vhtopmode);
+ ieee80211_setup_vht_rates(ni, vhtcap, vhtopmode);
+ }
+
ieee80211_node_setuptxparms(ni);
ieee80211_ratectl_node_init(ni);
}
Index: sys/net80211/ieee80211_var.h
===================================================================
--- sys/net80211/ieee80211_var.h (revision 311042)
+++ sys/net80211/ieee80211_var.h (working copy)
@@ -232,8 +232,9 @@
/* VHT information */
uint32_t ic_vhtcaps; /* VHT capabilities */
uint32_t ic_vhtextcaps; /* VHT extended capabilities (TODO) */
- struct ieee80211_vht_mcs_info iv_vht_mcsinfo; /* Support TX/RX VHT MCS */
- uint32_t ic_vht_spare[4];
+ struct ieee80211_vht_mcs_info ic_vht_mcsinfo; /* Support TX/RX VHT MCS */
+ uint32_t ic_flags_vht; /* VHT state flags */
+ uint32_t ic_vht_spare[3];
/* optional state for Atheros SuperG protocol extensions */
struct ieee80211_superg *ic_superg;
@@ -651,6 +652,10 @@
#define IEEE80211_FVEN_BITS "\20"
+#define IEEE80211_FVHT_VHT 0x000000001 /* CONF: VHT supported */
+#define IEEE80211_VFHT_BITS \
+ "\20\1VHT"
+
int ic_printf(struct ieee80211com *, const char *, ...) __printflike(2, 3);
void ieee80211_ifattach(struct ieee80211com *);
void ieee80211_ifdetach(struct ieee80211com *);
Index: sys/net80211/ieee80211_vht.c
===================================================================
--- sys/net80211/ieee80211_vht.c (nonexistent)
+++ sys/net80211/ieee80211_vht.c (working copy)
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 2016 Adrian Chadd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include
+#ifdef __FreeBSD__
+__FBSDID("$FreeBSD$");
+#endif
+
+/*
+ * IEEE 802.11ac-2013 protocol support.
+ */
+
+#include "opt_inet.h"
+#include "opt_wlan.h"
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+/* define here, used throughout file */
+#define MS(_v, _f) (((_v) & _f) >> _f##_S)
+#define SM(_v, _f) (((_v) << _f##_S) & _f)
+
+static void
+ieee80211_vht_init(void)
+{
+}
+
+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)
+{
+}
+
+void
+ieee80211_vht_vdetach(struct ieee80211vap *vap)
+{
+}
+
+#if 0
+static void
+vht_announce(struct ieee80211com *ic, enum ieee80211_phymode mode)
+{
+}
+#endif
+
+void
+ieee80211_vht_announce(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_vht_node_init(struct ieee80211_node *ni)
+{
+}
+
+void
+ieee80211_vht_node_cleanup(struct ieee80211_node *ni)
+{
+}
+
+/*
+ * Parse an 802.11ac VHT operation IE.
+ */
+void
+ieee80211_parse_vhtopmode(struct ieee80211_node *ni, const uint8_t *ie)
+{
+
+ printf("%s: called\n", __func__);
+}
+
+/*
+ * Parse an 802.11ac VHT capability IE.
+ */
+void
+ieee80211_parse_vhtcap(struct ieee80211_node *ni, const uint8_t *ie)
+{
+
+ printf("%s: called\n", __func__);
+}
+
+int
+ieee80211_vht_updateparams(struct ieee80211_node *ni,
+ const uint8_t *vhtcap_ie,
+ const uint8_t *vhtop_ie)
+{
+
+ printf("%s: called\n", __func__);
+ 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__);
+}
+
+void
+ieee80211_vht_timeout(struct ieee80211com *ic)
+{
+}
+
+void
+ieee80211_vht_node_join(struct ieee80211_node *ni)
+{
+
+ printf("%s: called\n", __func__);
+}
+
+void
+ieee80211_vht_node_leave(struct ieee80211_node *ni)
+{
+
+ printf("%s: called\n", __func__);
+}
Property changes on: sys/net80211/ieee80211_vht.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: sys/net80211/ieee80211_vht.h
===================================================================
--- sys/net80211/ieee80211_vht.h (nonexistent)
+++ sys/net80211/ieee80211_vht.h (working copy)
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2016 Adrian Chadd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $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);
+
+#endif /* _NET80211_IEEE80211_VHT_H_ */
Property changes on: sys/net80211/ieee80211_vht.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: sbin/ifconfig/ifieee80211.c
===================================================================
--- sbin/ifconfig/ifieee80211.c (revision 311042)
+++ sbin/ifconfig/ifieee80211.c (working copy)
@@ -143,7 +143,9 @@
[IEEE80211_MODE_11NA] = "11na",
[IEEE80211_MODE_11NG] = "11ng",
[IEEE80211_MODE_HALF] = "half",
- [IEEE80211_MODE_QUARTER] = "quarter"
+ [IEEE80211_MODE_QUARTER] = "quarter",
+ [IEEE80211_MODE_VHT_2GHZ] = "11ac2",
+ [IEEE80211_MODE_VHT_5GHZ] = "11ac",
};
static void set80211(int s, int type, int val, int len, void *data);
@@ -183,6 +185,20 @@
gothtconf = 1;
}
+/* VHT */
+static int vhtconf = 0;
+static int gotvhtconf = 0;
+
+static void
+getvhtconf(int s)
+{
+ if (gotvhtconf)
+ return;
+ if (get80211val(s, IEEE80211_IOC_VHTCONF, &vhtconf) < 0)
+ warn("unable to get VHT configuration information");
+ gotvhtconf = 1;
+}
+
/*
* Collect channel info from the kernel. We use this (mostly)
* to handle mapping between frequency and IEEE channel number.
@@ -200,6 +216,7 @@
err(1, "unable to get channel information");
ifmr = ifmedia_getstate(s);
gethtconf(s);
+ getvhtconf(s);
}
static struct regdata *
@@ -221,6 +238,9 @@
* allows the caller to look for promotion; e.g. from
* 11b > 11g.
*/
+/*
+ * XXX VHT
+ */
static int
canpromote(int i, int from, int to)
{
@@ -255,6 +275,9 @@
* channe list (e.g. mode 11a); we want to honor that to avoid
* confusing behaviour.
*/
+/*
+ * XXX VHT
+ */
static int
promote(int i)
{
@@ -361,6 +384,10 @@
static enum ieee80211_phymode
chan2mode(const struct ieee80211_channel *c)
{
+ if (IEEE80211_IS_CHAN_VHTA(c))
+ return IEEE80211_MODE_VHT_5GHZ;
+ if (IEEE80211_IS_CHAN_VHTG(c))
+ return IEEE80211_MODE_VHT_2GHZ;
if (IEEE80211_IS_CHAN_HTA(c))
return IEEE80211_MODE_11NA;
if (IEEE80211_IS_CHAN_HTG(c))
@@ -502,9 +529,12 @@
printf("drivercaps: 0x%x\n", dc->dc_drivercaps);
printf("cryptocaps: 0x%x\n", dc->dc_cryptocaps);
printf("htcaps : 0x%x\n", dc->dc_htcaps);
+ printf("vhtcaps : 0x%x\n", dc->dc_vhtcaps);
+#if 0
memcpy(chaninfo, &dc->dc_chaninfo,
IEEE80211_CHANINFO_SPACE(&dc->dc_chaninfo));
print_channels(s, &dc->dc_chaninfo, 1/*allchans*/, 1/*verbose*/);
+#endif
}
#endif
req = malloc(IEEE80211_REGDOMAIN_SIZE(dc->dc_chaninfo.ic_nchans));
@@ -610,6 +640,9 @@
* The result is not validated here; it's assumed to be
* checked against the channel table fetched from the kernel.
*/
+/*
+ * XXX VHT
+ */
static int
getchannelflags(const char *val, int freq)
{
@@ -616,6 +649,7 @@
#define _CHAN_HT 0x80000000
const char *cp;
int flags;
+ int is_vht = 0;
flags = 0;
@@ -636,6 +670,9 @@
case 'g': /* 802.11g */
flags |= IEEE80211_CHAN_G;
break;
+ case 'v':
+ is_vht = 1;
+ /* Fallthrough */
case 'h': /* ht = 802.11n */
case 'n': /* 802.11n */
flags |= _CHAN_HT; /* NB: private */
@@ -674,6 +711,15 @@
flags |= IEEE80211_CHAN_HT20;
break;
case 40:
+ case 80:
+ case 160:
+ /* Handle the 80/160 VHT flag */
+ if (cw == 80)
+ flags |= IEEE80211_CHAN_VHT80;
+ else if (cw == 160)
+ flags |= IEEE80211_CHAN_VHT160;
+
+ /* Fallthrough */
if (ep != NULL && *ep == '+')
flags |= IEEE80211_CHAN_HT40U;
else if (ep != NULL && *ep == '-')
@@ -683,6 +729,7 @@
errx(-1, "%s: Invalid channel width\n", val);
}
}
+
/*
* Cleanup specifications.
*/
@@ -695,6 +742,7 @@
* are also usable for legacy operation; e.g. freq:n/40.
*/
flags &= ~IEEE80211_CHAN_HT;
+ flags &= ~IEEE80211_CHAN_VHT;
} else {
/*
* Remove private indicator that this is an HT channel
@@ -714,6 +762,25 @@
mapchan(&chan, freq, 0);
flags |= (chan.ic_flags & IEEE80211_CHAN_HT);
}
+
+ /*
+ * If VHT is enabled, then also set the VHT flag and the
+ * relevant channel up/down.
+ */
+ if (is_vht && (flags & IEEE80211_CHAN_HT)) {
+ /*
+ * XXX yes, maybe we should just have VHT, and reuse
+ * HT20/HT40U/HT40D
+ */
+ if (flags & IEEE80211_CHAN_VHT80)
+ ;
+ else if (flags & IEEE80211_CHAN_HT20)
+ flags |= IEEE80211_CHAN_VHT20;
+ else if (flags & IEEE80211_CHAN_HT40U)
+ flags |= IEEE80211_CHAN_VHT40U;
+ else if (flags & IEEE80211_CHAN_HT40D)
+ flags |= IEEE80211_CHAN_VHT40D;
+ }
}
return flags;
#undef _CHAN_HT
@@ -1447,6 +1514,10 @@
case 'q': /* 1/4-width channels */
flags |= IEEE80211_CHAN_QUARTER;
break;
+ case 'v':
+ /* XXX set HT too? */
+ flags |= IEEE80211_CHAN_VHT;
+ break;
default:
errx(-1, "%s: Invalid mode attribute %c\n",
val, *cp);
@@ -1456,6 +1527,7 @@
return flags;
}
+/* XXX VHT */
#define IEEE80211_CHAN_HTA (IEEE80211_CHAN_HT|IEEE80211_CHAN_5GHZ)
#define IEEE80211_CHAN_HTG (IEEE80211_CHAN_HT|IEEE80211_CHAN_2GHZ)
@@ -1863,6 +1935,13 @@
set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
}
+static void
+set80211vhtconf(const char *val, int d, int s, const struct afswtch *rafp)
+{
+ set80211(s, IEEE80211_IOC_VHTCONF, d, 0, NULL);
+ vhtconf = d;
+}
+
static
DECL_CMD_FUNC(set80211tdmaslot, val, d)
{
@@ -1975,6 +2054,7 @@
/*
* Check channel compatibility.
*/
+/* XXX VHT */
static int
checkchan(const struct ieee80211req_chaninfo *avail, int freq, int flags)
{
@@ -2019,6 +2099,7 @@
}
}
+/* XXX VHT */
static void
regdomain_addchans(struct ieee80211req_chaninfo *ci,
const netband_head *bands,
@@ -2035,6 +2116,7 @@
hi_adj = (chanFlags & IEEE80211_CHAN_HT40U) ? -20 : 0;
lo_adj = (chanFlags & IEEE80211_CHAN_HT40D) ? 20 : 0;
channelSep = (chanFlags & IEEE80211_CHAN_2GHZ) ? 0 : 40;
+
LIST_FOREACH(nb, bands, next) {
b = nb->band;
if (verbose) {
@@ -2045,6 +2127,9 @@
putchar('\n');
}
prev = NULL;
+
+ /* XXX TODO: yes, take VHT80, VHT80+80, VHT160 into account */
+
for (freq = b->freqStart + lo_adj;
freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
/*
@@ -2055,6 +2140,38 @@
* then constrained according by channel separation.
*/
flags = nb->flags | b->flags;
+
+ /*
+ * VHT first - HT is a subset.
+ */
+ if (flags & IEEE80211_CHAN_VHT) {
+ if ((chanFlags & IEEE80211_CHAN_VHT20) &&
+ (flags & IEEE80211_CHAN_VHT20) == 0) {
+ if (verbose)
+ printf("%u: skip, not a "
+ "VHT20 channel\n", freq);
+ continue;
+ }
+ if ((chanFlags & IEEE80211_CHAN_VHT40) &&
+ (flags & IEEE80211_CHAN_VHT40) == 0) {
+ if (verbose)
+ printf("%u: skip, not a "
+ "VHT40 channel\n", freq);
+ continue;
+ }
+ if ((chanFlags & IEEE80211_CHAN_VHT80) &&
+ (flags & IEEE80211_CHAN_VHT80) == 0) {
+ if (verbose)
+ printf("%u: skip, not a "
+ "VHT80 channel\n", freq);
+ continue;
+ }
+
+ flags &= ~IEEE80211_CHAN_VHT;
+ flags |= chanFlags & IEEE80211_CHAN_VHT;
+ }
+
+ /* Now, constrain HT */
if (flags & IEEE80211_CHAN_HT) {
/*
* HT channels are generated specially; we're
@@ -2127,7 +2244,7 @@
memset(c, 0, sizeof(*c));
c->ic_freq = freq;
c->ic_flags = flags;
- if (c->ic_flags & IEEE80211_CHAN_DFS)
+ if (c->ic_flags & IEEE80211_CHAN_DFS)
c->ic_maxregpower = nb->maxPowerDFS;
else
c->ic_maxregpower = nb->maxPower;
@@ -2204,6 +2321,38 @@
&dc->dc_chaninfo);
}
}
+ if (!LIST_EMPTY(&rd->bands_11ac) && dc->dc_vhtcaps != 0) {
+ regdomain_addchans(ci, &rd->bands_11ac, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT20 |
+ IEEE80211_CHAN_VHT20,
+ &dc->dc_chaninfo);
+
+ /* VHT40 is a function of HT40.. */
+ if (dc->dc_htcaps & IEEE80211_HTCAP_CHWIDTH40) {
+ regdomain_addchans(ci, &rd->bands_11ac, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
+ IEEE80211_CHAN_VHT40U,
+ &dc->dc_chaninfo);
+ regdomain_addchans(ci, &rd->bands_11ac, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
+ IEEE80211_CHAN_VHT40D,
+ &dc->dc_chaninfo);
+ }
+
+ /* VHT80 */
+ /* XXX dc_vhtcap? */
+ if (1) {
+ regdomain_addchans(ci, &rd->bands_11ac, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT40U |
+ IEEE80211_CHAN_VHT80,
+ &dc->dc_chaninfo);
+ regdomain_addchans(ci, &rd->bands_11ac, reg,
+ IEEE80211_CHAN_A | IEEE80211_CHAN_HT40D |
+ IEEE80211_CHAN_VHT80,
+ &dc->dc_chaninfo);
+ }
+ }
+
if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
regdomain_addchans(ci, &rd->bands_11ng, reg,
IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
@@ -3574,7 +3723,22 @@
if (IEEE80211_IS_CHAN_TURBO(c))
strlcat(buf, " Turbo", bsize);
if (precise) {
- if (IEEE80211_IS_CHAN_HT20(c))
+ /* XXX should make VHT80U, VHT80D */
+ if (IEEE80211_IS_CHAN_VHT80(c) &&
+ IEEE80211_IS_CHAN_HT40D(c))
+ strlcat(buf, " vht/80-", bsize);
+ else if (IEEE80211_IS_CHAN_VHT80(c) &&
+ IEEE80211_IS_CHAN_HT40U(c))
+ strlcat(buf, " vht/80+", bsize);
+ else if (IEEE80211_IS_CHAN_VHT80(c))
+ strlcat(buf, " vht/80", bsize);
+ else if (IEEE80211_IS_CHAN_VHT40D(c))
+ strlcat(buf, " vht/40-", bsize);
+ else if (IEEE80211_IS_CHAN_VHT40U(c))
+ strlcat(buf, " vht/40+", bsize);
+ else if (IEEE80211_IS_CHAN_VHT20(c))
+ strlcat(buf, " vht/20", bsize);
+ else if (IEEE80211_IS_CHAN_HT20(c))
strlcat(buf, " ht/20", bsize);
else if (IEEE80211_IS_CHAN_HT40D(c))
strlcat(buf, " ht/40-", bsize);
@@ -3581,7 +3745,9 @@
else if (IEEE80211_IS_CHAN_HT40U(c))
strlcat(buf, " ht/40+", bsize);
} else {
- if (IEEE80211_IS_CHAN_HT(c))
+ if (IEEE80211_IS_CHAN_VHT(c))
+ strlcat(buf, " vht", bsize);
+ else if (IEEE80211_IS_CHAN_HT(c))
strlcat(buf, " ht", bsize);
}
return buf;
@@ -3612,6 +3778,12 @@
static int
chanpref(const struct ieee80211_channel *c)
{
+ if (IEEE80211_IS_CHAN_VHT80(c))
+ return 70;
+ if (IEEE80211_IS_CHAN_VHT40(c))
+ return 60;
+ if (IEEE80211_IS_CHAN_VHT20(c))
+ return 50;
if (IEEE80211_IS_CHAN_HT40(c))
return 40;
if (IEEE80211_IS_CHAN_HT20(c))
@@ -3807,6 +3979,11 @@
putchar('\n');
printb("htcaps", dc->dc_htcaps, IEEE80211_HTCAP_BITS);
}
+ if (dc->dc_vhtcaps != 0 || verbose) {
+ putchar('\n');
+ printb("vhtcaps", dc->dc_vhtcaps, IEEE80211_VHTCAP_BITS);
+ }
+
putchar('\n');
if (verbose) {
chaninfo = &dc->dc_chaninfo; /* XXX */
@@ -4847,6 +5024,18 @@
}
}
+ if (IEEE80211_IS_CHAN_VHT(c) || verbose) {
+ getvhtconf(s);
+ switch (vhtconf & 0x7) {
+ case 0:
+ LINE_CHECK("-vht");
+ break;
+ case 1:
+ LINE_CHECK("vht");
+ break;
+ }
+ }
+
if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
if (wme)
LINE_CHECK("wme");
@@ -5426,6 +5615,8 @@
DEF_CMD("-ht40", 0, set80211htconf),
DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */
DEF_CMD("-ht", 0, set80211htconf),
+ DEF_CMD("vht", 3, set80211vhtconf), /* NB: all */
+ DEF_CMD("-vht", 0, set80211vhtconf),
DEF_CMD("rifs", 1, set80211rifs),
DEF_CMD("-rifs", 0, set80211rifs),
DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps),
Index: lib/lib80211/lib80211_regdomain.c
===================================================================
--- lib/lib80211/lib80211_regdomain.c (revision 311042)
+++ lib/lib80211/lib80211_regdomain.c (working copy)
@@ -123,6 +123,8 @@
mt->curband = &mt->rd->bands_11ng;
else if (iseq(mode, "11na"))
mt->curband = &mt->rd->bands_11na;
+ else if (iseq(mode, "11ac"))
+ mt->curband = &mt->rd->bands_11ac;
else
warnx("unknown mode \"%s\" at line %ld",
__DECONST(char *, mode),
@@ -184,6 +186,9 @@
FLAG(IEEE80211_CHAN_G),
FLAG(IEEE80211_CHAN_HT20),
FLAG(IEEE80211_CHAN_HT40),
+ FLAG(IEEE80211_CHAN_VHT20),
+ FLAG(IEEE80211_CHAN_VHT40),
+ FLAG(IEEE80211_CHAN_VHT80),
FLAG(IEEE80211_CHAN_ST),
FLAG(IEEE80211_CHAN_TURBO),
FLAG(IEEE80211_CHAN_PASSIVE),
@@ -515,6 +520,15 @@
}
nb->band = id;
}
+ LIST_FOREACH(nb, &dp->bands_11ac, next) {
+ id = findid(rdp, nb->band, FREQBAND);
+ if (id == NULL) {
+ warnx("undefined 11ac band \"%s\"",
+ __DECONST(char *, nb->band));
+ errors++;
+ }
+ nb->band = id;
+ }
}
LIST_FOREACH(cp, &rdp->countries, next) {
id = cp->rd;
@@ -562,6 +576,7 @@
cleanup_bands(&dp->bands_11a);
cleanup_bands(&dp->bands_11ng);
cleanup_bands(&dp->bands_11na);
+ cleanup_bands(&dp->bands_11ac);
if (dp->name != NULL)
free(__DECONST(char *, dp->name));
}
Index: lib/lib80211/lib80211_regdomain.h
===================================================================
--- lib/lib80211/lib80211_regdomain.h (revision 311042)
+++ lib/lib80211/lib80211_regdomain.h (working copy)
@@ -75,6 +75,7 @@
netband_head bands_11a; /* 11a operation */
netband_head bands_11ng;/* 11ng operation */
netband_head bands_11na;/* 11na operation */
+ netband_head bands_11ac;/* 11ac 5GHz operation */
LIST_ENTRY(regdomain) next;
};