On Thu, Mar 10, 2022 at 12:25:17PM +0100, Stefan Sperling wrote:
Unless anyone else finds a problem, this patch can be considered ready
for review and commit.
Of course, I forgot to apply my sysassert fix to the second phy context
command function... Fixed here.
diff refs/heads/master refs/heads/11ac
blob - 57bdcce64458e7f9d5802ce4247d5651f9183200
blob + 61022a02f457141e31ec6c04351519fedf7b5782
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -304,6 +304,7 @@ int iwx_schedule_session_protection(struct iwx_softc *
uint32_t);
void iwx_init_channel_map(struct iwx_softc *, uint16_t *, uint32_t *, int);
void iwx_setup_ht_rates(struct iwx_softc *);
+void iwx_setup_vht_rates(struct iwx_softc *);
int iwx_mimo_enabled(struct iwx_softc *);
void iwx_mac_ctxt_task(void *);
void iwx_phy_ctxt_task(void *);
@@ -363,12 +364,13 @@ void iwx_clear_oactive(struct iwx_softc *, struct
iwx_
void iwx_rx_bmiss(struct iwx_softc *, struct iwx_rx_packet *,
struct iwx_rx_data *);
int iwx_binding_cmd(struct iwx_softc *, struct iwx_node *, uint32_t);
+uint8_t iwx_get_vht_ctrl_pos(struct ieee80211com *, struct
ieee80211_channel *);
int iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *, struct iwx_phy_ctxt *,
uint8_t,
- uint8_t, uint32_t, uint8_t);
+ uint8_t, uint32_t, uint8_t, uint8_t);
int iwx_phy_ctxt_cmd_v3(struct iwx_softc *, struct iwx_phy_ctxt *, uint8_t,
- uint8_t, uint32_t, uint8_t);
+ uint8_t, uint32_t, uint8_t, uint8_t);
int iwx_phy_ctxt_cmd(struct iwx_softc *, struct iwx_phy_ctxt *, uint8_t,
- uint8_t, uint32_t, uint32_t, uint8_t);
+ uint8_t, uint32_t, uint32_t, uint8_t, uint8_t);
int iwx_send_cmd(struct iwx_softc *, struct iwx_host_cmd *);
int iwx_send_cmd_pdu(struct iwx_softc *, uint32_t, uint32_t, uint16_t,
const void *);
@@ -430,10 +432,12 @@ int iwx_scan_abort(struct iwx_softc *);
int iwx_enable_mgmt_queue(struct iwx_softc *);
int iwx_rs_rval2idx(uint8_t);
uint16_t iwx_rs_ht_rates(struct iwx_softc *, struct ieee80211_node *, int);
+uint16_t iwx_rs_vht_rates(struct iwx_softc *, struct ieee80211_node *, int);
int iwx_rs_init(struct iwx_softc *, struct iwx_node *);
int iwx_enable_data_tx_queues(struct iwx_softc *);
int iwx_phy_ctxt_update(struct iwx_softc *, struct iwx_phy_ctxt *,
- struct ieee80211_channel *, uint8_t, uint8_t, uint32_t, uint8_t);
+ struct ieee80211_channel *, uint8_t, uint8_t, uint32_t, uint8_t,
+ uint8_t);
int iwx_auth(struct iwx_softc *);
int iwx_deauth(struct iwx_softc *);
int iwx_run(struct iwx_softc *);
@@ -2812,6 +2816,12 @@ iwx_init_channel_map(struct iwx_softc *sc, uint16_t *c
if (ch_flags & IWX_NVM_CHANNEL_40MHZ)
channel->ic_flags |= IEEE80211_CHAN_40MHZ;
}
+
+ if (is_5ghz && data->sku_cap_11ac_enable) {
+ channel->ic_flags |= IEEE80211_CHAN_VHT;
+ if (ch_flags & IWX_NVM_CHANNEL_80MHZ)
+ channel->ic_xflags |= IEEE80211_CHANX_80MHZ;
+ }
}
}
@@ -2846,6 +2856,34 @@ iwx_setup_ht_rates(struct iwx_softc *sc)
}
void
+iwx_setup_vht_rates(struct iwx_softc *sc)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ uint8_t rx_ant = iwx_fw_valid_rx_ant(sc);
+ int n;
+
+ ic->ic_vht_rxmcs = (IEEE80211_VHT_MCS_0_9 <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(1));
+
+ if (iwx_mimo_enabled(sc) &&
+ ((rx_ant & IWX_ANT_AB) == IWX_ANT_AB ||
+ (rx_ant & IWX_ANT_BC) == IWX_ANT_BC)) {
+ ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_0_9 <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(2));
+ } else {
+ ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(2));
+ }
+
+ for (n = 3; n <= IEEE80211_VHT_NUM_SS; n++) {
+ ic->ic_vht_rxmcs |= (IEEE80211_VHT_MCS_SS_NOT_SUPP <<
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n));
+ }
+
+ ic->ic_vht_txmcs = ic->ic_vht_rxmcs;
+}
+
+void
iwx_init_reorder_buffer(struct iwx_reorder_buffer *reorder_buf,
uint16_t ssn, uint16_t buf_size)
{
@@ -3147,7 +3185,7 @@ iwx_phy_ctxt_task(void *arg)
struct ieee80211com *ic = &sc->sc_ic;
struct iwx_node *in = (void *)ic->ic_bss;
struct ieee80211_node *ni = &in->in_ni;
- uint8_t chains, sco;
+ uint8_t chains, sco, vht_chan_width;
int err, s = splnet();
if ((sc->sc_flags & IWX_FLAG_SHUTDOWN) ||
@@ -3159,13 +3197,23 @@ iwx_phy_ctxt_task(void *arg)
}
chains = iwx_mimo_enabled(sc) ? 2 : 1;
- if (ieee80211_node_supports_ht_chan40(ni))
+ if ((ni->ni_flags & IEEE80211_NODE_HT) &&
+ IEEE80211_CHAN_40MHZ_ALLOWED(ni->ni_chan) &&
+ ieee80211_node_supports_ht_chan40(ni))
sco = (ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK);
else
sco = IEEE80211_HTOP0_SCO_SCN;
- if (in->in_phyctxt->sco != sco) {
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+ IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+ ieee80211_node_supports_vht_chan80(ni))
+ vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
+ else
+ vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
+ if (in->in_phyctxt->sco != sco ||
+ in->in_phyctxt->vht_chan_width != vht_chan_width) {
err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
- in->in_phyctxt->channel, chains, chains, 0, sco);
+ in->in_phyctxt->channel, chains, chains, 0, sco,
+ vht_chan_width);
if (err)
printf("%s: failed to update PHY\n", DEVNAME(sc));
}
@@ -3981,8 +4029,13 @@ iwx_rx_frame(struct iwx_softc *sc, struct mbuf *m, int
tap->wr_chan_freq =
htole16(ic->ic_channels[chanidx].ic_freq);
chan_flags = ic->ic_channels[chanidx].ic_flags;
- if (ic->ic_curmode != IEEE80211_MODE_11N)
+ if (ic->ic_curmode != IEEE80211_MODE_11N &&
+ ic->ic_curmode != IEEE80211_MODE_11AC) {
chan_flags &= ~IEEE80211_CHAN_HT;
+ chan_flags &= ~IEEE80211_CHAN_40MHZ;
+ }
+ if (ic->ic_curmode != IEEE80211_MODE_11AC)
+ chan_flags &= ~IEEE80211_CHAN_VHT;
tap->wr_chan_flags = htole16(chan_flags);
tap->wr_dbm_antsignal = (int8_t)rxi->rxi_rssi;
tap->wr_dbm_antnoise = (int8_t)sc->sc_noise;
@@ -4806,9 +4859,42 @@ iwx_binding_cmd(struct iwx_softc *sc, struct iwx_node
return err;
}
+uint8_t
+iwx_get_vht_ctrl_pos(struct ieee80211com *ic, struct ieee80211_channel *chan)
+{
+ int center_idx = ic->ic_bss->ni_vht_chan_center_freq_idx0;
+ int primary_idx = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
+ /*
+ * The FW is expected to check the control channel position only
+ * when in HT/VHT and the channel width is not 20MHz. Return
+ * this value as the default one:
+ */
+ uint8_t pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
+
+ switch (primary_idx - center_idx) {
+ case -6:
+ pos = IWX_PHY_VHT_CTRL_POS_2_BELOW;
+ break;
+ case -2:
+ pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
+ break;
+ case 2:
+ pos = IWX_PHY_VHT_CTRL_POS_1_ABOVE;
+ break;
+ case 6:
+ pos = IWX_PHY_VHT_CTRL_POS_2_ABOVE;
+ break;
+ default:
+ break;
+ }
+
+ return pos;
+}
+
int
iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
- uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t
sco)
+ uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t
sco,
+ uint8_t vht_chan_width)
{
struct ieee80211com *ic = &sc->sc_ic;
struct iwx_phy_context_cmd_uhb cmd;
@@ -4829,7 +4915,10 @@ iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *sc, struct i
cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
cmd.ci.channel = htole32(ieee80211_chan2ieee(ic, chan));
- if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
+ if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
+ cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+ cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
+ } else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
if (sco == IEEE80211_HTOP0_SCO_SCA) {
/* secondary chan above -> control chan below */
cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
@@ -4860,7 +4949,8 @@ iwx_phy_ctxt_cmd_uhb_v3(struct iwx_softc *sc, struct i
int
iwx_phy_ctxt_cmd_v3(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
- uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t
sco)
+ uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint8_t
sco,
+ uint8_t vht_chan_width)
{
struct ieee80211com *ic = &sc->sc_ic;
struct iwx_phy_context_cmd cmd;
@@ -4881,7 +4971,10 @@ iwx_phy_ctxt_cmd_v3(struct iwx_softc *sc, struct iwx_p
cmd.ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ?
IWX_PHY_BAND_24 : IWX_PHY_BAND_5;
cmd.ci.channel = ieee80211_chan2ieee(ic, chan);
- if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
+ if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80) {
+ cmd.ci.ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan);
+ cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80;
+ } else if (chan->ic_flags & IEEE80211_CHAN_40MHZ) {
if (sco == IEEE80211_HTOP0_SCO_SCA) {
/* secondary chan above -> control chan below */
cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW;
@@ -4913,7 +5006,7 @@ iwx_phy_ctxt_cmd_v3(struct iwx_softc *sc, struct iwx_p
int
iwx_phy_ctxt_cmd(struct iwx_softc *sc, struct iwx_phy_ctxt *ctxt,
uint8_t chains_static, uint8_t chains_dynamic, uint32_t action,
- uint32_t apply_time, uint8_t sco)
+ uint32_t apply_time, uint8_t sco, uint8_t vht_chan_width)
{
int cmdver;
@@ -4933,11 +5026,11 @@ iwx_phy_ctxt_cmd(struct iwx_softc *sc, struct iwx_phy_
*/
if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS)) {
return iwx_phy_ctxt_cmd_uhb_v3(sc, ctxt, chains_static,
- chains_dynamic, action, sco);
+ chains_dynamic, action, sco, vht_chan_width);
}
return iwx_phy_ctxt_cmd_v3(sc, ctxt, chains_static, chains_dynamic,
- action, sco);
+ action, sco, vht_chan_width);
}
int
@@ -5258,13 +5351,23 @@ iwx_tx_fill_cmd(struct iwx_softc *sc, struct iwx_node
if ((ni->ni_flags & IEEE80211_NODE_HT) &&
type == IEEE80211_FC0_TYPE_DATA &&
rinfo->ht_plcp != IWX_RATE_HT_SISO_MCS_INV_PLCP) {
- uint8_t sco;
- if (ieee80211_node_supports_ht_chan40(ni))
+ uint8_t sco = IEEE80211_HTOP0_SCO_SCN;
+ uint8_t vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+ IEEE80211_CHAN_80MHZ_ALLOWED(ni->ni_chan) &&
+ ieee80211_node_supports_vht_chan80(ni))
+ vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
+ else if (IEEE80211_CHAN_40MHZ_ALLOWED(ni->ni_chan) &&
+ ieee80211_node_supports_ht_chan40(ni))
sco = (ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK);
- else
- sco = IEEE80211_HTOP0_SCO_SCN;
rate_flags |= IWX_RATE_MCS_HT_MSK;
- if ((sco == IEEE80211_HTOP0_SCO_SCA ||
+ if (vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80 &&
+ in->in_phyctxt != NULL &&
+ in->in_phyctxt->vht_chan_width == vht_chan_width) {
+ rate_flags |= IWX_RATE_MCS_CHAN_WIDTH_80;
+ if (ieee80211_node_supports_vht_sgi80(ni))
+ rate_flags |= IWX_RATE_MCS_SGI_MSK;
+ } else if ((sco == IEEE80211_HTOP0_SCO_SCA ||
sco == IEEE80211_HTOP0_SCO_SCB) &&
in->in_phyctxt != NULL && in->in_phyctxt->sco == sco) {
rate_flags |= IWX_RATE_MCS_CHAN_WIDTH_40;
@@ -5376,8 +5479,13 @@ iwx_tx(struct iwx_softc *sc, struct mbuf *m, struct ie
tap->wt_flags = 0;
tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq);
chan_flags = ni->ni_chan->ic_flags;
- if (ic->ic_curmode != IEEE80211_MODE_11N)
+ if (ic->ic_curmode != IEEE80211_MODE_11N &&
+ ic->ic_curmode != IEEE80211_MODE_11AC) {
chan_flags &= ~IEEE80211_CHAN_HT;
+ chan_flags &= ~IEEE80211_CHAN_40MHZ;
+ }
+ if (ic->ic_curmode != IEEE80211_MODE_11AC)
+ chan_flags &= ~IEEE80211_CHAN_VHT;
tap->wt_chan_flags = htole16(chan_flags);
if ((ni->ni_flags & IEEE80211_NODE_HT) &&
!IEEE80211_IS_MULTICAST(wh->i_addr1) &&
@@ -5797,7 +5905,9 @@ iwx_add_sta_cmd(struct iwx_softc *sc, struct iwx_node
{
struct iwx_add_sta_cmd add_sta_cmd;
int err;
- uint32_t status;
+ uint32_t status, aggsize;
+ const uint32_t max_aggsize = (IWX_STA_FLG_MAX_AGG_SIZE_64K >>
+ IWX_STA_FLG_MAX_AGG_SIZE_SHIFT);
struct ieee80211com *ic = &sc->sc_ic;
if (!update && (sc->sc_flags & IWX_FLAG_STA_ACTIVE))
@@ -5832,24 +5942,52 @@ iwx_add_sta_cmd(struct iwx_softc *sc, struct iwx_node
IWX_STA_FLG_AGG_MPDU_DENS_MSK);
if (iwx_mimo_enabled(sc)) {
- if (in->in_ni.ni_rxmcs[1] != 0) {
- add_sta_cmd.station_flags |=
- htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
+ if (in->in_ni.ni_flags & IEEE80211_NODE_VHT) {
+ uint16_t rx_mcs = (in->in_ni.ni_vht_rxmcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(2)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(2);
+ if (rx_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP) {
+ add_sta_cmd.station_flags |=
+ htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
+ }
+ } else {
+ if (in->in_ni.ni_rxmcs[1] != 0) {
+ add_sta_cmd.station_flags |=
+ htole32(IWX_STA_FLG_MIMO_EN_MIMO2);
+ }
+ if (in->in_ni.ni_rxmcs[2] != 0) {
+ add_sta_cmd.station_flags |=
+ htole32(IWX_STA_FLG_MIMO_EN_MIMO3);
+ }
}
- if (in->in_ni.ni_rxmcs[2] != 0) {
- add_sta_cmd.station_flags |=
- htole32(IWX_STA_FLG_MIMO_EN_MIMO3);
- }
}
- if (ieee80211_node_supports_ht_chan40(&in->in_ni)) {
+ if (IEEE80211_CHAN_40MHZ_ALLOWED(in->in_ni.ni_chan) &&
+ ieee80211_node_supports_ht_chan40(&in->in_ni)) {
add_sta_cmd.station_flags |= htole32(
IWX_STA_FLG_FAT_EN_40MHZ);
}
- add_sta_cmd.station_flags
- |= htole32(IWX_STA_FLG_MAX_AGG_SIZE_64K);
- switch (ic->ic_ampdu_params & IEEE80211_AMPDU_PARAM_SS) {
+ if (in->in_ni.ni_flags & IEEE80211_NODE_VHT) {
+ if (IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+ ieee80211_node_supports_vht_chan80(&in->in_ni)) {
+ add_sta_cmd.station_flags |= htole32(
+ IWX_STA_FLG_FAT_EN_80MHZ);
+ }
+ aggsize = (in->in_ni.ni_vhtcaps &
+ IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK) >>
+ IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT;
+ } else {
+ aggsize = (in->in_ni.ni_ampdu_param &
+ IEEE80211_AMPDU_PARAM_LE);
+ }
+ if (aggsize > max_aggsize)
+ aggsize = max_aggsize;
+ add_sta_cmd.station_flags |= htole32((aggsize <<
+ IWX_STA_FLG_MAX_AGG_SIZE_SHIFT) &
+ IWX_STA_FLG_MAX_AGG_SIZE_MSK);
+
+ switch (in->in_ni.ni_ampdu_param & IEEE80211_AMPDU_PARAM_SS) {
case IEEE80211_AMPDU_PARAM_SS_2:
add_sta_cmd.station_flags
|= htole32(IWX_STA_FLG_AGG_MPDU_DENS_2US);
@@ -6072,7 +6210,15 @@ iwx_fill_probe_req(struct iwx_softc *sc, struct iwx_sc
return ENOBUFS;
frm = ieee80211_add_htcaps(frm, ic);
/* XXX add WME info? */
+ remain -= frm - pos;
}
+
+ if (ic->ic_flags & IEEE80211_F_VHTON) {
+ if (remain < 14)
+ return ENOBUFS;
+ frm = ieee80211_add_vhtcaps(frm, ic);
+ }
+
preq->common_data.len = htole16(frm - pos);
return 0;
@@ -6829,6 +6975,38 @@ iwx_rs_ht_rates(struct iwx_softc *sc, struct ieee80211
return htrates;
}
+uint16_t
+iwx_rs_vht_rates(struct iwx_softc *sc, struct ieee80211_node *ni, int num_ss)
+{
+ uint16_t rx_mcs;
+ int max_mcs = -1;
+
+ rx_mcs = (ni->ni_vht_rxmcs & IEEE80211_VHT_MCS_FOR_SS_MASK(num_ss)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(num_ss);
+ switch (rx_mcs) {
+ case IEEE80211_VHT_MCS_SS_NOT_SUPP:
+ break;
+ case IEEE80211_VHT_MCS_0_7:
+ max_mcs = 7;
+ break;
+ case IEEE80211_VHT_MCS_0_8:
+ max_mcs = 8;
+ break;
+ case IEEE80211_VHT_MCS_0_9:
+ /* Disable VHT MCS 9 for 20MHz-only stations. */
+ if (!ieee80211_node_supports_ht_chan40(ni))
+ max_mcs = 8;
+ else
+ max_mcs = 9;
+ break;
+ default:
+ /* Should not happen; Values above cover the possible range. */
+ panic("invalid VHT Rx MCS value %u", rx_mcs);
+ }
+
+ return ((1 << (max_mcs + 1)) - 1);
+}
+
int
iwx_rs_init(struct iwx_softc *sc, struct iwx_node *in)
{
@@ -6849,27 +7027,49 @@ iwx_rs_init(struct iwx_softc *sc, struct iwx_node *in)
cfg_cmd.non_ht_rates |= (1 << idx);
}
- if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (ni->ni_flags & IEEE80211_NODE_VHT) {
+ cfg_cmd.mode = IWX_TLC_MNG_MODE_VHT;
+ cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_HT_BW_NONE_160] =
+ htole16(iwx_rs_vht_rates(sc, ni, 1));
+ cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_HT_BW_NONE_160] =
+ htole16(iwx_rs_vht_rates(sc, ni, 2));
+ } else if (ni->ni_flags & IEEE80211_NODE_HT) {
cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_HT_BW_NONE_160] =
- iwx_rs_ht_rates(sc, ni, IEEE80211_HT_RATESET_SISO);
+ htole16(iwx_rs_ht_rates(sc, ni,
+ IEEE80211_HT_RATESET_SISO));
cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_HT_BW_NONE_160] =
- iwx_rs_ht_rates(sc, ni, IEEE80211_HT_RATESET_MIMO2);
+ htole16(iwx_rs_ht_rates(sc, ni,
+ IEEE80211_HT_RATESET_MIMO2));
} else
cfg_cmd.mode = IWX_TLC_MNG_MODE_NON_HT;
cfg_cmd.sta_id = IWX_STATION_ID;
- if (in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCA ||
+ if (in->in_phyctxt->vht_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80)
+ cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_80MHZ;
+ else if (in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCA ||
in->in_phyctxt->sco == IEEE80211_HTOP0_SCO_SCB)
cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_40MHZ;
else
cfg_cmd.max_ch_width = IWX_TLC_MNG_CH_WIDTH_20MHZ;
cfg_cmd.chains = IWX_TLC_MNG_CHAIN_A_MSK | IWX_TLC_MNG_CHAIN_B_MSK;
- cfg_cmd.max_mpdu_len = 3839;
- if (ieee80211_node_supports_ht_sgi20(ni))
- cfg_cmd.sgi_ch_width_supp = (1 << IWX_TLC_MNG_CH_WIDTH_20MHZ);
- if (ieee80211_node_supports_ht_sgi40(ni))
- cfg_cmd.sgi_ch_width_supp = (1 << IWX_TLC_MNG_CH_WIDTH_40MHZ);
+ if (ni->ni_flags & IEEE80211_NODE_VHT)
+ cfg_cmd.max_mpdu_len = htole16(3895);
+ else
+ cfg_cmd.max_mpdu_len = htole16(3839);
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ if (ieee80211_node_supports_ht_sgi20(ni)) {
+ cfg_cmd.sgi_ch_width_supp |= (1 <<
+ IWX_TLC_MNG_CH_WIDTH_20MHZ);
+ }
+ if (ieee80211_node_supports_ht_sgi40(ni)) {
+ cfg_cmd.sgi_ch_width_supp |= (1 <<
+ IWX_TLC_MNG_CH_WIDTH_40MHZ);
+ }
+ }
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+ ieee80211_node_supports_vht_sgi80(ni))
+ cfg_cmd.sgi_ch_width_supp |= (1 << IWX_TLC_MNG_CH_WIDTH_80MHZ);
cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, cmd_size, &cfg_cmd);
@@ -6889,7 +7089,11 @@ iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_upd
return;
rate_n_flags = le32toh(notif->rate);
- if (rate_n_flags & IWX_RATE_MCS_HT_MSK) {
+ if (rate_n_flags & IWX_RATE_MCS_VHT_MSK) {
+ ni->ni_txmcs = (rate_n_flags & IWX_RATE_VHT_MCS_RATE_CODE_MSK);
+ ni->ni_vht_ss = ((rate_n_flags & IWX_RATE_VHT_MCS_NSS_MSK) >>
+ IWX_RATE_VHT_MCS_NSS_POS) + 1;
+ } else if (rate_n_flags & IWX_RATE_MCS_HT_MSK) {
ni->ni_txmcs = (rate_n_flags &
(IWX_RATE_HT_MCS_RATE_CODE_MSK |
IWX_RATE_HT_MCS_NSS_MSK));
@@ -6918,7 +7122,8 @@ iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_upd
int
iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_phy_ctxt *phyctxt,
struct ieee80211_channel *chan, uint8_t chains_static,
- uint8_t chains_dynamic, uint32_t apply_time, uint8_t sco)
+ uint8_t chains_dynamic, uint32_t apply_time, uint8_t sco,
+ uint8_t vht_chan_width)
{
uint16_t band_flags = (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ);
int err;
@@ -6928,7 +7133,8 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
(phyctxt->channel->ic_flags & band_flags) !=
(chan->ic_flags & band_flags)) {
err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
- chains_dynamic, IWX_FW_CTXT_ACTION_REMOVE, apply_time, sco);
+ chains_dynamic, IWX_FW_CTXT_ACTION_REMOVE, apply_time, sco,
+ vht_chan_width);
if (err) {
printf("%s: could not remove PHY context "
"(error %d)\n", DEVNAME(sc), err);
@@ -6936,7 +7142,8 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
}
phyctxt->channel = chan;
err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
- chains_dynamic, IWX_FW_CTXT_ACTION_ADD, apply_time, sco);
+ chains_dynamic, IWX_FW_CTXT_ACTION_ADD, apply_time, sco,
+ vht_chan_width);
if (err) {
printf("%s: could not add PHY context "
"(error %d)\n", DEVNAME(sc), err);
@@ -6945,7 +7152,8 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
} else {
phyctxt->channel = chan;
err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
- chains_dynamic, IWX_FW_CTXT_ACTION_MODIFY, apply_time, sco);
+ chains_dynamic, IWX_FW_CTXT_ACTION_MODIFY, apply_time, sco,
+ vht_chan_width);
if (err) {
printf("%s: could not update PHY context (error %d)\n",
DEVNAME(sc), err);
@@ -6954,6 +7162,7 @@ iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_p
}
phyctxt->sco = sco;
+ phyctxt->vht_chan_width = vht_chan_width;
return 0;
}
@@ -6969,12 +7178,14 @@ iwx_auth(struct iwx_softc *sc)
if (ic->ic_opmode == IEEE80211_M_MONITOR) {
err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
- ic->ic_ibss_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+ ic->ic_ibss_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT);
if (err)
return err;
} else {
err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
- in->in_ni.ni_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+ in->in_ni.ni_chan, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT);
if (err)
return err;
}
@@ -7087,7 +7298,8 @@ iwx_deauth(struct iwx_softc *sc)
/* Move unused PHY context to a default channel. */
err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
- &ic->ic_channels[1], 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+ &ic->ic_channels[1], 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT);
if (err)
return err;
@@ -7116,27 +7328,43 @@ iwx_run(struct iwx_softc *sc)
uint8_t chains = iwx_mimo_enabled(sc) ? 2 : 1;
err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
in->in_phyctxt->channel, chains, chains,
- 0, IEEE80211_HTOP0_SCO_SCN);
+ 0, IEEE80211_HTOP0_SCO_SCN,
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT);
if (err) {
printf("%s: failed to update PHY\n", DEVNAME(sc));
return err;
}
} else if (ni->ni_flags & IEEE80211_NODE_HT) {
uint8_t chains = iwx_mimo_enabled(sc) ? 2 : 1;
- uint8_t sco;
- if (ieee80211_node_supports_ht_chan40(ni))
+ uint8_t sco, vht_chan_width;
+ if (IEEE80211_CHAN_40MHZ_ALLOWED(in->in_ni.ni_chan) &&
+ ieee80211_node_supports_ht_chan40(ni))
sco = (ni->ni_htop0 & IEEE80211_HTOP0_SCO_MASK);
else
sco = IEEE80211_HTOP0_SCO_SCN;
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) &&
+ IEEE80211_CHAN_80MHZ_ALLOWED(in->in_ni.ni_chan) &&
+ ieee80211_node_supports_vht_chan80(ni))
+ vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_80;
+ else
+ vht_chan_width = IEEE80211_VHTOP0_CHAN_WIDTH_HT;
err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
in->in_phyctxt->channel, chains, chains,
- 0, sco);
+ 0, sco, vht_chan_width);
if (err) {
printf("%s: failed to update PHY\n", DEVNAME(sc));
return err;
}
}
+ /* Update STA again to apply HT and VHT settings. */
+ err = iwx_add_sta_cmd(sc, in, 1);
+ if (err) {
+ printf("%s: could not update STA (error %d)\n",
+ DEVNAME(sc), err);
+ return err;
+ }
+
/* We have now been assigned an associd by the AP. */
err = iwx_mac_ctxt_cmd(sc, in, IWX_FW_CTXT_ACTION_MODIFY, 1);
if (err) {
@@ -7255,7 +7483,8 @@ iwx_run_stop(struct iwx_softc *sc)
/* Reset Tx chains in case MIMO or 40 MHz channels were enabled. */
if (in->in_ni.ni_flags & IEEE80211_NODE_HT) {
err = iwx_phy_ctxt_update(sc, in->in_phyctxt,
- in->in_phyctxt->channel, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN);
+ in->in_phyctxt->channel, 1, 1, 0, IEEE80211_HTOP0_SCO_SCN,
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT);
if (err) {
printf("%s: failed to update PHY\n", DEVNAME(sc));
return err;
@@ -7905,7 +8134,8 @@ iwx_init_hw(struct iwx_softc *sc)
sc->sc_phyctxt[i].id = i;
sc->sc_phyctxt[i].channel = &ic->ic_channels[1];
err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[i], 1, 1,
- IWX_FW_CTXT_ACTION_ADD, 0, IEEE80211_HTOP0_SCO_SCN);
+ IWX_FW_CTXT_ACTION_ADD, 0, IEEE80211_HTOP0_SCO_SCN,
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT);
if (err) {
printf("%s: could not add phy context %d (error %d)\n",
DEVNAME(sc), i, err);
@@ -8016,6 +8246,8 @@ iwx_init(struct ifnet *ifp)
if (sc->sc_nvm.sku_cap_11n_enable)
iwx_setup_ht_rates(sc);
+ if (sc->sc_nvm.sku_cap_11ac_enable)
+ iwx_setup_vht_rates(sc);
KASSERT(sc->task_refs.refs == 0);
refcnt_init(&sc->task_refs);
@@ -9272,6 +9504,8 @@ iwx_preinit(struct iwx_softc *sc)
if (sc->sc_nvm.sku_cap_11n_enable)
iwx_setup_ht_rates(sc);
+ if (sc->sc_nvm.sku_cap_11ac_enable)
+ iwx_setup_vht_rates(sc);
/* not all hardware can do 5GHz band */
if (!sc->sc_nvm.sku_cap_band_52GHz_enable)
@@ -9498,6 +9732,13 @@ iwx_attach(struct device *parent, struct device *self,
ic->ic_aselcaps = 0;
ic->ic_ampdu_params = (IEEE80211_AMPDU_PARAM_SS_4 | 0x3 /* 64k */);
+ ic->ic_vhtcaps = IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 |
+ (IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K <<
+ IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT) |
+ (IEEE80211_VHTCAP_CHAN_WIDTH_80 <<
+ IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT) | IEEE80211_VHTCAP_SGI80 |
+ IEEE80211_VHTCAP_RX_ANT_PATTERN | IEEE80211_VHTCAP_TX_ANT_PATTERN;
+
ic->ic_sup_rates[IEEE80211_MODE_11A] = ieee80211_std_rateset_11a;
ic->ic_sup_rates[IEEE80211_MODE_11B] = ieee80211_std_rateset_11b;
ic->ic_sup_rates[IEEE80211_MODE_11G] = ieee80211_std_rateset_11g;
@@ -9505,6 +9746,8 @@ iwx_attach(struct device *parent, struct device *self,
for (i = 0; i < nitems(sc->sc_phyctxt); i++) {
sc->sc_phyctxt[i].id = i;
sc->sc_phyctxt[i].sco = IEEE80211_HTOP0_SCO_SCN;
+ sc->sc_phyctxt[i].vht_chan_width =
+ IEEE80211_VHTOP0_CHAN_WIDTH_HT;
}
/* IBSS channel undefined for now. */
blob - a855b36f5aee316aeaef1e4e43ab8fb9f128482e
blob + 64c5ec103876f14d1ac8e53f682b2c8a351ba42b
--- sys/dev/pci/if_iwxreg.h
+++ sys/dev/pci/if_iwxreg.h
@@ -6431,7 +6431,9 @@ struct iwx_umac_scan_iter_complete_notif {
#define IWX_STA_FLG_MAX_AGG_SIZE_256K (5 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
#define IWX_STA_FLG_MAX_AGG_SIZE_512K (6 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
#define IWX_STA_FLG_MAX_AGG_SIZE_1024K (7 <<
IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
-#define IWX_STA_FLG_MAX_AGG_SIZE_MSK (7 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
+#define IWX_STA_FLG_MAX_AGG_SIZE_2M (8 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
+#define IWX_STA_FLG_MAX_AGG_SIZE_4M (9 << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
+#define IWX_STA_FLG_MAX_AGG_SIZE_MSK (0xf << IWX_STA_FLG_MAX_AGG_SIZE_SHIFT)
#define IWX_STA_FLG_AGG_MPDU_DENS_SHIFT 23
#define IWX_STA_FLG_AGG_MPDU_DENS_2US (4 << IWX_STA_FLG_AGG_MPDU_DENS_SHIFT)
blob - f40dd20c64f442c8f11f9723b8ed3b2cef2f06b8
blob + 39c4c34e97ef9d27c645a73123b48fce64ba4445
--- sys/dev/pci/if_iwxvar.h
+++ sys/dev/pci/if_iwxvar.h
@@ -321,6 +321,7 @@ struct iwx_phy_ctxt {
uint32_t ref;
struct ieee80211_channel *channel;
uint8_t sco; /* 40 MHz secondary channel offset */
+ uint8_t vht_chan_width;
};
struct iwx_bf_data {
blob - 83d4eac091af3240f88ad56d06a47f6373f80922
blob + 470ec987e4341275eaa2f9a9e5a0f64d0b54c6a2
--- sys/net80211/ieee80211.c
+++ sys/net80211/ieee80211.c
@@ -472,11 +472,10 @@ ieee80211_media_init(struct ifnet *ifp,
ADD(ic, IFM_IEEE80211_VHT_MCS0 + i,
mopt | IFM_IEEE80211_MONITOR);
}
-#if 0
ic->ic_flags |= IEEE80211_F_VHTON; /* enable 11ac by default */
+ ic->ic_flags |= IEEE80211_F_HTON; /* 11ac implies 11n */
if (ic->ic_caps & IEEE80211_C_QOS)
ic->ic_flags |= IEEE80211_F_QOS;
-#endif
}
ieee80211_media_status(ifp, &imr);
@@ -663,6 +662,7 @@ ieee80211_media_change(struct ifnet *ifp)
(newphymode == IEEE80211_MODE_AUTO ||
newphymode == IEEE80211_MODE_11AC)) {
ic->ic_flags |= IEEE80211_F_VHTON;
+ ic->ic_flags |= IEEE80211_F_HTON;
ieee80211_configure_ampdu_tx(ic, 1);
} else if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) &&
(newphymode == IEEE80211_MODE_AUTO ||
blob - 2d31a17c87d7496541dba5934bef696d59f8c8fc
blob + d21964d56add61ec02fbcda02e118c76fdc54f6c
--- sys/net80211/ieee80211.h
+++ sys/net80211/ieee80211.h
@@ -441,7 +441,17 @@ enum {
IEEE80211_ELEMID_U_APSD_COEX = 142,
/* 143-174 reserved */
IEEE80211_ELEMID_MCCAOP_ADVERT_OVIEW = 174,
- /* 175-220 reserved */
+ /* 175-190 reserved */
+ IEEE80211_ELEMID_VHTCAPS = 191, /* 11ac */
+ IEEE80211_ELEMID_VHTOP = 192, /* 11ac */
+ IEEE80211_ELEMID_EXT_BSS_LOAD = 193, /* 11ac */
+ IEEE80211_ELEMID_WIDEBAND_CHNL_SWITCH = 194, /* 11ac */
+ IEEE80211_ELEMID_VHT_TXPOWER = 195, /* 11ac */
+ IEEE80211_ELEMID_CHNL_SWITCH_WRAPPER = 196, /* 11ac */
+ IEEE80211_ELEMID_AID = 197, /* 11ac */
+ IEEE80211_ELEMID_QUIET_CHNL = 198, /* 11ac */
+ IEEE80211_ELEMID_OPMODE_NOTIF = 199, /* 11ac */
+ /* 200-220 reserved */
IEEE80211_ELEMID_VENDOR = 221 /* vendor private */
/* 222-255 reserved */
};
@@ -712,6 +722,84 @@ enum {
/* Bits 12-15 are reserved. */
/*
+ * VHT Capabilities Info (see 802.11ac-2013 8.4.2.160.2).
+ */
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_MASK 0x00000003
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_SHIFT 0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_3895 0
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_7991 1
+#define IEEE80211_VHTCAP_MAX_MPDU_LENGTH_11454 2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_MASK 0x0c
+#define IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT 2
+#define IEEE80211_VHTCAP_CHAN_WIDTH_80 0
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160 1
+#define IEEE80211_VHTCAP_CHAN_WIDTH_160_8080 2
+#define IEEE80211_VHTCAP_RX_LDPC 0x00000010
+#define IEEE80211_VHTCAP_SGI80 0x00000020
+#define IEEE80211_VHTCAP_SGI160 0x00000040
+#define IEEE80211_VHTCAP_TX_STBC 0x00000080
+#define IEEE80211_VHTCAP_RX_STBC_SS_MASK 0x00000700
+#define IEEE80211_VHTCAP_RX_STBC_SS_SHIFT 8
+#define IEEE80211_VHTCAP_SU_BEAMFORMER 0x00000800
+#define IEEE80211_VHTCAP_SU_BEAMFORMEE 0x00001000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_MASK 0x0000e000
+#define IEEE80211_VHTCAP_BEAMFORMEE_STS_SHIFT 13
+#define IEEE80211_VHTCAP_NUM_STS_MASK 0x00070000
+#define IEEE80211_VHTCAP_NUM_STS_SHIFT 16
+#define IEEE80211_VHTCAP_MU_BEAMFORMER 0x00080000
+#define IEEE80211_VHTCAP_MU_BEAMFORMEE 0x00100000
+#define IEEE80211_VHTCAP_TXOP_PS 0x00200000
+#define IEEE80211_VHTCAP_HTC_VHT 0x00400000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_MASK 0x03800000
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_SHIFT 23
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_8K 0
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_16K 1
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_32K 2
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_64K 3
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_128K 4
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_256K 5
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_512K 6
+#define IEEE80211_VHTCAP_MAX_AMPDU_LEN_1024K 7
+#define IEEE80211_VHTCAP_LINK_ADAPT_MASK 0x0c000000
+#define IEEE80211_VHTCAP_LINK_ADAPT_SHIFT 26
+#define IEEE80211_VHTCAP_LINK_ADAPT_UNSOL_MFB 2
+#define IEEE80211_VHTCAP_LINK_ADAPT_MRQ_MFB 3
+#define IEEE80211_VHTCAP_RX_ANT_PATTERN 0x10000000
+#define IEEE80211_VHTCAP_TX_ANT_PATTERN 0x20000000
+
+/*
+ * VHT-MCS and NSS map (see 802.11ac-2013 8.4.2.160.3, Figure 8-401bs).
+ * Set of VHT MCS supported for a given number of spatial streams, `n'.
+ * Used by the VHT capabilities IE and by the basic VHT MSC set in
+ * the VHT operation IE.
+ */
+#define IEEE80211_VHT_MCS_FOR_SS_MASK(n) (0x3 << (2*((n)-1)))
+#define IEEE80211_VHT_MCS_FOR_SS_SHIFT(n) (2*((n)-1))
+#define IEEE80211_VHT_MCS_0_7 0
+#define IEEE80211_VHT_MCS_0_8 1
+#define IEEE80211_VHT_MCS_0_9 2
+#define IEEE80211_VHT_MCS_SS_NOT_SUPP 3
+
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_MASK 0x1fff
+#define IEEE80211_VHT_MAX_LGI_MBIT_S_SHIFT 0
+
+/* The highest number of spatial streams supported by VHT. */
+#define IEEE80211_VHT_NUM_SS 8
+
+/*
+ * VHT Operation element (see 802.11ac-2013 8.4.2.161).
+ */
+/* Byte 0. */
+#define IEEE80211_VHTOP0_CHAN_WIDTH_MASK 0x03
+#define IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT 0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_HT 0
+#define IEEE80211_VHTOP0_CHAN_WIDTH_80 1
+#define IEEE80211_VHTOP0_CHAN_WIDTH_160 2
+#define IEEE80211_VHTOP0_CHAN_WIDTH_8080 3
+/* Byte 1 contains channel center frequency index 0 for 80, 80+80, 160 MHz. */
+/* Byte 2 contains channel center frequency index 1 for 80+80 MHz only. */
+
+/*
* EDCA Access Categories.
*/
enum ieee80211_edca_ac {
blob - 1483da3b25a656f42c0d14d420998a0e4039a54f
blob + 8d928e16e1f5abd7e46143e116c00eab7d0e9619
--- sys/net80211/ieee80211_input.c
+++ sys/net80211/ieee80211_input.c
@@ -1606,7 +1606,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
const u_int8_t *tstamp, *ssid, *rates, *xrates, *edcaie, *wmmie, *tim;
- const u_int8_t *rsnie, *wpaie, *htcaps, *htop;
+ const u_int8_t *rsnie, *wpaie, *htcaps, *htop, *vhtcaps, *vhtop;
u_int16_t capinfo, bintval;
u_int8_t chan, bchan, erp;
int is_new;
@@ -1647,7 +1647,7 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
capinfo = LE_READ_2(frm); frm += 2;
ssid = rates = xrates = edcaie = wmmie = rsnie = wpaie = tim = NULL;
- htcaps = htop = NULL;
+ htcaps = htop = vhtcaps = vhtop = NULL;
bchan = ieee80211_chan2ieee(ic, ic->ic_bss->ni_chan);
chan = bchan;
erp = 0;
@@ -1692,6 +1692,12 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
case IEEE80211_ELEMID_HTOP:
htop = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
+ case IEEE80211_ELEMID_VHTOP:
+ vhtop = frm;
+ break;
case IEEE80211_ELEMID_TIM:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -1779,6 +1785,11 @@ ieee80211_recv_probe_resp(struct ieee80211com *ic, str
ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
if (htop && !ieee80211_setup_htop(ni, htop + 2, htop[1], 1))
htop = NULL; /* invalid HTOP */
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+ vhtop = NULL; /* invalid VHTOP */
+ }
if (tim) {
ni->ni_dtimcount = tim[2];
@@ -2021,7 +2032,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
{
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates, *htcaps;
+ const u_int8_t *ssid, *rates, *xrates, *htcaps, *vhtcaps;
u_int8_t rate;
if (ic->ic_opmode == IEEE80211_M_STA ||
@@ -2032,7 +2043,7 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
frm = (const u_int8_t *)&wh[1];
efrm = mtod(m, u_int8_t *) + m->m_len;
- ssid = rates = xrates = htcaps = NULL;
+ ssid = rates = xrates = htcaps = vhtcaps = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -2051,6 +2062,9 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
case IEEE80211_ELEMID_HTCAPS:
htcaps = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
}
frm += 2 + frm[1];
}
@@ -2101,6 +2115,10 @@ ieee80211_recv_probe_req(struct ieee80211com *ic, stru
ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
else
ieee80211_clear_htcaps(ni);
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ else
+ ieee80211_clear_vhtcaps(ni);
IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
}
#endif /* IEEE80211_STA_ONLY */
@@ -2170,7 +2188,8 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
{
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
- const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie, *htcaps;
+ const u_int8_t *ssid, *rates, *xrates, *rsnie, *wpaie, *wmeie;
+ const u_int8_t *htcaps, *vhtcaps;
u_int16_t capinfo, bintval;
int resp, status = 0;
struct ieee80211_rsnparams rsn;
@@ -2204,7 +2223,7 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
} else
resp = IEEE80211_FC0_SUBTYPE_ASSOC_RESP;
- ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = NULL;
+ ssid = rates = xrates = rsnie = wpaie = wmeie = htcaps = vhtcaps = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -2228,6 +2247,9 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
case IEEE80211_ELEMID_HTCAPS:
htcaps = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -2479,6 +2501,10 @@ ieee80211_recv_assoc_req(struct ieee80211com *ic, stru
ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]);
else
ieee80211_clear_htcaps(ni);
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan))
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ else
+ ieee80211_clear_vhtcaps(ni);
end:
if (status != 0) {
IEEE80211_SEND_MGMT(ic, ni, resp, status);
@@ -2507,6 +2533,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
const struct ieee80211_frame *wh;
const u_int8_t *frm, *efrm;
const u_int8_t *rates, *xrates, *edcaie, *wmmie, *htcaps, *htop;
+ const u_int8_t *vhtcaps, *vhtop;
u_int16_t capinfo, status, associd;
u_int8_t rate;
@@ -2541,6 +2568,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
associd = LE_READ_2(frm); frm += 2;
rates = xrates = edcaie = wmmie = htcaps = htop = NULL;
+ vhtcaps = vhtop = NULL;
while (frm + 2 <= efrm) {
if (frm + 2 + frm[1] > efrm) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -2562,6 +2590,12 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
case IEEE80211_ELEMID_HTOP:
htop = frm;
break;
+ case IEEE80211_ELEMID_VHTCAPS:
+ vhtcaps = frm;
+ break;
+ case IEEE80211_ELEMID_VHTOP:
+ vhtop = frm;
+ break;
case IEEE80211_ELEMID_VENDOR:
if (frm[1] < 4) {
ic->ic_stats.is_rx_elem_toosmall++;
@@ -2609,8 +2643,17 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, str
ieee80211_setup_htop(ni, htop + 2, htop[1], 0);
ieee80211_ht_negotiate(ic, ni);
- /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */
- if (ni->ni_flags & IEEE80211_NODE_HT)
+ if (htcaps && vhtcaps && IEEE80211_IS_CHAN_5GHZ(ic->ic_bss->ni_chan)) {
+ ieee80211_setup_vhtcaps(ni, vhtcaps + 2, vhtcaps[1]);
+ if (vhtop && !ieee80211_setup_vhtop(ni, vhtop + 2, vhtop[1], 1))
+ vhtop = NULL; /* invalid VHTOP */
+ }
+ ieee80211_vht_negotiate(ic, ni);
+
+ /* Hop into 11n/11ac modes after associating to a HT/VHT AP. */
+ if (ni->ni_flags & IEEE80211_NODE_VHT)
+ ieee80211_setmode(ic, IEEE80211_MODE_11AC);
+ else if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_setmode(ic, IEEE80211_MODE_11N);
else
ieee80211_setmode(ic, ieee80211_chan2mode(ic, ni->ni_chan));
blob - 65e93c23da2d86c0ad259f77b7c9affc85d9038b
blob + 32fc38fc29eeb977d19cd3e395f75da4f532ae67
--- sys/net80211/ieee80211_ioctl.h
+++ sys/net80211/ieee80211_ioctl.h
@@ -110,6 +110,8 @@ struct ieee80211_stats {
u_int32_t is_ht_rx_ba_window_gap_timeout;
u_int32_t is_ht_rx_ba_timeout;
u_int32_t is_ht_tx_ba_timeout;
+ u_int32_t is_vht_nego_no_mandatory_mcs;
+ u_int32_t is_vht_nego_no_basic_mcs;
};
#define SIOCG80211STATS _IOWR('i', 242, struct ifreq)
blob - 02c5a00fef8a8b714f214baacaeaffc2a67d348a
blob + c874c8746572eee71976e13d651d76cd8579098d
--- sys/net80211/ieee80211_node.c
+++ sys/net80211/ieee80211_node.c
@@ -87,6 +87,7 @@ void ieee80211_node_join_ht(struct ieee80211com *, str
void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_ht(struct ieee80211com *, struct ieee80211_node *);
+void ieee80211_node_leave_vht(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *);
void ieee80211_node_leave_pwrsave(struct ieee80211com *,
@@ -2411,6 +2412,71 @@ ieee80211_setup_htop(struct ieee80211_node *ni, const
}
/*
+ * Install received VHT caps information in the node's state block.
+ */
+void
+ieee80211_setup_vhtcaps(struct ieee80211_node *ni, const uint8_t *data,
+ uint8_t len)
+{
+ if (len != 12)
+ return;
+
+ ni->ni_vhtcaps = (data[0] | (data[1] << 8) | data[2] << 16 |
+ data[3] << 24);
+ ni->ni_vht_rxmcs = (data[4] | (data[5] << 8));
+ ni->ni_vht_rx_max_lgi_mbit_s = ((data[6] | (data[7] << 8)) &
+ IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+ ni->ni_vht_txmcs = (data[8] | (data[9] << 8));
+ ni->ni_vht_tx_max_lgi_mbit_s = ((data[10] | (data[11] << 8)) &
+ IEEE80211_VHT_MAX_LGI_MBIT_S_MASK);
+
+ ni->ni_flags |= IEEE80211_NODE_VHTCAP;
+}
+
+/*
+ * Install received VHT op information in the node's state block.
+ */
+int
+ieee80211_setup_vhtop(struct ieee80211_node *ni, const uint8_t *data,
+ uint8_t len, int isprobe)
+{
+
+ if (len != 5)
+ return 0;
+
+ if (data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_HT &&
+ data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_80 &&
+ data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_160 &&
+ data[0] != IEEE80211_VHTOP0_CHAN_WIDTH_8080)
+ return 0;
+
+ ni->ni_vht_chan_width = data[0];
+ ni->ni_vht_chan_center_freq_idx0 = data[1];
+ ni->ni_vht_chan_center_freq_idx1 = data[2];
+ ni->ni_vht_basic_mcs = (data[3] | data[4] << 8);
+ return 1;
+}
+
+#ifndef IEEE80211_STA_ONLY
+/*
+ * Handle nodes switching from 11ac into legacy modes.
+ */
+void
+ieee80211_clear_vhtcaps(struct ieee80211_node *ni)
+{
+ ni->ni_vhtcaps = 0;
+ ni->ni_vht_rxmcs = 0;
+ ni->ni_vht_rx_max_lgi_mbit_s = 0;
+ ni->ni_vht_txmcs = 0;
+ ni->ni_vht_tx_max_lgi_mbit_s = 0;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+ IEEE80211_NODE_VHT_SGI160 | IEEE80211_NODE_VHTCAP);
+
+}
+#endif
+
+/*
* Install received rate set information in the node's state block.
*/
int
@@ -2772,6 +2838,15 @@ ieee80211_node_leave_ht(struct ieee80211com *ic, struc
}
/*
+ * Handle a VHT STA leaving a VHT network.
+ */
+void
+ieee80211_node_leave_vht(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ ieee80211_clear_vhtcaps(ni);
+}
+
+/*
* Handle a station leaving an RSN network.
*/
void
@@ -2910,6 +2985,8 @@ ieee80211_node_leave(struct ieee80211com *ic, struct i
if (ni->ni_flags & IEEE80211_NODE_HT)
ieee80211_node_leave_ht(ic, ni);
+ if (ni->ni_flags & IEEE80211_NODE_VHT)
+ ieee80211_node_leave_vht(ic, ni);
if (ic->ic_node_leave != NULL)
(*ic->ic_node_leave)(ic, ni);
blob - 82cddbfb3a65784f65eaeaca87ca1efd8f619d2f
blob + e64e3ec26b20b7bc5c38cb6c07aede94cf7a6a8f
--- sys/net80211/ieee80211_node.h
+++ sys/net80211/ieee80211_node.h
@@ -352,6 +352,19 @@ struct ieee80211_node {
uint16_t ni_htop2;
uint8_t ni_basic_mcs[howmany(128,NBBY)];
+ /* VHT capabilities */
+ uint32_t ni_vhtcaps;
+ uint16_t ni_vht_rxmcs;
+ uint16_t ni_vht_rx_max_lgi_mbit_s;
+ uint16_t ni_vht_txmcs;
+ uint16_t ni_vht_tx_max_lgi_mbit_s;
+
+ /* VHT operation */
+ uint8_t ni_vht_chan_width;
+ uint8_t ni_vht_chan_center_freq_idx0;
+ uint8_t ni_vht_chan_center_freq_idx1;
+ uint16_t ni_vht_basic_mcs;
+
/* Timeout handlers which trigger Tx Block Ack negotiation. */
struct timeout ni_addba_req_to[IEEE80211_NUM_TID];
int ni_addba_req_intval[IEEE80211_NUM_TID];
@@ -406,6 +419,9 @@ struct ieee80211_node {
#define IEEE80211_NODE_HT_SGI40 0x8000 /* SGI on 40 MHz
negotiated */
#define IEEE80211_NODE_VHT 0x10000 /* VHT negotiated */
#define IEEE80211_NODE_HTCAP 0x20000 /* claims to support HT */
+#define IEEE80211_NODE_VHTCAP 0x40000 /* claims to support VHT */
+#define IEEE80211_NODE_VHT_SGI80 0x80000 /* SGI on 80 MHz negotiated */
+#define IEEE80211_NODE_VHT_SGI160 0x100000 /* SGI on 160 MHz negotiated */
/* If not NULL, this function gets called when ni_refcnt hits zero. */
void (*ni_unref_cb)(struct ieee80211com *,
@@ -500,6 +516,64 @@ ieee80211_node_supports_ht_chan40(struct ieee80211_nod
(ni->ni_htop0 & IEEE80211_HTOP0_CHW));
}
+/*
+ * Check if the peer supports VHT.
+ * Require a VHT capabilities IE and support for VHT MCS with a single
+ * spatial stream.
+ */
+static inline int
+ieee80211_node_supports_vht(struct ieee80211_node *ni)
+{
+ uint16_t rx_mcs;
+
+ rx_mcs = (ni->ni_vht_rxmcs & IEEE80211_VHT_MCS_FOR_SS_MASK(1)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(1);
+
+ return ((ni->ni_flags & IEEE80211_NODE_VHTCAP) &&
+ rx_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 80 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi80(struct ieee80211_node *ni)
+{
+ return ieee80211_node_supports_vht(ni) &&
+ (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80);
+}
+
+/* Check if the peer supports VHT short guard interval (SGI) on 160 MHz. */
+static inline int
+ieee80211_node_supports_vht_sgi160(struct ieee80211_node *ni)
+{
+ return ieee80211_node_supports_vht(ni) &&
+ (ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160);
+}
+
+/* Check if the peer can receive frames sent on an 80 MHz channel. */
+static inline int
+ieee80211_node_supports_vht_chan80(struct ieee80211_node *ni)
+{
+ uint8_t cap_chan_width, op_chan_width;
+
+ if (!ieee80211_node_supports_vht(ni))
+ return 0;
+
+ cap_chan_width = (ni->ni_vhtcaps & IEEE80211_VHTCAP_CHAN_WIDTH_MASK) >>
+ IEEE80211_VHTCAP_CHAN_WIDTH_SHIFT;
+ if (cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_80 &&
+ cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160 &&
+ cap_chan_width != IEEE80211_VHTCAP_CHAN_WIDTH_160_8080)
+ return 0;
+
+ op_chan_width = (ni->ni_vht_chan_width &
+ IEEE80211_VHTOP0_CHAN_WIDTH_MASK) >>
+ IEEE80211_VHTOP0_CHAN_WIDTH_SHIFT;
+
+ return (op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_80 ||
+ op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_160 ||
+ op_chan_width == IEEE80211_VHTOP0_CHAN_WIDTH_8080);
+}
+
struct ieee80211com;
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
@@ -537,6 +611,11 @@ void ieee80211_setup_htcaps(struct ieee80211_node *, c
void ieee80211_clear_htcaps(struct ieee80211_node *);
int ieee80211_setup_htop(struct ieee80211_node *, const uint8_t *,
uint8_t, int);
+void ieee80211_setup_vhtcaps(struct ieee80211_node *, const uint8_t *,
+ uint8_t);
+void ieee80211_clear_vhtcaps(struct ieee80211_node *);
+int ieee80211_setup_vhtop(struct ieee80211_node *, const uint8_t *,
+ uint8_t, int);
int ieee80211_setup_rates(struct ieee80211com *,
struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int);
void ieee80211_node_trigger_addba_req(struct ieee80211_node *, int);
blob - 16373cfbb38daeda4f52916589fe803fad3739f5
blob + 3a31fc3f292b7c14b1b52ee086238af5f022a722
--- sys/net80211/ieee80211_output.c
+++ sys/net80211/ieee80211_output.c
@@ -319,6 +319,12 @@ const struct ieee80211_edca_ac_params
[EDCA_AC_VI] = { 3, 4, 2, 94 },
[EDCA_AC_VO] = { 2, 3, 2, 47 }
},
+ [IEEE80211_MODE_11AC] = {
+ [EDCA_AC_BK] = { 4, 10, 7, 0 },
+ [EDCA_AC_BE] = { 4, 10, 3, 0 },
+ [EDCA_AC_VI] = { 3, 4, 2, 94 },
+ [EDCA_AC_VO] = { 2, 3, 2, 47 }
+ },
};
#ifndef IEEE80211_STA_ONLY
@@ -348,6 +354,12 @@ const struct ieee80211_edca_ac_params
[EDCA_AC_VI] = { 3, 4, 1, 94 },
[EDCA_AC_VO] = { 2, 3, 1, 47 }
},
+ [IEEE80211_MODE_11AC] = {
+ [EDCA_AC_BK] = { 4, 10, 7, 0 },
+ [EDCA_AC_BE] = { 4, 6, 3, 0 },
+ [EDCA_AC_VI] = { 3, 4, 1, 94 },
+ [EDCA_AC_VO] = { 2, 3, 1, 47 }
+ },
};
#endif /* IEEE80211_STA_ONLY */
@@ -1177,6 +1189,22 @@ ieee80211_add_htop(u_int8_t *frm, struct ieee80211com
}
#endif /* !IEEE80211_STA_ONLY */
+/*
+ * Add a VHT Capabilities element to a frame (see 802.11ac-2013 8.4.2.160.2).
+ */
+u_int8_t *
+ieee80211_add_vhtcaps(u_int8_t *frm, struct ieee80211com *ic)
+{
+ *frm++ = IEEE80211_ELEMID_VHTCAPS;
+ *frm++ = 12;
+ LE_WRITE_4(frm, ic->ic_vhtcaps); frm += 4;
+ LE_WRITE_2(frm, ic->ic_vht_rxmcs); frm += 2;
+ LE_WRITE_2(frm, ic->ic_vht_rx_max_lgi_mbit_s); frm += 2;
+ LE_WRITE_2(frm, ic->ic_vht_txmcs); frm += 2;
+ LE_WRITE_2(frm, ic->ic_vht_tx_max_lgi_mbit_s); frm += 2;
+ return frm;
+}
+
#ifndef IEEE80211_STA_ONLY
/*
* Add a Timeout Interval element to a frame (see 7.3.2.49).
@@ -1234,7 +1262,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struc
2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) +
((rs->rs_nrates > IEEE80211_RATE_SIZE) ?
2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) +
- ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+ ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+ ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
if (m == NULL)
return NULL;
@@ -1247,6 +1276,8 @@ ieee80211_get_probe_req(struct ieee80211com *ic, struc
frm = ieee80211_add_htcaps(frm, ic);
frm = ieee80211_add_wme_info(frm, ic);
}
+ if (ic->ic_flags & IEEE80211_F_VHTON)
+ frm = ieee80211_add_htcaps(frm, ic);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
@@ -1414,7 +1445,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struc
(((ic->ic_flags & IEEE80211_F_RSNON) &&
(ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ?
2 + IEEE80211_WPAIE_MAXLEN : 0) +
- ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0));
+ ((ic->ic_flags & IEEE80211_F_HTON) ? 28 + 9 : 0) +
+ ((ic->ic_flags & IEEE80211_F_VHTON) ? 14 : 0));
if (m == NULL)
return NULL;
@@ -1449,6 +1481,8 @@ ieee80211_get_assoc_req(struct ieee80211com *ic, struc
frm = ieee80211_add_htcaps(frm, ic);
frm = ieee80211_add_wme_info(frm, ic);
}
+ if (ic->ic_flags & IEEE80211_F_VHTON)
+ frm = ieee80211_add_vhtcaps(frm, ic);
m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *);
blob - afffb2b26dff94a28845e6fa407de62fce8aaf15
blob + f6b8501977be0a209efd6af8ac36744dee17c750
--- sys/net80211/ieee80211_proto.c
+++ sys/net80211/ieee80211_proto.c
@@ -75,6 +75,7 @@ const char * const ieee80211_phymode_name[] = {
"11b", /* IEEE80211_MODE_11B */
"11g", /* IEEE80211_MODE_11G */
"11n", /* IEEE80211_MODE_11N */
+ "11ac", /* IEEE80211_MODE_11AC */
};
void ieee80211_set_beacon_miss_threshold(struct ieee80211com *);
@@ -616,6 +617,58 @@ ieee80211_ht_negotiate(struct ieee80211com *ic, struct
}
void
+ieee80211_vht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni)
+{
+ int n;
+
+ ni->ni_flags &= ~(IEEE80211_NODE_VHT | IEEE80211_NODE_VHT_SGI80 |
+ IEEE80211_NODE_VHT_SGI160);
+
+ /* Check if we support VHT. */
+ if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11AC)) == 0)
+ return;
+
+ /* Check if VHT support has been explicitly disabled. */
+ if ((ic->ic_flags & IEEE80211_F_VHTON) == 0)
+ return;
+
+ /*
+ * Check if the peer supports VHT.
+ * MCS 0-7 for a single spatial stream are mandatory.
+ */
+ if (!ieee80211_node_supports_vht(ni)) {
+ ic->ic_stats.is_vht_nego_no_mandatory_mcs++;
+ return;
+ }
+
+ if (ic->ic_opmode == IEEE80211_M_STA) {
+ /* We must support the AP's basic MCS set. */
+ for (n = 1; n <= IEEE80211_VHT_NUM_SS; n++) {
+ uint16_t basic_mcs = (ni->ni_vht_basic_mcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+ uint16_t rx_mcs = (ic->ic_vht_rxmcs &
+ IEEE80211_VHT_MCS_FOR_SS_MASK(n)) >>
+ IEEE80211_VHT_MCS_FOR_SS_SHIFT(n);
+ if (basic_mcs != IEEE80211_VHT_MCS_SS_NOT_SUPP &&
+ basic_mcs > rx_mcs) {
+ ic->ic_stats.is_vht_nego_no_basic_mcs++;
+ return;
+ }
+ }
+ }
+
+ ni->ni_flags |= IEEE80211_NODE_VHT;
+
+ if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI80) &&
+ (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI80))
+ ni->ni_flags |= IEEE80211_NODE_VHT_SGI80;
+ if ((ni->ni_vhtcaps & IEEE80211_VHTCAP_SGI160) &&
+ (ic->ic_vhtcaps & IEEE80211_VHTCAP_SGI160))
+ ni->ni_flags |= IEEE80211_NODE_VHT_SGI160;
+}
+
+void
ieee80211_tx_ba_timeout(void *arg)
{
struct ieee80211_tx_ba *ba = arg;
@@ -1259,7 +1312,7 @@ justcleanup:
else
printf(" start %u%sMb",
rate / 2, (rate & 1) ? ".5" : "");
- printf(" %s preamble %s slot time%s%s\n",
+ printf(" %s preamble %s slot time%s%s%s\n",
(ic->ic_flags & IEEE80211_F_SHPREAMBLE) ?
"short" : "long",
(ic->ic_flags & IEEE80211_F_SHSLOT) ?
@@ -1267,7 +1320,9 @@ justcleanup:
(ic->ic_flags & IEEE80211_F_USEPROT) ?
" protection enabled" : "",
(ni->ni_flags & IEEE80211_NODE_HT) ?
- " HT enabled" : "");
+ " HT enabled" : "",
+ (ni->ni_flags & IEEE80211_NODE_VHT) ?
+ " VHT enabled" : "");
}
if (!(ic->ic_flags & IEEE80211_F_RSNON)) {
/*
blob - da0ab9168df1548f3ad0cff640e7211373c7c6ce
blob + 04292acf1a90b9776c080075410f12a9f1053f0b
--- sys/net80211/ieee80211_proto.h
+++ sys/net80211/ieee80211_proto.h
@@ -138,6 +138,7 @@ extern u_int8_t *ieee80211_add_xrates(u_int8_t *,
const struct ieee80211_rateset *);
extern u_int8_t *ieee80211_add_htcaps(u_int8_t *, struct ieee80211com
*);
extern u_int8_t *ieee80211_add_htop(u_int8_t *, struct ieee80211com *);
+extern u_int8_t *ieee80211_add_vhtcaps(u_int8_t *, struct ieee80211com *);
extern u_int8_t *ieee80211_add_tie(u_int8_t *, u_int8_t, u_int32_t);
extern int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *,
@@ -168,6 +169,8 @@ extern void ieee80211_sa_query_request(struct ieee8021
struct ieee80211_node *);
extern void ieee80211_ht_negotiate(struct ieee80211com *,
struct ieee80211_node *);
+extern void ieee80211_vht_negotiate(struct ieee80211com *,
+ struct ieee80211_node *);
extern void ieee80211_tx_ba_timeout(void *);
extern void ieee80211_rx_ba_timeout(void *);
extern int ieee80211_addba_request(struct ieee80211com *,
blob - 5be62abf361d73ee696752904b3a334b4ac65f38
blob + a51c1bc427c88add9132d7b5f1ed186bd151625a
--- sys/net80211/ieee80211_radiotap.h
+++ sys/net80211/ieee80211_radiotap.h
@@ -193,6 +193,7 @@ enum ieee80211_radiotap_type {
#define IEEE80211_CHAN_XR 0x1000 /* Extended range OFDM channel */
#define IEEE80211_CHAN_HT 0x2000 /* 11n/HT channel */
#define IEEE80211_CHAN_VHT 0x4000 /* 11ac/VHT channel */
+#define IEEE80211_CHAN_40MHZ 0x8000 /* use of 40 MHz is allowed */
#endif /* !_KERNEL */
/* For IEEE80211_RADIOTAP_FLAGS */
blob - 161853a629887a1aa997480d605d4febd66dd2db
blob + d9b8d5cd8832ecf5264539c16c4d1cad78e5ad17
--- sys/net80211/ieee80211_var.h
+++ sys/net80211/ieee80211_var.h
@@ -120,6 +120,7 @@ enum ieee80211_protmode {
struct ieee80211_channel {
u_int16_t ic_freq; /* setting in MHz */
u_int16_t ic_flags; /* see below */
+ u_int32_t ic_xflags; /* extra flags; see below */
};
/*
@@ -137,6 +138,12 @@ struct ieee80211_channel {
#define IEEE80211_CHAN_40MHZ 0x8000 /* use of 40 MHz is allowed */
/*
+ * Extra channel flags.
+ */
+#define IEEE80211_CHANX_80MHZ 0x00000001 /* use of 80 MHz is allowed */
+#define IEEE80211_CHANX_160MHZ 0x00000002 /* use of 160 MHz is allowed */
+
+/*
* Useful combinations of channel characteristics.
*/
#define IEEE80211_CHAN_A \
@@ -172,6 +179,13 @@ struct ieee80211_channel {
#define IEEE80211_IS_CHAN_XR(_c) \
(((_c)->ic_flags & IEEE80211_CHAN_XR) != 0)
+#define IEEE80211_CHAN_40MHZ_ALLOWED(_c) \
+ (((_c)->ic_flags & IEEE80211_CHAN_40MHZ) != 0)
+#define IEEE80211_CHAN_80MHZ_ALLOWED(_c) \
+ (((_c)->ic_xflags & IEEE80211_CHANX_80MHZ) != 0)
+#define IEEE80211_CHAN_160MHZ_ALLOWED(_c) \
+ (((_c)->ic_xflags & IEEE80211_CHANX_160MHZ) != 0)
+
/*
* EDCA AC parameters.
*/
@@ -362,6 +376,13 @@ struct ieee80211com {
u_int8_t ic_aselcaps;
u_int8_t ic_dialog_token;
int ic_fixed_mcs;
+
+ uint32_t ic_vhtcaps;
+ uint16_t ic_vht_rxmcs;
+ uint16_t ic_vht_rx_max_lgi_mbit_s;
+ uint16_t ic_vht_txmcs;
+ uint16_t ic_vht_tx_max_lgi_mbit_s;
+
TAILQ_HEAD(, ieee80211_ess) ic_ess;
};
#define ic_if ic_ac.ac_if