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); @@ -147,10 +151,13 @@ */ if (c->ic_ieee == 0) c->ic_ieee = ieee80211_mhz2ieee(c->ic_freq,c->ic_flags); + + /* XXX VHT */ 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); + /* default max tx power to max regulatory */ if (c->ic_maxpower == 0) c->ic_maxpower = 2*c->ic_maxregpower; @@ -180,6 +187,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 +218,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 +347,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 +391,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 +521,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 +614,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 +675,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 +723,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 +740,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 +879,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. @@ -1003,6 +1069,9 @@ } } +/* + * XXX VHT + */ static __inline void set_extchan(struct ieee80211_channel *c) { @@ -1019,6 +1088,9 @@ c->ic_extieee = 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 +1100,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,6 +1113,7 @@ c->ic_maxregpower = maxregpower; c->ic_maxpower = 2 * maxregpower; c->ic_flags = flags; + /* XXX VHT */ set_extchan(c); return (0); @@ -1050,6 +1130,12 @@ 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; @@ -1058,6 +1144,9 @@ return (0); } +/* + * XXX VHT-2GHz + */ static void getflags_2ghz(const uint8_t bands[], uint32_t flags[], int ht40) { @@ -1077,36 +1166,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 +1252,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 +1286,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) @@ -1242,6 +1373,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 +1380,39 @@ { 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 (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 +1441,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 +1454,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 +1601,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 +1715,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 +1763,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; };