On Sat, Dec 12, 2015 at 12:09:17AM +0100, Stefan Sperling wrote: > Here's an updated diff, which applies to -current, for testing. > I'll spend some time tomorrow splitting this up into smaller > chunks for review with explanations of the changes. > > No known problems exist with this diff. > Please test anywhere, even without iwm(4). Thanks. >
For some reason, a rouge pci/if_iwm.c file sneaked into the previous diff. Sorry about that. Fixed diff: Index: dev/pci/if_iwm.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v retrieving revision 1.70 diff -u -p -r1.70 if_iwm.c --- dev/pci/if_iwm.c 11 Dec 2015 16:07:02 -0000 1.70 +++ dev/pci/if_iwm.c 11 Dec 2015 22:48:52 -0000 @@ -170,19 +170,22 @@ const uint8_t iwm_nvm_channels[] = { const struct iwm_rate { uint8_t rate; uint8_t plcp; + uint8_t ht_plcp; } iwm_rates[] = { - { 2, IWM_RATE_1M_PLCP }, - { 4, IWM_RATE_2M_PLCP }, - { 11, IWM_RATE_5M_PLCP }, - { 22, IWM_RATE_11M_PLCP }, - { 12, IWM_RATE_6M_PLCP }, - { 18, IWM_RATE_9M_PLCP }, - { 24, IWM_RATE_12M_PLCP }, - { 36, IWM_RATE_18M_PLCP }, - { 48, IWM_RATE_24M_PLCP }, - { 72, IWM_RATE_36M_PLCP }, - { 96, IWM_RATE_48M_PLCP }, - { 108, IWM_RATE_54M_PLCP }, + /* Legacy */ /* HT */ + { 2, IWM_RATE_1M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, + { 4, IWM_RATE_2M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, + { 11, IWM_RATE_5M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, + { 22, IWM_RATE_11M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, + { 12, IWM_RATE_6M_PLCP, IWM_RATE_HT_SISO_MCS_0_PLCP }, + { 18, IWM_RATE_9M_PLCP, IWM_RATE_HT_SISO_MCS_INV_PLCP }, + { 24, IWM_RATE_12M_PLCP, IWM_RATE_HT_SISO_MCS_1_PLCP }, + { 36, IWM_RATE_18M_PLCP, IWM_RATE_HT_SISO_MCS_2_PLCP }, + { 48, IWM_RATE_24M_PLCP, IWM_RATE_HT_SISO_MCS_3_PLCP }, + { 72, IWM_RATE_36M_PLCP, IWM_RATE_HT_SISO_MCS_4_PLCP }, + { 96, IWM_RATE_48M_PLCP, IWM_RATE_HT_SISO_MCS_5_PLCP }, + { 108, IWM_RATE_54M_PLCP, IWM_RATE_HT_SISO_MCS_6_PLCP }, + { 128, IWM_RATE_INVM_PLCP, IWM_RATE_HT_SISO_MCS_7_PLCP }, }; #define IWM_RIDX_CCK 0 #define IWM_RIDX_OFDM 4 @@ -190,6 +193,18 @@ const struct iwm_rate { #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) +/* Convert an MCS index into an iwm_rates[] index. */ +const int iwm_mcs2ridx[] = { + IWM_RATE_MCS_0_INDEX, + IWM_RATE_MCS_1_INDEX, + IWM_RATE_MCS_2_INDEX, + IWM_RATE_MCS_3_INDEX, + IWM_RATE_MCS_4_INDEX, + IWM_RATE_MCS_5_INDEX, + IWM_RATE_MCS_6_INDEX, + IWM_RATE_MCS_7_INDEX, +}; + int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t); int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type, uint8_t *, size_t); @@ -278,6 +293,24 @@ int iwm_nvm_read_chunk(struct iwm_softc int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, uint16_t *); void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const); +#ifndef IEEE80211_NO_HT +void iwm_setup_ht_rates(struct iwm_softc *); +int iwm_ampdu_rx_start(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +void iwm_ampdu_rx_stop(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +void iwm_mvm_sta_rx_agg(struct iwm_softc *, struct ieee80211_node *, + uint8_t, uint16_t, int); +#ifdef notyet +int iwm_ampdu_tx_start(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +void iwm_ampdu_tx_stop(struct ieee80211com *, + struct ieee80211_node *, uint8_t); +#endif +void iwm_ba_task(void *); + +#endif /* IEEE80211_NO_HT */ + int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, const uint16_t *, const uint16_t *, uint8_t, uint8_t); @@ -2370,6 +2403,13 @@ iwm_nvm_read_section(struct iwm_softc *s * BEGIN IWM_NVM_PARSE */ +#define IWM_FW_VALID_TX_ANT(sc) \ + ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) \ + >> IWM_FW_PHY_CFG_TX_CHAIN_POS) +#define IWM_FW_VALID_RX_ANT(sc) \ + ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) \ + >> IWM_FW_PHY_CFG_RX_CHAIN_POS) + /* NVM offsets (in words) definitions */ enum wkp_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ @@ -2394,6 +2434,7 @@ enum nvm_sku_bits { IWM_NVM_SKU_CAP_BAND_52GHZ = (1 << 1), IWM_NVM_SKU_CAP_11N_ENABLE = (1 << 2), IWM_NVM_SKU_CAP_11AC_ENABLE = (1 << 3), + IWM_NVM_SKU_CAP_MIMO_DISABLE = (1 << 5), }; /* radio config bits (actual values from NVM definition) */ @@ -2477,9 +2518,146 @@ iwm_init_channel_map(struct iwm_softc *s if (!(ch_flags & IWM_NVM_CHANNEL_ACTIVE)) channel->ic_flags |= IEEE80211_CHAN_PASSIVE; + +#ifndef IEEE80211_NO_HT + if (data->sku_cap_11n_enable) + channel->ic_flags |= IEEE80211_CHAN_HT; +#endif + } +} + +#ifndef IEEE80211_NO_HT +void +iwm_setup_ht_rates(struct iwm_softc *sc) +{ + struct ieee80211com *ic = &sc->sc_ic; + + /* TX is supported with the same MCS as RX. */ + ic->ic_tx_mcs_set = IEEE80211_TX_MCS_SET_DEFINED; + + ic->ic_sup_mcs[0] = 0xff; /* MCS 0-7 */ + +#ifdef notyet + if (sc->sc_nvm.sku_cap_mimo_disable) + return; + + if (IWM_FW_VALID_RX_ANT(sc) > 1) + ic->ic_sup_mcs[1] = 0xff; /* MCS 7-15 */ + if (IWM_FW_VALID_RX_ANT(sc) > 2) + ic->ic_sup_mcs[2] = 0xff; /* MCS 16-23 */ +#endif +} + +#define IWM_MAX_RX_BA_SESSIONS 16 + +void +iwm_mvm_sta_rx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, + uint8_t tid, uint16_t ssn, int start) +{ + struct iwm_mvm_add_sta_cmd_v6 cmd; + struct iwm_node *in = (void *)ni; + int ret, s; + uint32_t status; + + if (start && sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS) + return; + + memset(&cmd, 0, sizeof(cmd)); + + cmd.sta_id = IWM_STATION_ID; + cmd.mac_id_n_color + = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); + cmd.add_modify = IWM_STA_MODE_MODIFY; + + if (start) { + cmd.add_immediate_ba_tid = (uint8_t)tid; + cmd.add_immediate_ba_ssn = ssn; + } else { + cmd.remove_immediate_ba_tid = (uint8_t)tid; + } + cmd.modify_mask = start ? IWM_STA_MODIFY_ADD_BA_TID : + IWM_STA_MODIFY_REMOVE_BA_TID; + + status = IWM_ADD_STA_SUCCESS; + ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status); + if (ret) + return; + + switch (status) { + case IWM_ADD_STA_SUCCESS: + DPRINTF(("RX BA Session %sed in fw\n", + start ? "start" : "stopp")); + s = splnet(); + if (start) + sc->sc_rx_ba_sessions++; + else if (sc->sc_rx_ba_sessions > 0) + /* check that restart flow didn't zero counter */ + sc->sc_rx_ba_sessions--; + splx(s); + break; + case IWM_ADD_STA_IMMEDIATE_BA_FAILURE: + ret = EIO; + DPRINTF(("RX BA Session refused by fw\n")); + break; + default: + ret = EIO; + DPRINTF(("RX BA Session failed %sing, status 0x%x\n", + start ? "start" : "stopp", status)); + break; } } +void +iwm_ba_task(void *arg) +{ + struct iwm_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = ic->ic_bss; + + if (sc->ba_start) + iwm_mvm_sta_rx_agg(sc, ni, sc->ba_tid, sc->ba_ssn, 1); + else + iwm_mvm_sta_rx_agg(sc, ni, sc->ba_tid, 0, 0); +} + +/* + * This function is called by upper layer when an ADDBA request is received + * from another STA and before the ADDBA response is sent. + */ +int +iwm_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; + struct iwm_softc *sc = IC2IFP(ic)->if_softc; + + if (sc->sc_rx_ba_sessions >= IWM_MAX_RX_BA_SESSIONS) + return ENOSPC; + + sc->ba_start = 1; + sc->ba_tid = tid; + sc->ba_ssn = htole16(ba->ba_winstart); + task_add(systq, &sc->ba_task); + + return 0; /* XXX firmware may still fail to add BA agreement... */ +} + +/* + * This function is called by upper layer on teardown of an HT-immediate + * Block Ack agreement (eg. uppon receipt of a DELBA frame). + */ +void +iwm_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, + uint8_t tid) +{ + struct iwm_softc *sc = IC2IFP(ic)->if_softc; + + sc->ba_start = 0; + sc->ba_tid = tid; + task_add(systq, &sc->ba_task); +} +#endif /* IEEE80211_NO_HT */ + int iwm_parse_nvm_data(struct iwm_softc *sc, const uint16_t *nvm_hw, const uint16_t *nvm_sw, @@ -2502,7 +2680,13 @@ iwm_parse_nvm_data(struct iwm_softc *sc, sku = le16_to_cpup(nvm_sw + IWM_SKU); data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; +#ifndef IEEE80211_NO_HT + data->sku_cap_11n_enable = sku & IWM_NVM_SKU_CAP_11N_ENABLE; + data->sku_cap_mimo_disable = sku & IWM_NVM_SKU_CAP_MIMO_DISABLE; +#else data->sku_cap_11n_enable = 0; + data->sku_cap_mimo_disable = 1; +#endif if (!data->valid_tx_ant || !data->valid_rx_ant) { DPRINTF(("%s: invalid antennas (0x%x, 0x%x)\n", @@ -2544,13 +2728,6 @@ struct iwm_nvm_section { const uint8_t *data; }; -#define IWM_FW_VALID_TX_ANT(sc) \ - ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) \ - >> IWM_FW_PHY_CFG_TX_CHAIN_POS) -#define IWM_FW_VALID_RX_ANT(sc) \ - ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) \ - >> IWM_FW_PHY_CFG_RX_CHAIN_POS) - int iwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) { @@ -3075,23 +3252,35 @@ iwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, tap->wr_dbm_antsignal = (int8_t)rssi; tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; tap->wr_tsft = phy_info->system_timestamp; - switch (phy_info->rate) { - /* CCK rates. */ - case 10: tap->wr_rate = 2; break; - case 20: tap->wr_rate = 4; break; - case 55: tap->wr_rate = 11; break; - case 110: tap->wr_rate = 22; break; - /* OFDM rates. */ - case 0xd: tap->wr_rate = 12; break; - case 0xf: tap->wr_rate = 18; break; - case 0x5: tap->wr_rate = 24; break; - case 0x7: tap->wr_rate = 36; break; - case 0x9: tap->wr_rate = 48; break; - case 0xb: tap->wr_rate = 72; break; - case 0x1: tap->wr_rate = 96; break; - case 0x3: tap->wr_rate = 108; break; - /* Unknown rate: should not happen. */ - default: tap->wr_rate = 0; + if (phy_info->phy_flags & + htole16(IWM_RX_RES_PHY_FLAGS_OFDM_HT)) { +#ifdef notyet + uint8_t mcs = (phy_info->rate_n_flags & + htole32(IWM_RATE_HT_MCS_RATE_CODE_MSK)); +#endif + /* XXX need a way to pass current MCS in 11n mode */ + tap->wr_rate = 0; + } else { + uint8_t rate = (phy_info->rate_n_flags & + htole32(IWM_RATE_LEGACY_RATE_MSK)); + switch (rate) { + /* CCK rates. */ + case 10: tap->wr_rate = 2; break; + case 20: tap->wr_rate = 4; break; + case 55: tap->wr_rate = 11; break; + case 110: tap->wr_rate = 22; break; + /* OFDM rates. */ + case 0xd: tap->wr_rate = 12; break; + case 0xf: tap->wr_rate = 18; break; + case 0x5: tap->wr_rate = 24; break; + case 0x7: tap->wr_rate = 36; break; + case 0x9: tap->wr_rate = 48; break; + case 0xb: tap->wr_rate = 72; break; + case 0x1: tap->wr_rate = 96; break; + case 0x3: tap->wr_rate = 108; break; + /* Unknown rate: should not happen. */ + default: tap->wr_rate = 0; + } } mb.m_data = (caddr_t)tap; @@ -3660,7 +3849,7 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, st struct ieee80211_node *ni = &in->in_ni; const struct iwm_rate *rinfo; int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - int ridx, rate_flags; + int ridx, rate_flags, i; int nrates = ni->ni_rates.rs_nrates; tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; @@ -3668,16 +3857,40 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, st if (type != IEEE80211_FC0_TYPE_DATA) { /* for non-data, use the lowest supported rate */ - ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + ridx = (ic->ic_curmode == IEEE80211_MODE_11A || + ic->ic_curmode == IEEE80211_MODE_11N) ? IWM_RIDX_OFDM : IWM_RIDX_CCK; +#ifndef IEEE80211_NO_HT + } else if (ic->ic_fixed_mcs != -1) { + ridx = sc->sc_fixed_ridx; +#endif } else if (ic->ic_fixed_rate != -1) { ridx = sc->sc_fixed_ridx; } else { /* for data frames, use RS table */ - tx->initial_rate_index = (nrates - 1) - ni->ni_txrate; +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) { + tx->initial_rate_index = + (nitems(iwm_mcs2ridx) - 1) - ni->ni_txmcs; + } else +#endif + tx->initial_rate_index = (nrates - 1) - ni->ni_txrate; tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); DPRINTFN(12, ("start with txrate %d\n", tx->initial_rate_index)); - ridx = in->in_ridx[ni->ni_txrate]; +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) { + ridx = iwm_mcs2ridx[ni->ni_txmcs]; + return &iwm_rates[ridx]; + } +#endif + ridx = 0; + for (i = 0; i < nrates; i++) { + if (iwm_rates[i].rate == (ni->ni_txrate & + IEEE80211_RATE_VAL)) { + ridx = i; + break; + } + } return &iwm_rates[ridx]; } @@ -3685,7 +3898,14 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, st rate_flags = 1 << IWM_RATE_MCS_ANT_POS; if (IWM_RIDX_IS_CCK(ridx)) rate_flags |= IWM_RATE_MCS_CCK_MSK; - tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); +#ifndef IEEE80211_NO_HT + if ((ni->ni_flags & IEEE80211_NODE_HT) && + rinfo->ht_plcp != IWM_RATE_HT_SISO_MCS_INV_PLCP) { + rate_flags |= IWM_RATE_MCS_HT_MSK; + tx->rate_n_flags = htole32(rate_flags | rinfo->ht_plcp); + } else +#endif + tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); return rinfo; } @@ -3750,7 +3970,11 @@ iwm_tx(struct iwm_softc *sc, struct mbuf tap->wt_flags = 0; tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); - tap->wt_rate = rinfo->rate; + if (rinfo->plcp == IWM_RATE_INVM_PLCP) { + /* XXX need a way to pass current MCS in 11n mode */ + tap->wt_rate = 0; + } else + tap->wt_rate = rinfo->rate; tap->wt_hwqueue = ac; if ((ic->ic_flags & IEEE80211_F_WEPON) && (wh->i_fc[1] & IEEE80211_FC1_PROTECTED)) @@ -4746,6 +4970,30 @@ iwm_mvm_mac_ctxt_cmd_common(struct iwm_s cmd->ac[txf].edca_txop = 0; } +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) { + enum ieee80211_htprot htprot = + (ni->ni_htop1 & IEEE80211_HTOP1_PROT_MASK); + switch (htprot) { + case IEEE80211_HTPROT_NONE: + break; + case IEEE80211_HTPROT_NONMEMBER: + case IEEE80211_HTPROT_NONHT_MIXED: + cmd->protection_flags |= + htole32(IWM_MAC_PROT_FLG_HT_PROT); + case IEEE80211_HTPROT_20MHZ: + cmd->protection_flags |= + htole32(IWM_MAC_PROT_FLG_HT_PROT | + IWM_MAC_PROT_FLG_FAT_PROT); + break; + default: + DPRINTF(("Unknown protection mode %d\n", htprot)); + break; + } + + cmd->qos_flags |= htole32(IWM_MAC_QOS_FLG_TGN); + } +#endif if (ic->ic_flags & IEEE80211_F_USEPROT) cmd->protection_flags |= htole32(IWM_MAC_PROT_FLG_TGG_PROTECT); @@ -5118,8 +5366,11 @@ iwm_calib_timeout(void *arg) int s; s = splnet(); - if (ic->ic_fixed_rate == -1 - && ic->ic_opmode == IEEE80211_M_STA + if ((ic->ic_fixed_rate == -1 +#ifndef IEEE80211_NO_HT + || ic->ic_fixed_mcs == -1 +#endif + ) && ic->ic_opmode == IEEE80211_M_STA && ic->ic_bss) { struct iwm_node *in = (void *)ic->ic_bss; ieee80211_amrr_choose(&sc->sc_amrr, &in->in_ni, &in->in_amn); @@ -5136,77 +5387,92 @@ iwm_setrates(struct iwm_node *in) struct ieee80211com *ic = ni->ni_ic; struct iwm_softc *sc = IC2IFP(ic)->if_softc; struct iwm_lq_cmd *lq = &in->in_lq; - int nrates = ni->ni_rates.rs_nrates; - int i, ridx, tab = 0; - int txant = 0; - - if (nrates > nitems(lq->rs_table)) { - DPRINTF(("%s: node supports %d rates, driver handles " - "only %zu\n", DEVNAME(sc), nrates, nitems(lq->rs_table))); - return; - } - - /* first figure out which rates we should support */ - memset(&in->in_ridx, -1, sizeof(in->in_ridx)); - for (i = 0; i < nrates; i++) { - int rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL; - - /* Map 802.11 rate to HW rate index. */ - for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) - if (iwm_rates[ridx].rate == rate) + struct ieee80211_rateset *rs = &ni->ni_rates; + int i, ridx, j, tab = 0; +#ifndef IEEE80211_NO_HT + int maxmcs = 0; + + /* Figure out which HT rates we should support. */ + if (ni->ni_flags & IEEE80211_NODE_HT) { + for (i = 0; i < IEEE80211_HT_NUM_MCS; i++) { + if (isclr(ic->ic_sup_mcs, i)) break; - if (ridx > IWM_RIDX_MAX) - DPRINTF(("%s: WARNING: device rate for %d not found!\n", - DEVNAME(sc), rate)); - else - in->in_ridx[i] = ridx; - } - - /* then construct a lq_cmd based on those */ + if (isset(ni->ni_rxmcs, i)) + maxmcs = i; + } + } +#endif memset(lq, 0, sizeof(*lq)); lq->sta_id = IWM_STATION_ID; /* - * are these used? (we don't do SISO or MIMO) - * need to set them to non-zero, though, or we get an error. + * Fill the LQ rate selection table with legacy and/or HT rates + * in descending order, i.e. with the highest rate first. + * In cases where throughput of an HT rate corresponds to a legacy + * rate it makes no sense to add both. We rely on the fact that + * iwm_rates is laid out such that equivalent HT/legacy rates share + * the same IWM_RATE_*_INDEX value. Also, rates not applicable to + * legacy/HT are assumed to be marked with an 'invalid' PLCP value. */ - lq->single_stream_ant_msk = 1; - lq->dual_stream_ant_msk = 1; + j = 0; + for (ridx = IWM_RIDX_MAX; ridx >= 0; ridx--) { + if (j >= nitems(lq->rs_table)) + break; + tab = 0; +#ifndef IEEE80211_NO_HT + if ((ni->ni_flags & IEEE80211_NODE_HT) && + iwm_rates[ridx].ht_plcp != IWM_RATE_HT_SISO_MCS_INV_PLCP) { + for (i = 0; i <= maxmcs; i++) { + if (isclr(ni->ni_rxmcs, i)) + continue; + if (ridx == iwm_mcs2ridx[i]) { + tab = iwm_rates[ridx].ht_plcp; + tab |= IWM_RATE_MCS_HT_MSK; + break; + } + } + } +#endif + if (tab == 0 && iwm_rates[ridx].plcp != IWM_RATE_INVM_PLCP) { + for (i = 0; i < rs->rs_nrates; i++) { + if (iwm_rates[ridx].rate == (rs->rs_rates[i] & + IEEE80211_RATE_VAL)) { + tab = iwm_rates[ridx].plcp; + break; + } + } + } - /* - * Build the actual rate selection table. - * The lowest bits are the rates. Additionally, - * CCK needs bit 9 to be set. The rest of the bits - * we add to the table select the tx antenna - * Note that we add the rates in the highest rate first - * (opposite of ni_rates). - */ - for (i = 0; i < nrates; i++) { - int nextant; + if (tab == 0) + continue; - if (txant == 0) - txant = IWM_FW_VALID_TX_ANT(sc); - nextant = 1<<(ffs(txant)-1); - txant &= ~nextant; - - ridx = in->in_ridx[(nrates-1)-i]; - tab = iwm_rates[ridx].plcp; - tab |= nextant << IWM_RATE_MCS_ANT_POS; + tab |= 1 << IWM_RATE_MCS_ANT_POS; if (IWM_RIDX_IS_CCK(ridx)) tab |= IWM_RATE_MCS_CCK_MSK; - DPRINTFN(2, ("station rate %d %x\n", i, tab)); - lq->rs_table[i] = htole32(tab); - } - /* then fill the rest with the lowest possible rate */ - for (i = nrates; i < nitems(lq->rs_table); i++) { - KASSERT(tab != 0); - lq->rs_table[i] = htole32(tab); + DPRINTFN(2, ("station rate %d %x\n", j, tab)); + lq->rs_table[j++] = htole32(tab); } + /* Fill the rest with the lowest possible rate */ + i = j > 0 ? j - 1 : 0; + while (j < nitems(lq->rs_table)) + lq->rs_table[j++] = lq->rs_table[i]; + + lq->single_stream_ant_msk = IWM_ANT_A; + lq->dual_stream_ant_msk = IWM_ANT_AB; + /* init amrr */ ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn); /* Start at lowest available bit-rate, AMRR will raise. */ ni->ni_txrate = 0; + +#ifndef IEEE80211_NO_HT + /* + * XXX AMRR doesn't seem to raise properly in HT mode. + * For now, start with the highest rate and let AMRR drop down. + */ + ni->ni_txmcs = 7; +#endif } int @@ -5221,6 +5487,11 @@ iwm_media_change(struct ifnet *ifp) if (error != ENETRESET) return error; +#ifndef IEEE80211_NO_HT + if (ic->ic_fixed_mcs != -1) + sc->sc_fixed_ridx = iwm_mcs2ridx[ic->ic_fixed_mcs]; + else +#endif if (ic->ic_fixed_rate != -1) { rate = ic->ic_sup_rates[ic->ic_curmode]. rs_rates[ic->ic_fixed_rate] & IEEE80211_RATE_VAL; @@ -5630,6 +5901,9 @@ iwm_stop(struct ifnet *ifp, int disable) task_del(systq, &sc->init_task); task_del(sc->sc_nswq, &sc->newstate_task); task_del(sc->sc_eswq, &sc->sc_eswk); +#ifndef IEEE80211_NO_HT + task_del(systq, &sc->ba_task); +#endif sc->sc_newstate(ic, IEEE80211_S_INIT, -1); timeout_del(&sc->sc_calib_to); @@ -6315,6 +6589,10 @@ iwm_preinit(struct iwm_softc *sc) IWM_UCODE_API(sc->sc_fwver), ether_sprintf(sc->sc_nvm.hw_addr)); +#ifndef IEEE80211_NO_HT + if (sc->sc_nvm.sku_cap_11n_enable) + iwm_setup_ht_rates(sc); +#endif /* not all hardware can do 5GHz band */ if (!sc->sc_nvm.sku_cap_band_52GHz_enable) memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0, @@ -6334,6 +6612,14 @@ iwm_preinit(struct iwm_softc *sc) /* Override 802.11 state transition machine. */ sc->sc_newstate = ic->ic_newstate; ic->ic_newstate = iwm_newstate; +#ifndef IEEE80211_NO_HT + ic->ic_ampdu_rx_start = iwm_ampdu_rx_start; + ic->ic_ampdu_rx_stop = iwm_ampdu_rx_stop; +#ifdef notyet + ic->ic_ampdu_tx_start = iwm_ampdu_tx_start; + ic->ic_ampdu_tx_stop = iwm_ampdu_tx_stop; +#endif +#endif ieee80211_media_init(ifp, iwm_media_change, ieee80211_media_status); return 0; @@ -6519,6 +6805,14 @@ iwm_attach(struct device *parent, struct IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_SHPREAMBLE; /* short preamble supported */ +#ifndef IEEE80211_NO_HT + /* No optional HT features supported for now, */ + ic->ic_htcaps = 0; + ic->ic_htxcaps = 0; + ic->ic_txbfcaps = 0; + ic->ic_aselcaps = 0; +#endif + 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; @@ -6555,6 +6849,9 @@ iwm_attach(struct device *parent, struct timeout_set(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc); task_set(&sc->init_task, iwm_init_task, sc); task_set(&sc->newstate_task, iwm_newstate_task, sc); +#ifndef IEEE80211_NO_HT + task_set(&sc->ba_task, iwm_ba_task, sc); +#endif /* * We cannot read the MAC address without loading the Index: dev/pci/if_iwmreg.h =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwmreg.h,v retrieving revision 1.8 diff -u -p -r1.8 if_iwmreg.h --- dev/pci/if_iwmreg.h 10 Dec 2015 17:34:14 -0000 1.8 +++ dev/pci/if_iwmreg.h 11 Dec 2015 22:48:52 -0000 @@ -2398,9 +2398,7 @@ struct iwm_rx_phy_info { #define IWM_PHY_INFO_FLAG_SHPREAMBLE (1 << 2) uint16_t channel; uint32_t non_cfg_phy[IWM_RX_INFO_PHY_CNT]; - uint8_t rate; - uint8_t rflags; - uint16_t xrflags; + uint32_t rate_n_flags; uint32_t byte_count; uint16_t mac_active_msk; uint16_t frame_time; @@ -3461,10 +3459,56 @@ struct iwm_beacon_filter_cmd { .bf_escape_timer = htole32(IWM_BF_ESCAPE_TIMER_DEFAULT), \ .ba_escape_timer = htole32(IWM_BA_ESCAPE_TIMER_DEFAULT) +/* uCode API values for HT/VHT bit rates */ +enum { + IWM_RATE_HT_SISO_MCS_0_PLCP = 0, + IWM_RATE_HT_SISO_MCS_1_PLCP = 1, + IWM_RATE_HT_SISO_MCS_2_PLCP = 2, + IWM_RATE_HT_SISO_MCS_3_PLCP = 3, + IWM_RATE_HT_SISO_MCS_4_PLCP = 4, + IWM_RATE_HT_SISO_MCS_5_PLCP = 5, + IWM_RATE_HT_SISO_MCS_6_PLCP = 6, + IWM_RATE_HT_SISO_MCS_7_PLCP = 7, + IWM_RATE_HT_MIMO2_MCS_0_PLCP = 0x8, + IWM_RATE_HT_MIMO2_MCS_1_PLCP = 0x9, + IWM_RATE_HT_MIMO2_MCS_2_PLCP = 0xA, + IWM_RATE_HT_MIMO2_MCS_3_PLCP = 0xB, + IWM_RATE_HT_MIMO2_MCS_4_PLCP = 0xC, + IWM_RATE_HT_MIMO2_MCS_5_PLCP = 0xD, + IWM_RATE_HT_MIMO2_MCS_6_PLCP = 0xE, + IWM_RATE_HT_MIMO2_MCS_7_PLCP = 0xF, + IWM_RATE_VHT_SISO_MCS_0_PLCP = 0, + IWM_RATE_VHT_SISO_MCS_1_PLCP = 1, + IWM_RATE_VHT_SISO_MCS_2_PLCP = 2, + IWM_RATE_VHT_SISO_MCS_3_PLCP = 3, + IWM_RATE_VHT_SISO_MCS_4_PLCP = 4, + IWM_RATE_VHT_SISO_MCS_5_PLCP = 5, + IWM_RATE_VHT_SISO_MCS_6_PLCP = 6, + IWM_RATE_VHT_SISO_MCS_7_PLCP = 7, + IWM_RATE_VHT_SISO_MCS_8_PLCP = 8, + IWM_RATE_VHT_SISO_MCS_9_PLCP = 9, + IWM_RATE_VHT_MIMO2_MCS_0_PLCP = 0x10, + IWM_RATE_VHT_MIMO2_MCS_1_PLCP = 0x11, + IWM_RATE_VHT_MIMO2_MCS_2_PLCP = 0x12, + IWM_RATE_VHT_MIMO2_MCS_3_PLCP = 0x13, + IWM_RATE_VHT_MIMO2_MCS_4_PLCP = 0x14, + IWM_RATE_VHT_MIMO2_MCS_5_PLCP = 0x15, + IWM_RATE_VHT_MIMO2_MCS_6_PLCP = 0x16, + IWM_RATE_VHT_MIMO2_MCS_7_PLCP = 0x17, + IWM_RATE_VHT_MIMO2_MCS_8_PLCP = 0x18, + IWM_RATE_VHT_MIMO2_MCS_9_PLCP = 0x19, + IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_HT_MIMO2_MCS_INV_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_VHT_SISO_MCS_INV_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_VHT_MIMO2_MCS_INV_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_HT_SISO_MCS_8_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_HT_SISO_MCS_9_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_HT_MIMO2_MCS_8_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, + IWM_RATE_HT_MIMO2_MCS_9_PLCP = IWM_RATE_HT_SISO_MCS_INV_PLCP, +}; + /* - * These serve as indexes into - * struct iwm_rate_info fw_rate_idx_to_plcp[IWM_RATE_COUNT]; - * TODO: avoid overlap between legacy and HT rates + * These serve as indexes into struct iwm_rate iwm_rates[IWM_RIDX_MAX]. */ enum { IWM_RATE_1M_INDEX = 0, @@ -3518,7 +3562,7 @@ enum { IWM_RATE_2M_PLCP = 20, IWM_RATE_5M_PLCP = 55, IWM_RATE_11M_PLCP = 110, - IWM_RATE_INVM_PLCP = -1, + IWM_RATE_INVM_PLCP = 0xff, }; /* Index: dev/pci/if_iwmvar.h =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v retrieving revision 1.13 diff -u -p -r1.13 if_iwmvar.h --- dev/pci/if_iwmvar.h 8 Dec 2015 17:10:02 -0000 1.13 +++ dev/pci/if_iwmvar.h 11 Dec 2015 22:48:52 -0000 @@ -191,6 +191,7 @@ struct iwm_nvm_data { int sku_cap_11n_enable; int sku_cap_amt_enable; int sku_cap_ipan_enable; + int sku_cap_mimo_disable; uint8_t radio_cfg_type; uint8_t radio_cfg_step; @@ -368,6 +369,14 @@ struct iwm_softc { enum ieee80211_state ns_nstate; int ns_arg; +#ifndef IEEE80211_NO_HT + /* Task for firmware BlockAck setup/teardown and its arguments. */ + struct task ba_task; + int ba_start; + int ba_tid; + uint16_t ba_ssn; +#endif + bus_space_tag_t sc_st; bus_space_handle_t sc_sh; bus_size_t sc_sz; @@ -435,6 +444,9 @@ struct iwm_softc { struct iwm_bf_data sc_bf; int sc_tx_timer; +#ifndef IEEE80211_NO_HT + int sc_rx_ba_sessions; +#endif struct iwm_scan_cmd *sc_scan_cmd; size_t sc_scan_cmd_len; @@ -497,8 +509,6 @@ struct iwm_node { struct iwm_lq_cmd in_lq; struct ieee80211_amrr_node in_amn; - - uint8_t in_ridx[IEEE80211_RATE_MAXSIZE]; }; #define IWM_STATION_ID 0 Index: net80211/ieee80211_input.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v retrieving revision 1.142 diff -u -p -r1.142 ieee80211_input.c --- net80211/ieee80211_input.c 15 Nov 2015 11:14:17 -0000 1.142 +++ net80211/ieee80211_input.c 11 Dec 2015 22:43:24 -0000 @@ -280,6 +280,43 @@ ieee80211_input(struct ifnet *ifp, struc tid = 0; } +#ifndef IEEE80211_NO_HT + if (type == IEEE80211_FC0_TYPE_DATA && hasqos && + !(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE)) { + int ba_state = ni->ni_rx_ba[tid].ba_state; + + /* + * If Block Ack was explicitly requested, check + * if we have a BA agreement for this RA/TID. + */ + if ((qos & IEEE80211_QOS_ACK_POLICY_MASK) == + IEEE80211_QOS_ACK_POLICY_BA && + ba_state != IEEE80211_BA_AGREED) { + DPRINTF(("no BA agreement for %s, TID %d\n", + ether_sprintf(ni->ni_macaddr), tid)); + /* send a DELBA with reason code UNKNOWN-BA */ + IEEE80211_SEND_ACTION(ic, ni, + IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA, + IEEE80211_REASON_SETUP_REQUIRED << 16 | tid); + goto err; + } + + /* + * Check if we have an explicit or implicit + * Block Ack Request for a valid BA agreement. + */ + if (ba_state == IEEE80211_BA_AGREED && + ((qos & IEEE80211_QOS_ACK_POLICY_MASK) == + IEEE80211_QOS_ACK_POLICY_BA || + (qos & IEEE80211_QOS_ACK_POLICY_MASK) == + IEEE80211_QOS_ACK_POLICY_NORMAL)) { + /* go through A-MPDU reordering */ + ieee80211_input_ba(ifp, m, ni, tid, rxi); + return; /* don't free m! */ + } + } +#endif + /* duplicate detection (see 9.2.9) */ if (ieee80211_has_seq(wh) && ic->ic_state != IEEE80211_S_SCAN) { @@ -430,27 +467,6 @@ ieee80211_input(struct ifnet *ifp, struc goto out; } -#ifndef IEEE80211_NO_HT - if (!(rxi->rxi_flags & IEEE80211_RXI_AMPDU_DONE) && - hasqos && (qos & IEEE80211_QOS_ACK_POLICY_MASK) == - IEEE80211_QOS_ACK_POLICY_BA) { - /* check if we have a BA agreement for this RA/TID */ - if (ni->ni_rx_ba[tid].ba_state != - IEEE80211_BA_AGREED) { - DPRINTF(("no BA agreement for %s, TID %d\n", - ether_sprintf(ni->ni_macaddr), tid)); - /* send a DELBA with reason code UNKNOWN-BA */ - IEEE80211_SEND_ACTION(ic, ni, - IEEE80211_CATEG_BA, IEEE80211_ACTION_DELBA, - IEEE80211_REASON_SETUP_REQUIRED << 16 | - tid); - goto err; - } - /* go through A-MPDU reordering */ - ieee80211_input_ba(ifp, m, ni, tid, rxi); - return; /* don't free m! */ - } -#endif if ((ic->ic_flags & IEEE80211_F_WEPON) || ((ic->ic_flags & IEEE80211_F_RSNON) && (ni->ni_flags & IEEE80211_NODE_RXPROT))) { @@ -1046,6 +1062,14 @@ ieee80211_amsdu_decap(struct ieee80211co } len += ETHER_HDR_LEN; + if (len > m->m_pkthdr.len) { + /* stop processing A-MSDU subframes */ + DPRINTF(("A-MSDU subframe too long (%d)\n", len)); + ic->ic_stats.is_rx_decap++; + m_freem(m); + break; + } + /* "detach" our A-MSDU subframe from the others */ n = m_split(m, len, M_NOWAIT); if (n == NULL) { @@ -1056,8 +1080,12 @@ ieee80211_amsdu_decap(struct ieee80211co } ieee80211_deliver_data(ic, m, ni); + if (n->m_len == 0) { + m_freem(n); + break; + } m = n; - /* remove padding */ + /* remove padding (last subframe has no padding) */ pad = ((len + 3) & ~3) - len; m_adj(m, pad); } @@ -1569,10 +1597,13 @@ ieee80211_recv_probe_resp(struct ieee802 */ if (ni->ni_flags & IEEE80211_NODE_QOS) { /* always prefer EDCA IE over Wi-Fi Alliance WMM IE */ - if (edcaie != NULL) - ieee80211_parse_edca_params(ic, edcaie); - else if (wmmie != NULL) - ieee80211_parse_wmm_params(ic, wmmie); + if ((edcaie != NULL && + ieee80211_parse_edca_params(ic, edcaie) == 0) || + (wmmie != NULL && + ieee80211_parse_wmm_params(ic, wmmie) == 0)) + ni->ni_flags |= IEEE80211_NODE_QOS; + else + ni->ni_flags &= ~IEEE80211_NODE_QOS; } if (ic->ic_state == IEEE80211_S_SCAN @@ -2177,7 +2208,7 @@ ieee80211_recv_assoc_resp(struct ieee802 break; } if (memcmp(frm + 2, MICROSOFT_OUI, 3) == 0) { - if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 1) + if (frm[1] >= 5 && frm[5] == 2 && frm[6] == 0) wmmie = frm; } break; @@ -2449,6 +2480,7 @@ ieee80211_recv_addba_req(struct ieee8021 ba->ba_timeout_val = IEEE80211_BA_MIN_TIMEOUT; else if (ba->ba_timeout_val > IEEE80211_BA_MAX_TIMEOUT) ba->ba_timeout_val = IEEE80211_BA_MAX_TIMEOUT; + ba->ba_ni = ni; timeout_set(&ba->ba_to, ieee80211_rx_ba_timeout, ba); ba->ba_winsize = bufsz; if (ba->ba_winsize == 0 || ba->ba_winsize > IEEE80211_BA_MAX_WINSZ) @@ -2465,7 +2497,7 @@ ieee80211_recv_addba_req(struct ieee8021 ba->ba_head = 0; /* notify drivers of this new Block Ack agreement */ - if (ic->ic_ampdu_rx_start != NULL && + if (ic->ic_ampdu_rx_start == NULL || ic->ic_ampdu_rx_start(ic, ni, tid) != 0) { /* driver failed to setup, rollback */ free(ba->ba_buf, M_DEVBUF, 0); Index: net80211/ieee80211_node.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v retrieving revision 1.92 diff -u -p -r1.92 ieee80211_node.c --- net80211/ieee80211_node.c 24 Nov 2015 13:45:06 -0000 1.92 +++ net80211/ieee80211_node.c 3 Dec 2015 12:24:16 -0000 @@ -68,6 +68,9 @@ u_int8_t ieee80211_node_getrssi(struct i void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *, const u_int8_t *); void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); +#ifndef IEEE80211_NO_HT +void ieee80211_ba_del(struct ieee80211_node *); +#endif struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *); void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *); void ieee80211_needs_auth(struct ieee80211com *, struct ieee80211_node *); @@ -757,6 +760,9 @@ ieee80211_node_cleanup(struct ieee80211c free(ni->ni_rsnie, M_DEVBUF, 0); ni->ni_rsnie = NULL; } +#ifndef IEEE80211_NO_HT + ieee80211_ba_del(ni); +#endif } void @@ -1065,6 +1071,32 @@ ieee80211_find_node_for_beacon(struct ie return (keep); } +#ifndef IEEE80211_NO_HT +void +ieee80211_ba_del(struct ieee80211_node *ni) +{ + int tid; + + for (tid = 0; tid < nitems(ni->ni_rx_ba); tid++) { + struct ieee80211_rx_ba *ba = &ni->ni_rx_ba[tid]; + if (ba->ba_state == IEEE80211_BA_AGREED) { + if (timeout_pending(&ba->ba_to)) + timeout_del(&ba->ba_to); + ba->ba_state = IEEE80211_BA_INIT; + } + } + + for (tid = 0; tid < nitems(ni->ni_tx_ba); tid++) { + struct ieee80211_tx_ba *ba = &ni->ni_tx_ba[tid]; + if (ba->ba_state == IEEE80211_BA_AGREED) { + if (timeout_pending(&ba->ba_to)) + timeout_del(&ba->ba_to); + ba->ba_state = IEEE80211_BA_INIT; + } + } +} +#endif + void ieee80211_free_node(struct ieee80211com *ic, struct ieee80211_node *ni) { @@ -1078,6 +1110,9 @@ ieee80211_free_node(struct ieee80211com timeout_del(&ni->ni_eapol_to); timeout_del(&ni->ni_sa_query_to); IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); +#endif +#ifndef IEEE80211_NO_HT + ieee80211_ba_del(ni); #endif RB_REMOVE(ieee80211_tree, &ic->ic_tree, ni); ic->ic_nnodes--; Index: net80211/ieee80211_node.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v retrieving revision 1.49 diff -u -p -r1.49 ieee80211_node.h --- net80211/ieee80211_node.h 15 Nov 2015 12:34:07 -0000 1.49 +++ net80211/ieee80211_node.h 15 Nov 2015 13:06:28 -0000 @@ -112,8 +112,8 @@ struct ieee80211_tx_ba { struct ieee80211_node *ba_ni; /* backpointer for callbacks */ struct timeout ba_to; int ba_timeout_val; -#define IEEE80211_BA_MIN_TIMEOUT (10 * 1000) /* 10msec */ -#define IEEE80211_BA_MAX_TIMEOUT (10 * 1000 * 1000) /* 10sec */ +#define IEEE80211_BA_MIN_TIMEOUT (10 * 1000 * 1000) /* 10 sec */ +#define IEEE80211_BA_MAX_TIMEOUT (60 * 1000 * 1000) /* 60 sec */ int ba_state; #define IEEE80211_BA_INIT 0 Index: net80211/ieee80211_output.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_output.c,v retrieving revision 1.101 diff -u -p -r1.101 ieee80211_output.c --- net80211/ieee80211_output.c 24 Nov 2015 12:32:53 -0000 1.101 +++ net80211/ieee80211_output.c 10 Dec 2015 14:00:34 -0000 @@ -832,6 +832,25 @@ ieee80211_add_qos_capability(u_int8_t *f } /* + * Add a Wifi-Alliance WME (aka WMM) info element to a frame. + * WME is a requirement for Wifi-Alliance compliance and some + * 11n APs will not negotiate HT if this element is missing. + */ +u_int8_t * +ieee80211_add_wme_info(u_int8_t *frm, struct ieee80211com *ic) +{ + *frm++ = IEEE80211_ELEMID_VENDOR; + *frm++ = 7; + memcpy(frm, MICROSOFT_OUI, 3); frm += 3; + *frm++ = 2; /* OUI type */ + *frm++ = 0; /* OUI subtype */ + *frm++ = 1; /* version */ + *frm++ = 0; /* info */ + + return frm; +} + +/* * Add an RSN element to a frame (see 802.11-2012 8.4.2.27) */ u_int8_t * @@ -1097,7 +1116,7 @@ ieee80211_get_probe_req(struct ieee80211 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + ((rs->rs_nrates > IEEE80211_RATE_SIZE) ? 2 + rs->rs_nrates - IEEE80211_RATE_SIZE : 0) + - ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0)); + ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 9 : 0)); if (m == NULL) return NULL; @@ -1107,8 +1126,10 @@ ieee80211_get_probe_req(struct ieee80211 if (rs->rs_nrates > IEEE80211_RATE_SIZE) frm = ieee80211_add_xrates(frm, rs); #ifndef IEEE80211_NO_HT - if (ni->ni_flags & IEEE80211_NODE_HT) + if (ic->ic_flags & IEEE80211_F_HTON) { frm = ieee80211_add_htcaps(frm, ic); + frm = ieee80211_add_wme_info(frm, ic); + } #endif m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); @@ -1278,7 +1299,7 @@ ieee80211_get_assoc_req(struct ieee80211 (((ic->ic_flags & IEEE80211_F_RSNON) && (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) ? 2 + IEEE80211_WPAIE_MAXLEN : 0) + - ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 : 0)); + ((ni->ni_flags & IEEE80211_NODE_HT) ? 28 + 9 : 0)); if (m == NULL) return NULL; @@ -1310,8 +1331,10 @@ ieee80211_get_assoc_req(struct ieee80211 (ni->ni_rsnprotos & IEEE80211_PROTO_WPA)) frm = ieee80211_add_wpa(frm, ic, ni); #ifndef IEEE80211_NO_HT - if (ni->ni_flags & IEEE80211_NODE_HT) + if (ic->ic_flags & IEEE80211_F_HTON) { frm = ieee80211_add_htcaps(frm, ic); + frm = ieee80211_add_wme_info(frm, ic); + } #endif m->m_pkthdr.len = m->m_len = frm - mtod(m, u_int8_t *); @@ -1370,7 +1393,7 @@ ieee80211_get_assoc_resp(struct ieee8021 frm = ieee80211_add_tie(frm, 3, 1000 /* XXX */); } #ifndef IEEE80211_NO_HT - if (ni->ni_flags & IEEE80211_NODE_HT) { + if (ic->ic_flags & IEEE80211_F_HTON) { frm = ieee80211_add_htcaps(frm, ic); frm = ieee80211_add_htop(frm, ic); }