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