[ifconfig] add initial VHT (802.11ac) configuration and channel support to ifconfig.

This is very preliminary and mostly enough for me (with other patches)
to work on VHT support.

It adds:

* VHT20, VHT40 and VHT80 regulatory/band awareness
* VHT20, VHT40 and VHT80 channel configuration / population
* Parses vht channel specifications (eg ifconfig wlan0 create wlandev athp0 wlanmode monitor channel 36:vht/80)
* Configuration of VHT, VHT40, VHT80, VHT80+80, VHT160 channel
  width (IEEE80211_FVHT_VHT* flags in net80211)

TODO:

* No VHT80+80 or VHT160 channels yet - I don't yet have hardware, and I'm
  not yet sure how to support/populate VHT80+80 channels.
* No, I won't update the manpage until this is "more done", lest someone
  tries using vht and gets upset with me.
* No, I won't commit the regulatory database I'm testing with, so you'll
  just end up with no VHT channels ever populated.  Which is good, as there
  isn't an 11ac driver in-tree yet to try it with.
This commit is contained in:
Adrian Chadd 2017-01-07 02:07:05 +00:00
parent 35bcfd1c70
commit e9bb7f9aa1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=311579

View File

@ -119,6 +119,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 */
#endif
#define MAXCHAN 1536 /* max 1.5K channels */
@ -143,7 +144,9 @@ static const char *modename[IEEE80211_MODE_MAX] = {
[IEEE80211_MODE_11NA] = "11na",
[IEEE80211_MODE_11NG] = "11ng",
[IEEE80211_MODE_HALF] = "half",
[IEEE80211_MODE_QUARTER] = "quarter"
[IEEE80211_MODE_QUARTER] = "quarter",
[IEEE80211_MODE_VHT_2GHZ] = "11acg",
[IEEE80211_MODE_VHT_5GHZ] = "11ac",
};
static void set80211(int s, int type, int val, int len, void *data);
@ -183,6 +186,20 @@ gethtconf(int s)
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 +217,7 @@ getchaninfo(int s)
err(1, "unable to get channel information");
ifmr = ifmedia_getstate(s);
gethtconf(s);
getvhtconf(s);
}
static struct regdata *
@ -255,6 +273,9 @@ canpromote(int i, int from, int to)
* channe list (e.g. mode 11a); we want to honor that to avoid
* confusing behaviour.
*/
/*
* XXX VHT
*/
static int
promote(int i)
{
@ -361,6 +382,10 @@ getcurchan(int s)
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 +527,12 @@ setregdomain_cb(int s, void *arg)
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));
@ -616,6 +644,7 @@ getchannelflags(const char *val, int freq)
#define _CHAN_HT 0x80000000
const char *cp;
int flags;
int is_vht = 0;
flags = 0;
@ -636,6 +665,9 @@ getchannelflags(const char *val, int freq)
case 'g': /* 802.11g */
flags |= IEEE80211_CHAN_G;
break;
case 'v': /* vht: 802.11ac */
is_vht = 1;
/* Fallthrough */
case 'h': /* ht = 802.11n */
case 'n': /* 802.11n */
flags |= _CHAN_HT; /* NB: private */
@ -674,6 +706,15 @@ getchannelflags(const char *val, int freq)
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 +724,7 @@ getchannelflags(const char *val, int freq)
errx(-1, "%s: Invalid channel width\n", val);
}
}
/*
* Cleanup specifications.
*/
@ -695,6 +737,7 @@ getchannelflags(const char *val, int freq)
* 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 +757,25 @@ getchannelflags(const char *val, int freq)
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 +1509,10 @@ getmodeflags(const char *val)
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);
@ -1863,6 +1929,21 @@ set80211rifs(const char *val, int d, int s, const struct afswtch *rafp)
set80211(s, IEEE80211_IOC_RIFS, d, 0, NULL);
}
static void
set80211vhtconf(const char *val, int d, int s, const struct afswtch *rafp)
{
if (get80211val(s, IEEE80211_IOC_VHTCONF, &vhtconf) < 0)
errx(-1, "cannot set VHT setting");
printf("%s: vhtconf=0x%08x, d=%d\n", __func__, vhtconf, d);
if (d < 0) {
d = -d;
vhtconf &= ~d;
} else
vhtconf |= d;
printf("%s: vhtconf is now 0x%08x\n", __func__, vhtconf);
set80211(s, IEEE80211_IOC_VHTCONF, vhtconf, 0, NULL);
}
static
DECL_CMD_FUNC(set80211tdmaslot, val, d)
{
@ -2035,6 +2116,7 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
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,7 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
putchar('\n');
}
prev = NULL;
for (freq = b->freqStart + lo_adj;
freq <= b->freqEnd + hi_adj; freq += b->chanSep) {
/*
@ -2055,6 +2138,40 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
* then constrained according by channel separation.
*/
flags = nb->flags | b->flags;
/*
* VHT first - HT is a subset.
*
* XXX TODO: VHT80p80, VHT160 is not yet done.
*/
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 @@ regdomain_addchans(struct ieee80211req_chaninfo *ci,
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,40 @@ regdomain_makechannels(
&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);
}
/* XXX TODO: VHT80_80, VHT160 */
}
if (!LIST_EMPTY(&rd->bands_11ng) && dc->dc_htcaps != 0) {
regdomain_addchans(ci, &rd->bands_11ng, reg,
IEEE80211_CHAN_G | IEEE80211_CHAN_HT20,
@ -2435,6 +2586,8 @@ getflags(int flags)
if (flags & IEEE80211_NODE_HTCOMPAT)
*cp++ = '+';
}
if (flags & IEEE80211_NODE_VHT)
*cp++ = 'V';
if (flags & IEEE80211_NODE_WPS)
*cp++ = 'W';
if (flags & IEEE80211_NODE_TSN)
@ -3574,14 +3727,31 @@ get_chaninfo(const struct ieee80211_channel *c, int precise,
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);
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 +3782,16 @@ print_chaninfo(const struct ieee80211_channel *c, int verb)
static int
chanpref(const struct ieee80211_channel *c)
{
if (IEEE80211_IS_CHAN_VHT160(c))
return 80;
if (IEEE80211_IS_CHAN_VHT80_80(c))
return 75;
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 +3987,11 @@ list_capabilities(int s)
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 +5032,30 @@ ieee80211_status(int s)
}
}
if (IEEE80211_IS_CHAN_VHT(c) || verbose) {
getvhtconf(s);
if (vhtconf & 0x1)
LINE_CHECK("vht");
else
LINE_CHECK("-vht");
if (vhtconf & 0x2)
LINE_CHECK("vht40");
else
LINE_CHECK("-vht40");
if (vhtconf & 0x4)
LINE_CHECK("vht80");
else
LINE_CHECK("-vht80");
if (vhtconf & 0x8)
LINE_CHECK("vht80p80");
else
LINE_CHECK("-vht80p80");
if (vhtconf & 0x10)
LINE_CHECK("vht160");
else
LINE_CHECK("-vht160");
}
if (get80211val(s, IEEE80211_IOC_WME, &wme) != -1) {
if (wme)
LINE_CHECK("wme");
@ -5426,6 +5635,16 @@ static struct cmd ieee80211_cmds[] = {
DEF_CMD("-ht40", 0, set80211htconf),
DEF_CMD("ht", 3, set80211htconf), /* NB: 20+40 */
DEF_CMD("-ht", 0, set80211htconf),
DEF_CMD("vht", 1, set80211vhtconf),
DEF_CMD("-vht", 0, set80211vhtconf),
DEF_CMD("vht40", 2, set80211vhtconf),
DEF_CMD("-vht40", -2, set80211vhtconf),
DEF_CMD("vht80", 4, set80211vhtconf),
DEF_CMD("-vht80", -4, set80211vhtconf),
DEF_CMD("vht80p80", 8, set80211vhtconf),
DEF_CMD("-vht80p80", -8, set80211vhtconf),
DEF_CMD("vht160", 16, set80211vhtconf),
DEF_CMD("-vht160", -16, set80211vhtconf),
DEF_CMD("rifs", 1, set80211rifs),
DEF_CMD("-rifs", 0, set80211rifs),
DEF_CMD("smps", IEEE80211_HTCAP_SMPS_ENA, set80211smps),