This patch adds initial 11ac support to the iwx(4) driver. This means that 80MHz channels can be used. No other 11ac features are enabled yet.
This is not yet a patch which could be committed. Apart from debug prints which need to go, there is a known issue found by dv@ where this patch causes a firmware error, sysassert 0x20101A25. The reason for this is not known. It would help to get more testing to see if more clues can be found based on where this error pops up. I cannot reproduce the error myself. When sending feedback, please be clear about which iwx(4) device and which access point has been tested. Thanks! The patch works for me on AX200 and AX201 with a pepwave AC one mini AP, although throughput is not much different to 11n 40MHz with this AP. diff refs/heads/master refs/heads/11ac blob - 57bdcce64458e7f9d5802ce4247d5651f9183200 blob + a56c59f82c854c282a61c302162df4eae3c27fb8 --- 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,15 @@ 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) { + printf("%s: channel %d 80MHz ok\n", __func__, hw_value); + channel->ic_xflags |= IEEE80211_CHANX_80MHZ; + } else + printf("%s: channel %d 80MHz NOT ok\n", __func__, hw_value); + } } } @@ -2846,6 +2859,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 +3188,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 +3200,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 +4032,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 +4862,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 +4918,17 @@ 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) { + uint8_t ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan); + if (ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_BELOW || + ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_ABOVE) { + cmd.ci.ctrl_pos = ctrl_pos; + cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80; + } else { + cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20; + cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW; + } + } 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 +4959,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 +4981,17 @@ 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) { + uint8_t ctrl_pos = iwx_get_vht_ctrl_pos(ic, chan); + if (ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_BELOW || + ctrl_pos == IWX_PHY_VHT_CTRL_POS_2_ABOVE) { + cmd.ci.ctrl_pos = ctrl_pos; + cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE80; + } else { + cmd.ci.width = IWX_PHY_VHT_CHANNEL_MODE20; + cmd.ci.ctrl_pos = IWX_PHY_VHT_CTRL_POS_1_BELOW; + } + } 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 +5023,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 +5043,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 +5368,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 +5496,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 +5922,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 +5959,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 +6227,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 +6992,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 +7044,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 +7106,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 +7139,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 +7150,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 +7159,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 +7169,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 +7179,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 +7195,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 +7315,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 +7345,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 +7500,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 +8151,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 +8263,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 +9521,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 +9749,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 +9763,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