This diff adds an initial implementation of 802.11n. It is functional but mostly adds foundations to build future work on top of. It completes the little bits of 11n code added by damien@ years ago. It supports all non-optional 11n features for STA (client) operation.
It adds 11n support only for the iwm(4) driver. Support for more drivers will be added later. - Add a new '11n' mode with all the ifmedia goo that goes with it. This is straightforward because the ifmedia word was extended to 64 bits back in September during the l2k15 hackathon in preparation for 11n. - Add a concept of MCS (modulation coding scheme) alongside the "rates" used in 11a/b/g. My initial plan was to extend the ieee80211_rateset structure until I noticed this struct is copied out verbatim into frames during 11a/b/g operation. So changing its size is not an option and I'm adding MCS as a new concept which exists only in 11n mode. The mandatory MCS 0-7 are supported, providing a maximum theoretical data rate of 65Mbit/s. - Add more 11n related state to various data structures, most notably ieee80211_node and ieee80211com. - Add support for block ack agreements and receiving A-MPDUs and A-MSDUs. This mostly builds on code which damien@ added years ago. I'm very happy he left a complete implementation for me to find. Sending A-MPDUs is left for later (the sender has to initiate block ack, and since we don't our 11n peers won't care, so no problem). The 11n standard adds a lot of new features, most of which are optional. The optional 11n features which this diff does not add support for include: - MIMO (MCS 8 and above) - Short guard interval aka SGI (improves throughput) - 40 MHz wide channels I've been testing against OpenWRT and Fritzbox APs based on the ath10k chipset. Testing against all sorts of APs is much appreciated. Apart from 11n operation I've been testing 11a/b/g operation as well. Everything seems to work. The diff has survived a full 'make release' + install cycle on amd64. Most development and testing was done on i386. To test this diff, apply it to /usr/src/sys, compile a new kernel and reboot into it. Then install the new if_media.h header and recompile ifconfig: doas install -m 444 -o root -g bin /usr/src/sys/net/if_media.h /usr/include/net/ cd /usr/src/sbin/ifconfig make obj make depend make doas make install `ifconfig iwm0 media' should show an additional line advertising 11n support: media autoselect mode 11n While associated to an 11n AP, `ifconfig iwm0' should indicate the MCS being used for transmissions, e.g.: media: IEEE802.11 autoselect (HT-MCS7 mode 11n) This work is being supported by genua gmbh which allows me to spend the necessary time to keep working on 11n and do my best at building this feature together with the OpenBSD community. Index: dev/pci/if_iwm.c =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v retrieving revision 1.63 diff -u -p -r1.63 if_iwm.c --- dev/pci/if_iwm.c 4 Nov 2015 12:11:59 -0000 1.63 +++ dev/pci/if_iwm.c 12 Nov 2015 15:33:07 -0000 @@ -175,19 +175,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 @@ -195,6 +198,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); @@ -283,6 +298,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); @@ -2375,6 +2408,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 */ @@ -2399,6 +2439,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) */ @@ -2482,9 +2523,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, @@ -2507,7 +2685,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", @@ -2549,13 +2733,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) { @@ -3080,23 +3257,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; @@ -3665,7 +3854,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; @@ -3673,16 +3862,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]; } @@ -3690,7 +3903,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; } @@ -3755,7 +3975,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)) @@ -4751,6 +4975,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); @@ -5142,8 +5390,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); @@ -5160,77 +5411,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 @@ -5245,6 +5511,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; @@ -5654,6 +5925,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); @@ -6347,6 +6621,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, @@ -6366,6 +6644,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; @@ -6551,6 +6837,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; @@ -6587,6 +6881,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.5 diff -u -p -r1.5 if_iwmreg.h --- dev/pci/if_iwmreg.h 16 Oct 2015 10:04:56 -0000 1.5 +++ dev/pci/if_iwmreg.h 12 Nov 2015 15:33:07 -0000 @@ -2361,9 +2361,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; @@ -3424,10 +3422,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, @@ -3481,7 +3525,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, }; /* @@ -3666,6 +3710,15 @@ enum { #define IWM_LQ_FLAG_DYNAMIC_BW_POS 6 #define IWM_LQ_FLAG_DYNAMIC_BW_MSK (1 << IWM_LQ_FLAG_DYNAMIC_BW_POS) +/* Antenna flags. */ +#define IWM_ANT_A (1 << 0) +#define IWM_ANT_B (1 << 1) +#define IWM_ANT_C (1 << 2) +/* Shortcuts. */ +#define IWM_ANT_AB (IWM_ANT_A | IWM_ANT_B) +#define IWM_ANT_BC (IWM_ANT_B | IWM_ANT_C) +#define IWM_ANT_ABC (IWM_ANT_A | IWM_ANT_B | IWM_ANT_C) + /** * struct iwm_lq_cmd - link quality command * @sta_id: station to update @@ -3674,8 +3727,8 @@ enum { * @mimo_delim: the first SISO index in rs_table, which separates MIMO * and SISO rates * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). - * Should be ANT_[ABC] - * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] + * Should be IWM_ANT_[ABC] + * @dual_stream_ant_msk: best antennas for MIMO, combination of IWM_ANT_[ABC] * @initial_rate_index: first index from rs_table per AC category * @agg_time_limit: aggregation max time threshold in usec/100, meaning * value of 100 is one usec. Range is 100 to 8000 Index: dev/pci/if_iwmvar.h =================================================================== RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v retrieving revision 1.12 diff -u -p -r1.12 if_iwmvar.h --- dev/pci/if_iwmvar.h 22 Oct 2015 11:51:28 -0000 1.12 +++ dev/pci/if_iwmvar.h 12 Nov 2015 15:33:07 -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; @@ -499,8 +511,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: dev/usb/if_rsu.c =================================================================== RCS file: /cvs/src/sys/dev/usb/if_rsu.c,v retrieving revision 1.28 diff -u -p -r1.28 if_rsu.c --- dev/usb/if_rsu.c 25 Oct 2015 12:11:56 -0000 1.28 +++ dev/usb/if_rsu.c 12 Nov 2015 17:18:10 -0000 @@ -1982,7 +1982,9 @@ rsu_fw_loadsection(struct rsu_softc *sc, int rsu_load_firmware(struct rsu_softc *sc) { +#ifndef IEEE80211_NO_HT struct ieee80211com *ic = &sc->sc_ic; +#endif struct r92s_fw_hdr *hdr; struct r92s_fw_priv *dmem; uint8_t *imem, *emem; @@ -2113,7 +2115,9 @@ rsu_load_firmware(struct rsu_softc *sc) dmem->rf_config = 0x12; /* 1T2R */ dmem->vcs_type = R92S_VCS_TYPE_AUTO; dmem->vcs_mode = R92S_VCS_MODE_RTS_CTS; +#ifndef IEEE80211_NO_HT dmem->bw40_en = (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) != 0; +#endif dmem->turbo_mode = 1; /* Load DMEM section. */ error = rsu_fw_loadsection(sc, (uint8_t *)dmem, sizeof(*dmem)); @@ -2254,6 +2258,7 @@ rsu_init(struct ifnet *ifp) goto fail; } +#ifndef IEEE80211_NO_HT if (ic->ic_htcaps & IEEE80211_HTCAP_CBW20_40) { /* Enable 40MHz mode. */ error = rsu_fw_iocmd(sc, @@ -2266,7 +2271,7 @@ rsu_init(struct ifnet *ifp) goto fail; } } - +#endif /* Set default channel. */ ic->ic_bss->ni_chan = ic->ic_ibss_chan; Index: net/if_media.h =================================================================== RCS file: /cvs/src/sys/net/if_media.h,v retrieving revision 1.34 diff -u -p -r1.34 if_media.h --- net/if_media.h 11 Sep 2015 13:02:28 -0000 1.34 +++ net/if_media.h 12 Nov 2015 15:33:07 -0000 @@ -202,6 +202,83 @@ uint64_t ifmedia_baudrate(uint64_t); #define IFM_IEEE80211_OFDM48 16 /* OFDM 48Mbps */ #define IFM_IEEE80211_OFDM54 17 /* OFDM 54Mbps */ #define IFM_IEEE80211_OFDM72 18 /* OFDM 72Mbps */ +#define IFM_IEEE80211_HT_MCS0 19 /* 11n MCS 0 */ +#define IFM_IEEE80211_HT_MCS1 20 /* 11n MCS 1 */ +#define IFM_IEEE80211_HT_MCS2 21 /* 11n MCS 2 */ +#define IFM_IEEE80211_HT_MCS3 22 /* 11n MCS 3 */ +#define IFM_IEEE80211_HT_MCS4 23 /* 11n MCS 4 */ +#define IFM_IEEE80211_HT_MCS5 24 /* 11n MCS 5 */ +#define IFM_IEEE80211_HT_MCS6 25 /* 11n MCS 6 */ +#define IFM_IEEE80211_HT_MCS7 26 /* 11n MCS 7 */ +#define IFM_IEEE80211_HT_MCS8 27 /* 11n MCS 8 */ +#define IFM_IEEE80211_HT_MCS9 28 /* 11n MCS 9 */ +#define IFM_IEEE80211_HT_MCS10 29 /* 11n MCS 10 */ +#define IFM_IEEE80211_HT_MCS11 30 /* 11n MCS 11 */ +#define IFM_IEEE80211_HT_MCS12 31 /* 11n MCS 12 */ +#define IFM_IEEE80211_HT_MCS13 32 /* 11n MCS 13 */ +#define IFM_IEEE80211_HT_MCS14 33 /* 11n MCS 14 */ +#define IFM_IEEE80211_HT_MCS15 34 /* 11n MCS 15 */ +#define IFM_IEEE80211_HT_MCS16 35 /* 11n MCS 16 */ +#define IFM_IEEE80211_HT_MCS17 36 /* 11n MCS 17 */ +#define IFM_IEEE80211_HT_MCS18 37 /* 11n MCS 18 */ +#define IFM_IEEE80211_HT_MCS19 38 /* 11n MCS 19 */ +#define IFM_IEEE80211_HT_MCS20 39 /* 11n MCS 20 */ +#define IFM_IEEE80211_HT_MCS21 40 /* 11n MCS 21 */ +#define IFM_IEEE80211_HT_MCS22 41 /* 11n MCS 22 */ +#define IFM_IEEE80211_HT_MCS23 42 /* 11n MCS 23 */ +#define IFM_IEEE80211_HT_MCS24 43 /* 11n MCS 24 */ +#define IFM_IEEE80211_HT_MCS25 44 /* 11n MCS 25 */ +#define IFM_IEEE80211_HT_MCS26 45 /* 11n MCS 26 */ +#define IFM_IEEE80211_HT_MCS27 46 /* 11n MCS 27 */ +#define IFM_IEEE80211_HT_MCS28 47 /* 11n MCS 28 */ +#define IFM_IEEE80211_HT_MCS29 48 /* 11n MCS 29 */ +#define IFM_IEEE80211_HT_MCS30 49 /* 11n MCS 30 */ +#define IFM_IEEE80211_HT_MCS31 50 /* 11n MCS 31 */ +#define IFM_IEEE80211_HT_MCS32 51 /* 11n MCS 32 */ +#define IFM_IEEE80211_HT_MCS33 52 /* 11n MCS 33 */ +#define IFM_IEEE80211_HT_MCS34 53 /* 11n MCS 34 */ +#define IFM_IEEE80211_HT_MCS35 54 /* 11n MCS 35 */ +#define IFM_IEEE80211_HT_MCS36 55 /* 11n MCS 36 */ +#define IFM_IEEE80211_HT_MCS37 56 /* 11n MCS 37 */ +#define IFM_IEEE80211_HT_MCS38 57 /* 11n MCS 38 */ +#define IFM_IEEE80211_HT_MCS39 58 /* 11n MCS 39 */ +#define IFM_IEEE80211_HT_MCS40 59 /* 11n MCS 40 */ +#define IFM_IEEE80211_HT_MCS41 60 /* 11n MCS 41 */ +#define IFM_IEEE80211_HT_MCS42 61 /* 11n MCS 42 */ +#define IFM_IEEE80211_HT_MCS43 62 /* 11n MCS 43 */ +#define IFM_IEEE80211_HT_MCS44 63 /* 11n MCS 44 */ +#define IFM_IEEE80211_HT_MCS45 64 /* 11n MCS 45 */ +#define IFM_IEEE80211_HT_MCS46 65 /* 11n MCS 46 */ +#define IFM_IEEE80211_HT_MCS47 66 /* 11n MCS 47 */ +#define IFM_IEEE80211_HT_MCS48 67 /* 11n MCS 48 */ +#define IFM_IEEE80211_HT_MCS49 68 /* 11n MCS 49 */ +#define IFM_IEEE80211_HT_MCS50 69 /* 11n MCS 50 */ +#define IFM_IEEE80211_HT_MCS51 70 /* 11n MCS 51 */ +#define IFM_IEEE80211_HT_MCS52 71 /* 11n MCS 52 */ +#define IFM_IEEE80211_HT_MCS53 72 /* 11n MCS 53 */ +#define IFM_IEEE80211_HT_MCS54 73 /* 11n MCS 54 */ +#define IFM_IEEE80211_HT_MCS55 74 /* 11n MCS 55 */ +#define IFM_IEEE80211_HT_MCS56 75 /* 11n MCS 56 */ +#define IFM_IEEE80211_HT_MCS57 76 /* 11n MCS 57 */ +#define IFM_IEEE80211_HT_MCS58 77 /* 11n MCS 58 */ +#define IFM_IEEE80211_HT_MCS59 78 /* 11n MCS 59 */ +#define IFM_IEEE80211_HT_MCS60 79 /* 11n MCS 60 */ +#define IFM_IEEE80211_HT_MCS61 80 /* 11n MCS 61 */ +#define IFM_IEEE80211_HT_MCS62 81 /* 11n MCS 62 */ +#define IFM_IEEE80211_HT_MCS63 82 /* 11n MCS 63 */ +#define IFM_IEEE80211_HT_MCS64 83 /* 11n MCS 64 */ +#define IFM_IEEE80211_HT_MCS65 84 /* 11n MCS 65 */ +#define IFM_IEEE80211_HT_MCS66 85 /* 11n MCS 66 */ +#define IFM_IEEE80211_HT_MCS67 86 /* 11n MCS 67 */ +#define IFM_IEEE80211_HT_MCS68 87 /* 11n MCS 68 */ +#define IFM_IEEE80211_HT_MCS69 88 /* 11n MCS 69 */ +#define IFM_IEEE80211_HT_MCS70 89 /* 11n MCS 70 */ +#define IFM_IEEE80211_HT_MCS71 90 /* 11n MCS 71 */ +#define IFM_IEEE80211_HT_MCS72 91 /* 11n MCS 72 */ +#define IFM_IEEE80211_HT_MCS73 92 /* 11n MCS 73 */ +#define IFM_IEEE80211_HT_MCS74 93 /* 11n MCS 74 */ +#define IFM_IEEE80211_HT_MCS75 94 /* 11n MCS 75 */ +#define IFM_IEEE80211_HT_MCS76 95 /* 11n MCS 76 */ #define IFM_IEEE80211_ADHOC 0x0000000000010000ULL /* Operate in Adhoc mode */ #define IFM_IEEE80211_HOSTAP 0x0000000000020000ULL /* Operate in Host AP mode */ @@ -215,6 +292,7 @@ uint64_t ifmedia_baudrate(uint64_t); #define IFM_IEEE80211_11B 0x0000000200000000ULL /* Direct Sequence mode */ #define IFM_IEEE80211_11G 0x0000000300000000ULL /* 2GHz, CCK mode */ #define IFM_IEEE80211_FH 0x0000000400000000ULL /* 2GHz, GFSK mode */ +#define IFM_IEEE80211_11N 0x0000000800000000ULL /* 11n/HT 2GHz/5GHz */ /* * Digitally multiplexed "Carrier" Serial Interfaces @@ -444,6 +522,83 @@ struct ifmedia_description { { IFM_IEEE80211|IFM_IEEE80211_OFDM48, "OFDM48" }, \ { IFM_IEEE80211|IFM_IEEE80211_OFDM54, "OFDM54" }, \ { IFM_IEEE80211|IFM_IEEE80211_OFDM72, "OFDM72" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS0, "HT-MCS0" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS1, "HT-MCS1" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS2, "HT-MCS2" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS3, "HT-MCS3" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS4, "HT-MCS4" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS5, "HT-MCS5" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS6, "HT-MCS6" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS7, "HT-MCS7" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS8, "HT-MCS8" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS9, "HT-MCS9" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS10, "HT-MCS10" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS11, "HT-MCS11" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS12, "HT-MCS12" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS13, "HT-MCS13" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS14, "HT-MCS14" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS15, "HT-MCS15" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS16, "HT-MCS16" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS17, "HT-MCS17" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS18, "HT-MCS18" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS19, "HT-MCS19" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS20, "HT-MCS20" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS21, "HT-MCS21" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS22, "HT-MCS22" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS23, "HT-MCS23" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS24, "HT-MCS24" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS25, "HT-MCS25" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS26, "HT-MCS26" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS27, "HT-MCS27" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS28, "HT-MCS28" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS29, "HT-MCS29" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS30, "HT-MCS30" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS31, "HT-MCS31" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS32, "HT-MCS32" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS33, "HT-MCS33" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS34, "HT-MCS34" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS35, "HT-MCS35" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS36, "HT-MCS36" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS37, "HT-MCS37" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS38, "HT-MCS38" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS39, "HT-MCS39" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS40, "HT-MCS40" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS41, "HT-MCS41" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS42, "HT-MCS42" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS43, "HT-MCS43" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS44, "HT-MCS44" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS45, "HT-MCS45" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS46, "HT-MCS46" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS47, "HT-MCS47" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS48, "HT-MCS48" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS49, "HT-MCS49" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS50, "HT-MCS50" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS51, "HT-MCS51" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS52, "HT-MCS52" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS53, "HT-MCS53" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS54, "HT-MCS54" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS55, "HT-MCS55" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS56, "HT-MCS56" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS57, "HT-MCS57" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS58, "HT-MCS58" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS59, "HT-MCS59" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS60, "HT-MCS60" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS61, "HT-MCS61" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS62, "HT-MCS62" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS63, "HT-MCS63" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS64, "HT-MCS64" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS65, "HT-MCS65" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS66, "HT-MCS66" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS67, "HT-MCS67" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS68, "HT-MCS68" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS69, "HT-MCS69" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS70, "HT-MCS70" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS71, "HT-MCS71" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS72, "HT-MCS72" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS73, "HT-MCS73" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS74, "HT-MCS74" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS75, "HT-MCS75" }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS76, "HT-MCS76" }, \ \ { IFM_TDM|IFM_TDM_T1, "t1" }, \ { IFM_TDM|IFM_TDM_T1_AMI, "t1-ami" }, \ @@ -468,6 +623,7 @@ struct ifmedia_description { { IFM_IEEE80211|IFM_IEEE80211_11B, "11b" }, \ { IFM_IEEE80211|IFM_IEEE80211_11G, "11g" }, \ { IFM_IEEE80211|IFM_IEEE80211_FH, "fh" }, \ + { IFM_IEEE80211|IFM_IEEE80211_11N, "11n" }, \ { IFM_TDM|IFM_TDM_MASTER, "master" }, \ { 0, NULL }, \ } @@ -558,6 +714,84 @@ struct ifmedia_baudrate { { IFM_IEEE80211|IFM_IEEE80211_OFDM48, IF_Mbps(48) }, \ { IFM_IEEE80211|IFM_IEEE80211_OFDM54, IF_Mbps(54) }, \ { IFM_IEEE80211|IFM_IEEE80211_OFDM72, IF_Mbps(72) }, \ + /* These HT rates correspond to 20 MHz channel with no SGI. */ \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS0, IF_Kbps(6500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS1, IF_Mbps(13) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS2, IF_Kbps(19500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS3, IF_Mbps(26) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS4, IF_Mbps(39) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS5, IF_Mbps(52) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS6, IF_Kbps(58500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS7, IF_Mbps(65) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS8, IF_Mbps(13) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS9, IF_Mbps(26) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS10, IF_Mbps(39) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS11, IF_Mbps(52) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS12, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS13, IF_Mbps(104) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS14, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS15, IF_Mbps(130) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS16, IF_Kbps(19500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS17, IF_Mbps(39) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS18, IF_Kbps(58500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS19, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS20, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS21, IF_Mbps(156) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS22, IF_Kbps(175500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS23, IF_Mbps(195) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS24, IF_Mbps(26) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS25, IF_Mbps(52) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS26, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS27, IF_Mbps(104) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS28, IF_Mbps(156) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS29, IF_Mbps(208) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS30, IF_Mbps(234) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS31, IF_Mbps(260) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS32, IF_Mbps(0) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS33, IF_Mbps(39) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS34, IF_Mbps(52) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS35, IF_Mbps(65) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS36, IF_Kbps(58500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS37, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS38, IF_Kbps(97500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS39, IF_Mbps(52) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS40, IF_Mbps(65) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS41, IF_Mbps(65) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS42, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS43, IF_Mbps(91) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS44, IF_Mbps(91) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS45, IF_Mbps(104) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS46, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS47, IF_Kbps(97500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS48, IF_Kbps(97500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS49, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS50, IF_Kbps(136500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS51, IF_Kbps(136500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS52, IF_Mbps(156) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS53, IF_Mbps(65) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS54, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS55, IF_Mbps(91) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS56, IF_Mbps(78) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS57, IF_Mbps(91) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS58, IF_Mbps(104) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS59, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS60, IF_Mbps(104) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS61, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS62, IF_Mbps(130) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS63, IF_Mbps(130) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS64, IF_Mbps(143) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS65, IF_Kbps(97500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS66, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS67, IF_Kbps(136500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS68, IF_Mbps(117) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS69, IF_Kbps(136500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS70, IF_Mbps(156) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS71, IF_Kbps(175500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS72, IF_Mbps(156) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS73, IF_Kbps(175500) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS74, IF_Mbps(195) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS75, IF_Mbps(195) }, \ + { IFM_IEEE80211|IFM_IEEE80211_HT_MCS76, IF_Kbps(214500) }, \ \ { IFM_TDM|IFM_TDM_T1, IF_Kbps(1536) }, \ { IFM_TDM|IFM_TDM_T1_AMI, IF_Kbps(1536) }, \ Index: net80211/ieee80211.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211.c,v retrieving revision 1.47 diff -u -p -r1.47 ieee80211.c --- net80211/ieee80211.c 17 Oct 2015 21:30:29 -0000 1.47 +++ net80211/ieee80211.c 12 Nov 2015 15:33:07 -0000 @@ -111,6 +111,10 @@ ieee80211_channel_init(struct ifnet *ifp ic->ic_modecaps |= 1<<IEEE80211_MODE_11G; if (IEEE80211_IS_CHAN_T(c)) ic->ic_modecaps |= 1<<IEEE80211_MODE_TURBO; +#ifndef IEEE80211_NO_HT + if (IEEE80211_IS_CHAN_N(c)) + ic->ic_modecaps |= 1<<IEEE80211_MODE_11N; +#endif } } /* validate ic->ic_curmode */ @@ -275,7 +279,7 @@ ieee80211_media_init(struct ifnet *ifp, ifmedia_init(&ic->ic_media, 0, media_change, media_stat); maxrate = 0; memset(&allrates, 0, sizeof(allrates)); - for (mode = IEEE80211_MODE_AUTO; mode < IEEE80211_MODE_MAX; mode++) { + for (mode = IEEE80211_MODE_AUTO; mode <= IEEE80211_MODE_TURBO; mode++) { static const uint64_t mopts[] = { IFM_AUTO, IFM_IEEE80211_11A, @@ -351,6 +355,34 @@ ieee80211_media_init(struct ifnet *ifp, if (ic->ic_caps & IEEE80211_C_MONITOR) ADD(ic, mword, IFM_IEEE80211_MONITOR); } + +#ifndef IEEE80211_NO_HT + if (ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) { + mopt = IFM_IEEE80211_11N; + ADD(ic, IFM_AUTO, mopt); +#ifndef IEEE80211_STA_ONLY + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_IBSS); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, IFM_AUTO, mopt | IFM_IEEE80211_HOSTAP); +#endif + for (i = 0; i < IEEE80211_HT_NUM_MCS; i++) { + if (!isset(ic->ic_sup_mcs, i)) + continue; + ADD(ic, IFM_IEEE80211_HT_MCS0 + i, mopt); +#ifndef IEEE80211_STA_ONLY + if (ic->ic_caps & IEEE80211_C_IBSS) + ADD(ic, IFM_IEEE80211_HT_MCS0 + i, + mopt | IFM_IEEE80211_IBSS); + if (ic->ic_caps & IEEE80211_C_HOSTAP) + ADD(ic, IFM_IEEE80211_HT_MCS0 + i, + mopt | IFM_IEEE80211_HOSTAP); +#endif + } + ic->ic_flags |= IEEE80211_F_HTON; /* enable 11n by default */ + } +#endif /* IEEE80211_NO_HT */ + ieee80211_media_status(ifp, &imr); ifmedia_set(&ic->ic_media, imr.ifm_active); @@ -400,6 +432,11 @@ ieee80211_media_change(struct ifnet *ifp case IFM_IEEE80211_11G: newphymode = IEEE80211_MODE_11G; break; +#ifndef IEEE80211_NO_HT + case IFM_IEEE80211_11N: + newphymode = IEEE80211_MODE_11N; + break; +#endif case IFM_AUTO: newphymode = IEEE80211_MODE_AUTO; break; @@ -425,7 +462,21 @@ ieee80211_media_change(struct ifnet *ifp * Next, the fixed/variable rate. */ i = -1; - if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { + if (IFM_SUBTYPE(ime->ifm_media) >= IFM_IEEE80211_HT_MCS0 && + IFM_SUBTYPE(ime->ifm_media) <= IFM_IEEE80211_HT_MCS76) { +#ifndef IEEE80211_NO_HT + if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) == 0) + return EINVAL; + if (newphymode != IEEE80211_MODE_AUTO || + newphymode != IEEE80211_MODE_11N) + return EINVAL; + i = ieee80211_media2mcs(ime->ifm_media); + if (i == -1 || isclr(ic->ic_sup_mcs, i)) + return EINVAL; +#else + return EINVAL; +#endif + } else if (IFM_SUBTYPE(ime->ifm_media) != IFM_AUTO) { /* * Convert media subtype to rate. */ @@ -484,11 +535,17 @@ ieee80211_media_change(struct ifnet *ifp */ if (newopmode == IEEE80211_M_HOSTAP && newphymode == IEEE80211_MODE_AUTO) { - for (j = IEEE80211_MODE_11A; j < IEEE80211_MODE_MAX; j++) - if (ic->ic_modecaps & (1<<j)) { - newphymode = j; - break; - } +#ifndef IEEE80211_NO_HT + if (ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) + newphymode = IEEE80211_MODE_11N; + else +#endif + if (ic->ic_modecaps & (1 << IEEE80211_MODE_11A)) + newphymode = IEEE80211_MODE_11A; + else if (ic->ic_modecaps & (1 << IEEE80211_MODE_11G)) + newphymode = IEEE80211_MODE_11G; + else + newphymode = IEEE80211_MODE_11B; } #endif @@ -503,12 +560,33 @@ ieee80211_media_change(struct ifnet *ifp } /* - * Committed to changes, install the rate setting. + * Committed to changes, install the MCS/rate setting. */ - if (ic->ic_fixed_rate != i) { - ic->ic_fixed_rate = i; /* set fixed tx rate */ - error = ENETRESET; + ic->ic_flags &= ~IEEE80211_F_HTON; +#ifndef IEEE80211_NO_HT + if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) && + (newphymode == IEEE80211_MODE_AUTO || + newphymode == IEEE80211_MODE_11N)) + ic->ic_flags |= IEEE80211_F_HTON; +#endif + if ((ic->ic_flags & IEEE80211_F_HTON) == 0) { +#ifndef IEEE80211_NO_HT + ic->ic_fixed_mcs = -1; +#endif + if (ic->ic_fixed_rate != i) { + ic->ic_fixed_rate = i; /* set fixed tx rate */ + error = ENETRESET; + } } +#ifndef IEEE80211_NO_HT + else { + ic->ic_fixed_rate = -1; + if (ic->ic_fixed_mcs != i) { + ic->ic_fixed_mcs = i; /* set fixed mcs */ + error = ENETRESET; + } + } +#endif /* * Handle operating mode change. @@ -560,9 +638,16 @@ ieee80211_media_status(struct ifnet *ifp switch (ic->ic_opmode) { case IEEE80211_M_STA: ni = ic->ic_bss; - /* calculate rate subtype */ - imr->ifm_active |= ieee80211_rate2media(ic, - ni->ni_rates.rs_rates[ni->ni_txrate], ic->ic_curmode); +#ifndef IEEE80211_NO_HT + if (ic->ic_curmode == IEEE80211_MODE_11N) + imr->ifm_active |= ieee80211_mcs2media(ic, + ni->ni_txmcs, ic->ic_curmode); + else +#endif + /* calculate rate subtype */ + imr->ifm_active |= ieee80211_rate2media(ic, + ni->ni_rates.rs_rates[ni->ni_txrate], + ic->ic_curmode); break; #ifndef IEEE80211_STA_ONLY case IEEE80211_M_IBSS: @@ -595,6 +680,11 @@ ieee80211_media_status(struct ifnet *ifp imr->ifm_active |= IFM_IEEE80211_11A | IFM_IEEE80211_TURBO; break; +#ifndef IEEE80211_NO_HT + case IEEE80211_MODE_11N: + imr->ifm_active |= IFM_IEEE80211_11N; + break; +#endif } } @@ -672,6 +762,9 @@ ieee80211_setmode(struct ieee80211com *i IEEE80211_CHAN_B, /* IEEE80211_MODE_11B */ IEEE80211_CHAN_PUREG, /* IEEE80211_MODE_11G */ IEEE80211_CHAN_T, /* IEEE80211_MODE_TURBO */ +#ifndef IEEE80211_NO_HT + IEEE80211_CHAN_HT, /* IEEE80211_MODE_11N */ +#endif }; const struct ieee80211_channel *c; u_int modeflags; @@ -771,10 +864,12 @@ ieee80211_next_mode(struct ifnet *ifp) * Get the next supported mode */ for (++ic->ic_curmode; - ic->ic_curmode <= IEEE80211_MODE_TURBO; + ic->ic_curmode <= IEEE80211_MODE_MAX; ic->ic_curmode++) { /* Wrap around and ignore turbo mode */ - if (ic->ic_curmode >= IEEE80211_MODE_TURBO) { + if (ic->ic_curmode == IEEE80211_MODE_TURBO) + continue; + if (ic->ic_curmode >= IEEE80211_MODE_MAX) { ic->ic_curmode = IEEE80211_MODE_AUTO; break; } @@ -811,6 +906,11 @@ ieee80211_chan2mode(struct ieee80211com * characteristics. We assume that turbo-only channels * are not considered when the channel set is constructed. */ +#ifndef IEEE80211_NO_HT + if (IEEE80211_IS_CHAN_N(chan)) + return IEEE80211_MODE_11N; + else +#endif if (IEEE80211_IS_CHAN_T(chan)) return IEEE80211_MODE_TURBO; else if (IEEE80211_IS_CHAN_5GHZ(chan)) @@ -821,6 +921,56 @@ ieee80211_chan2mode(struct ieee80211com return IEEE80211_MODE_11B; } +#ifndef IEEE80211_NO_HT +/* + * Convert IEEE80211 MCS index to ifmedia subtype. + */ +uint64_t +ieee80211_mcs2media(struct ieee80211com *ic, int mcs, + enum ieee80211_phymode mode) +{ + switch (mode) { + case IEEE80211_MODE_11A: + case IEEE80211_MODE_TURBO: + case IEEE80211_MODE_11B: + case IEEE80211_MODE_11G: + /* these modes use rates, not MCS */ + panic("unexpected mode %d", mode); + break; + case IEEE80211_MODE_AUTO: + case IEEE80211_MODE_11N: + if (mcs >= 0 && mcs < IEEE80211_HT_NUM_MCS) + return (IFM_IEEE80211_11N | + (IFM_IEEE80211_HT_MCS0 + mcs)); + break; + } + + return IFM_AUTO; +} + +/* + * Convert ifmedia subtype to IEEE80211 MCS index. + */ +int +ieee80211_media2mcs(uint64_t mword) +{ + uint64_t subtype; + + subtype = IFM_SUBTYPE(mword); + + if (subtype == IFM_AUTO) + return -1; + else if (subtype == IFM_MANUAL || subtype == IFM_NONE) + return 0; + + if (subtype >= IFM_IEEE80211_HT_MCS0 && + subtype <= IFM_IEEE80211_HT_MCS76) + return (int)(subtype - IFM_IEEE80211_HT_MCS0); + + return -1; +} +#endif /* IEEE80211_NO_HT */ + /* * convert IEEE80211 rate value to ifmedia subtype. * ieee80211 rate is in unit of 0.5Mbps. @@ -877,6 +1027,10 @@ ieee80211_rate2media(struct ieee80211com /* FALLTHROUGH */ case IEEE80211_MODE_11G: mask |= IFM_IEEE80211_11G; + break; + case IEEE80211_MODE_11N: + /* 11n uses MCS, not rates. */ + panic("unexpected mode %d", mode); break; } for (i = 0; i < nitems(rates); i++) Index: net80211/ieee80211.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211.h,v retrieving revision 1.53 diff -u -p -r1.53 ieee80211.h --- net80211/ieee80211.h 10 Oct 2015 07:51:47 -0000 1.53 +++ net80211/ieee80211.h 12 Nov 2015 15:33:07 -0000 @@ -504,6 +504,8 @@ enum { #define IEEE80211_RATE_SIZE 8 /* 802.11 standard */ #define IEEE80211_RATE_MAXSIZE 15 /* max rates we'll handle */ +#define IEEE80211_HT_NUM_MCS 77 + /* * BlockAck/BlockAckReq Control field (see 802.11-2012 8.3.1.9 Figure 8-25). */ @@ -576,11 +578,13 @@ enum { */ #define IEEE80211_AMPDU_PARAM_LE 0x03 #define IEEE80211_AMPDU_PARAM_SS 0x1c +/* bits 5-7 reserved */ /* * HT Supported MCS Set (see 802.11-2012 8.4.2.58.4). * This field is 16 bytes in size. Bitmasks given below - * operate on 8 or 16 bit integer subsets of this field. + * operate on 8 or 16 bit integer subsets of this field + * for use with ieee80211com and ieee80211_node. */ /* Bits 0-76: Supported Rx MCS bitmask */ /* Bits 77-79: Reserved */ @@ -661,6 +665,7 @@ enum { /* * HT Operation element (see 802.11-2012 8.4.2.59). */ +/* Byte 0 contains primary channel number. */ /* Byte 1. */ #define IEEE80211_HTOP0_SCO_MASK 0x03 #define IEEE80211_HTOP0_SCO_SHIFT 0 @@ -987,6 +992,16 @@ enum { IEEE80211_KDE_LIFETIME = 7, IEEE80211_KDE_ERROR = 8, IEEE80211_KDE_IGTK = 9 /* 11w */ +}; + +/* + * HT protection modes (see 802.11-2012 8.4.2.59) + */ +enum ieee80211_htprot { + IEEE80211_HTPROT_NONE = 0, + IEEE80211_HTPROT_NONMEMBER, + IEEE80211_HTPROT_20MHZ, + IEEE80211_HTPROT_NONHT_MIXED, }; #endif /* _NET80211_IEEE80211_H_ */ Index: net80211/ieee80211_amrr.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_amrr.c,v retrieving revision 1.8 diff -u -p -r1.8 ieee80211_amrr.c --- net80211/ieee80211_amrr.c 23 Dec 2014 03:24:08 -0000 1.8 +++ net80211/ieee80211_amrr.c 12 Nov 2015 15:33:07 -0000 @@ -38,16 +38,57 @@ ((amn)->amn_retrycnt > (amn)->amn_txcnt / 3) #define is_enough(amn) \ ((amn)->amn_txcnt > 10) -#define is_min_rate(ni) \ - ((ni)->ni_txrate == 0) -#define is_max_rate(ni) \ - ((ni)->ni_txrate == (ni)->ni_rates.rs_nrates - 1) -#define increase_rate(ni) \ - ((ni)->ni_txrate++) -#define decrease_rate(ni) \ - ((ni)->ni_txrate--) #define reset_cnt(amn) \ do { (amn)->amn_txcnt = (amn)->amn_retrycnt = 0; } while (0) + +/* + * XXX In HT mode we only support MCS 0-7, for now. + * Beyond MCS 7, incrementing the MCS index does not imply a + * higher data rate, so this simple implementation will need + * to be enhanced. + */ + +static inline int +is_min_rate(struct ieee80211_node *ni) +{ +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) + return (ni->ni_txmcs == 0); +#endif + return (ni->ni_txrate == 0); +} + +static inline int +is_max_rate(struct ieee80211_node *ni) +{ +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) + return (ni->ni_txmcs == 7); /* XXX up to MCS 7 only */ +#endif + return (ni->ni_txrate == ni->ni_rates.rs_nrates - 1); +} + +static inline void +increase_rate(struct ieee80211_node *ni) +{ +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) + ni->ni_txmcs++; + else +#endif + ni->ni_txrate++; +} + +static inline void +decrease_rate(struct ieee80211_node *ni) +{ +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) + ni->ni_txmcs--; + else +#endif + ni->ni_txrate--; +} void ieee80211_amrr_node_init(const struct ieee80211_amrr *amrr, Index: net80211/ieee80211_crypto_ccmp.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_crypto_ccmp.c,v retrieving revision 1.16 diff -u -p -r1.16 ieee80211_crypto_ccmp.c --- net80211/ieee80211_crypto_ccmp.c 15 Jul 2015 22:16:42 -0000 1.16 +++ net80211/ieee80211_crypto_ccmp.c 12 Nov 2015 15:33:07 -0000 @@ -88,17 +88,18 @@ ieee80211_ccmp_phase1(rijndael_ctx *ctx, /* construct AAD (additional authenticated data) */ aad = &auth[2]; /* skip l(a), will be filled later */ *aad = wh->i_fc[0]; - /* 11w: conditionnally mask subtype field */ + /* 11w: conditionally mask subtype field */ if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA) - *aad &= ~IEEE80211_FC0_SUBTYPE_MASK; + *aad &= ~IEEE80211_FC0_SUBTYPE_MASK | + IEEE80211_FC0_SUBTYPE_QOS; aad++; /* protected bit is already set in wh */ *aad = wh->i_fc[1]; *aad &= ~(IEEE80211_FC1_RETRY | IEEE80211_FC1_PWR_MGT | IEEE80211_FC1_MORE_DATA); - /* 11n: conditionnally mask order bit */ - if (ieee80211_has_htc(wh)) + /* 11n: conditionally mask order bit */ + if (ieee80211_has_qos(wh)) *aad &= ~IEEE80211_FC1_ORDER; aad++; IEEE80211_ADDR_COPY(aad, wh->i_addr1); aad += IEEE80211_ADDR_LEN; @@ -112,6 +113,10 @@ ieee80211_ccmp_phase1(rijndael_ctx *ctx, aad += IEEE80211_ADDR_LEN; } if (ieee80211_has_qos(wh)) { + /* + * XXX 802.11-2012 11.4.3.3.3 g says the A-MSDU present bit + * should be set here if both STAs are SPP A-MSDU capable. + */ *aad++ = tid = ieee80211_get_qos(wh) & IEEE80211_QOS_TID; *aad++ = 0; } Index: net80211/ieee80211_input.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_input.c,v retrieving revision 1.140 diff -u -p -r1.140 ieee80211_input.c --- net80211/ieee80211_input.c 8 Nov 2015 18:51:47 -0000 1.140 +++ net80211/ieee80211_input.c 12 Nov 2015 15:33:07 -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))) { @@ -1628,7 +1644,12 @@ ieee80211_recv_probe_resp(struct ieee802 ni->ni_erp = erp; /* NB: must be after ni_chan is setup */ ieee80211_setup_rates(ic, ni, rates, xrates, IEEE80211_F_DOSORT); - +#ifndef IEEE80211_NO_HT + if (htcaps) + ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]); + if (htop) + ieee80211_setup_htop(ni, htop + 2, htop[1]); +#endif #ifndef IEEE80211_STA_ONLY if (ic->ic_opmode == IEEE80211_M_IBSS && is_new && isprobe) { /* @@ -1734,6 +1755,10 @@ ieee80211_recv_probe_req(struct ieee8021 ether_sprintf((u_int8_t *)wh->i_addr2))); return; } +#ifndef IEEE80211_NO_HT + if (htcaps) + ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]); +#endif IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0); } #endif /* IEEE80211_STA_ONLY */ @@ -2073,6 +2098,10 @@ ieee80211_recv_assoc_req(struct ieee8021 ni->ni_intval = bintval; ni->ni_capinfo = capinfo; ni->ni_chan = ic->ic_bss->ni_chan; +#ifndef IEEE80211_NO_HT + if (htcaps) + ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]); +#endif end: if (status != 0) { IEEE80211_SEND_MGMT(ic, ni, resp, status); @@ -2199,6 +2228,32 @@ ieee80211_recv_assoc_resp(struct ieee802 else /* for Reassociation */ ni->ni_flags &= ~IEEE80211_NODE_QOS; } +#ifndef IEEE80211_NO_HT + if (htcaps) + ieee80211_setup_htcaps(ni, htcaps + 2, htcaps[1]); + if (htop) + ieee80211_setup_htop(ni, htop + 2, htop[1]); + ieee80211_ht_negotiate(ic, ni); + + /* Hop into 11n mode after associating to an HT AP in a non-11n mode. */ + if (ic->ic_curmode != IEEE80211_MODE_AUTO && + ic->ic_curmode != IEEE80211_MODE_11N && + (ni->ni_flags & IEEE80211_NODE_HT)) + ieee80211_setmode(ic, IEEE80211_MODE_11N); + + /* Hop out of 11n mode after associating to a non-HT AP. */ + if (ic->ic_curmode == IEEE80211_MODE_11N && + (ni->ni_flags & IEEE80211_NODE_HT) == 0) { + if (IEEE80211_IS_CHAN_T(ni->ni_chan)) + ieee80211_setmode(ic, IEEE80211_MODE_TURBO); + else if (IEEE80211_IS_CHAN_A(ni->ni_chan)) + ieee80211_setmode(ic, IEEE80211_MODE_11A); + else if (IEEE80211_IS_CHAN_G(ni->ni_chan)) + ieee80211_setmode(ic, IEEE80211_MODE_11G); + else + ieee80211_setmode(ic, IEEE80211_MODE_11B); + } +#endif /* * Configure state now that we are associated. */ @@ -2410,6 +2465,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) @@ -2426,7 +2482,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_ioctl.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_ioctl.h,v retrieving revision 1.21 diff -u -p -r1.21 ieee80211_ioctl.h --- net80211/ieee80211_ioctl.h 9 Jan 2015 20:34:21 -0000 1.21 +++ net80211/ieee80211_ioctl.h 12 Nov 2015 15:33:07 -0000 @@ -175,6 +175,7 @@ struct ieee80211_channel { #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_XR 0x1000 /* Extended range OFDM channel */ +#define IEEE80211_CHAN_HT 0x2000 /* 11n/HT channel */ #endif /* !_KERNEL */ struct ieee80211_chanreq_all { Index: net80211/ieee80211_node.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.c,v retrieving revision 1.89 diff -u -p -r1.89 ieee80211_node.c --- net80211/ieee80211_node.c 4 Nov 2015 12:12:00 -0000 1.89 +++ net80211/ieee80211_node.c 12 Nov 2015 15:33:07 -0000 @@ -1255,6 +1255,61 @@ ieee80211_iterate_nodes(struct ieee80211 splx(s); } + +#ifndef IEEE80211_NO_HT +/* + * Install received HT caps information in the node's state block. + */ +void +ieee80211_setup_htcaps(struct ieee80211_node *ni, const uint8_t *data, + uint8_t len) +{ + uint16_t rxrate; + + if (len != 26) + return; + + ni->ni_htcaps = (data[0] | (data[1] << 8)); + ni->ni_ampdu_param = data[2]; + + memcpy(ni->ni_rxmcs, &data[3], sizeof(ni->ni_rxmcs)); + /* clear reserved bits */ + clrbit(ni->ni_rxmcs, 77); + clrbit(ni->ni_rxmcs, 78); + clrbit(ni->ni_rxmcs, 79); + + /* Max MCS Rx rate in 1Mb/s units (0 means "not specified"). */ + rxrate = ((data[13] | (data[14]) << 8) & IEEE80211_MCS_RX_RATE_HIGH); + if (rxrate < 1024) + ni->ni_max_rxrate = rxrate; + + ni->ni_tx_mcs_set = data[15]; + ni->ni_htxcaps = (data[19] | (data[20] << 8)); + ni->ni_txbfcaps = (data[21] | (data[22] << 8) | (data[23] << 16) | + (data[24] << 24)); + ni->ni_aselcaps = data[25]; +} + +/* + * Install received HT op information in the node's state block. + */ +void +ieee80211_setup_htop(struct ieee80211_node *ni, const uint8_t *data, + uint8_t len) +{ + if (len != 22) + return; + + ni->ni_primary_chan = data[0]; /* XXX corresponds to ni_chan */ + + ni->ni_htop0 = data[1]; + ni->ni_htop1 = (data[2] | (data[3] << 8)); + ni->ni_htop2 = (data[3] | (data[4] << 8)); + + memcpy(ni->ni_basic_mcs, &data[6], sizeof(ni->ni_basic_mcs)); +} +#endif /* IEEE80211_NO_HT */ + /* * Install received rate set information in the node's state block. */ @@ -1470,6 +1525,7 @@ ieee80211_node_join(struct ieee80211com ieee80211_node_join_rsn(ic, ni); #ifndef IEEE80211_NO_HT + ieee80211_ht_negotiate(ic, ni); if (ni->ni_flags & IEEE80211_NODE_HT) ieee80211_node_join_ht(ic, ni); #endif Index: net80211/ieee80211_node.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_node.h,v retrieving revision 1.47 diff -u -p -r1.47 ieee80211_node.h --- net80211/ieee80211_node.h 4 Nov 2015 12:12:00 -0000 1.47 +++ net80211/ieee80211_node.h 12 Nov 2015 15:33:07 -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 (10 * 1000 * 1000 * 60) /* 60 sec */ int ba_state; #define IEEE80211_BA_INIT 0 @@ -220,10 +220,31 @@ struct ieee80211_node { struct timeout ni_sa_query_to; int ni_sa_query_count; +#ifndef IEEE80211_NO_HT + /* HT capabilities */ + uint16_t ni_htcaps; + uint8_t ni_ampdu_param; + uint8_t ni_rxmcs[howmany(80,NBBY)]; + uint16_t ni_max_rxrate; /* in Mb/s, 0 <= rate <= 1023 */ + uint8_t ni_tx_mcs_set; + uint16_t ni_htxcaps; + uint32_t ni_txbfcaps; + uint8_t ni_aselcaps; + + /* HT operation */ + uint8_t ni_primary_chan; /* XXX corresponds to ni_chan */ + uint8_t ni_htop0; + uint16_t ni_htop1; + uint16_t ni_htop2; + uint8_t ni_basic_mcs[howmany(128,NBBY)]; + /* Block Ack records */ struct ieee80211_tx_ba ni_tx_ba[IEEE80211_NUM_TID]; struct ieee80211_rx_ba ni_rx_ba[IEEE80211_NUM_TID]; + int ni_txmcs; /* current MCS used for TX */ +#endif + /* others */ u_int16_t ni_associd; /* assoc response */ u_int16_t ni_txseq; /* seq to be transmitted */ @@ -327,6 +348,12 @@ extern void ieee80211_iterate_nodes(stru ieee80211_iter_func *, void *); extern void ieee80211_clean_cached(struct ieee80211com *ic); extern void ieee80211_clean_nodes(struct ieee80211com *, int); +#ifndef IEEE80211_NO_HT +void ieee80211_setup_htcaps(struct ieee80211_node *, const uint8_t *, + uint8_t); +void ieee80211_setup_htop(struct ieee80211_node *, const uint8_t *, + uint8_t); +#endif extern int ieee80211_setup_rates(struct ieee80211com *, struct ieee80211_node *, const u_int8_t *, const u_int8_t *, int); extern int ieee80211_iserp_sta(const struct ieee80211_node *); Index: net80211/ieee80211_output.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_output.c,v retrieving revision 1.99 diff -u -p -r1.99 ieee80211_output.c --- net80211/ieee80211_output.c 8 Nov 2015 18:48:07 -0000 1.99 +++ net80211/ieee80211_output.c 12 Nov 2015 15:33:07 -0000 @@ -1009,8 +1009,14 @@ ieee80211_add_htcaps(u_int8_t *frm, stru *frm++ = IEEE80211_ELEMID_HTCAPS; *frm++ = 26; LE_WRITE_2(frm, ic->ic_htcaps); frm += 2; - *frm++ = 0; - memcpy(frm, ic->ic_sup_mcs, 16); frm += 16; + *frm++ = 0; /* XXX A-MPDU params */ + memcpy(frm, ic->ic_sup_mcs, 10); frm += 10; + LE_WRITE_2(frm, (ic->ic_max_rxrate & IEEE80211_MCS_RX_RATE_HIGH)); + frm += 2; + *frm++ = ic->ic_tx_mcs_set; + *frm++ = 0; /* reserved */ + *frm++ = 0; /* reserved */ + *frm++ = 0; /* reserved */ LE_WRITE_2(frm, ic->ic_htxcaps); frm += 2; LE_WRITE_4(frm, ic->ic_txbfcaps); frm += 4; *frm++ = ic->ic_aselcaps; @@ -1082,11 +1088,17 @@ ieee80211_getmgmt(int flags, int type, u struct mbuf * ieee80211_get_probe_req(struct ieee80211com *ic, struct ieee80211_node *ni) { - const struct ieee80211_rateset *rs = - &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; + const struct ieee80211_rateset *rs; struct mbuf *m; u_int8_t *frm; +#ifndef IEEE80211_NO_HT + if (ic->ic_curmode == IEEE80211_MODE_11N) + rs = &ic->ic_sup_rates[IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ? + IEEE80211_MODE_11G : IEEE80211_MODE_11A]; + else +#endif + rs = &ic->ic_sup_rates[ieee80211_chan2mode(ic, ni->ni_chan)]; m = ieee80211_getmgmt(M_DONTWAIT, MT_DATA, 2 + ic->ic_des_esslen + 2 + min(rs->rs_nrates, IEEE80211_RATE_SIZE) + @@ -1102,7 +1114,7 @@ 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); #endif @@ -1305,7 +1317,7 @@ 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); #endif @@ -1365,7 +1377,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); } Index: net80211/ieee80211_proto.c =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_proto.c,v retrieving revision 1.53 diff -u -p -r1.53 ieee80211_proto.c --- net80211/ieee80211_proto.c 4 Nov 2015 12:12:00 -0000 1.53 +++ net80211/ieee80211_proto.c 12 Nov 2015 15:33:07 -0000 @@ -75,6 +75,7 @@ const char * const ieee80211_phymode_nam "11b", /* IEEE80211_MODE_11B */ "11g", /* IEEE80211_MODE_11G */ "turbo", /* IEEE80211_MODE_TURBO */ + "11n", /* IEEE80211_MODE_11N */ }; int ieee80211_newstate(struct ieee80211com *, enum ieee80211_state, int); @@ -96,6 +97,9 @@ ieee80211_proto_attach(struct ifnet *ifp #endif ic->ic_fragthreshold = 2346; /* XXX not used yet */ ic->ic_fixed_rate = -1; /* no fixed rate */ +#ifndef IEEE80211_NO_HT + ic->ic_fixed_mcs = -1; /* no fixed mcs */ +#endif ic->ic_protmode = IEEE80211_PROT_CTSONLY; /* protocol state change handler */ @@ -546,6 +550,48 @@ ieee80211_sa_query_request(struct ieee80 #ifndef IEEE80211_NO_HT void +ieee80211_ht_negotiate(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + int i; + + ni->ni_flags &= ~IEEE80211_NODE_HT; + + /* Check if we support HT. */ + if ((ic->ic_modecaps & (1 << IEEE80211_MODE_11N)) == 0) + return; + + /* Check if HT support has been explicitly disabled. */ + if ((ic->ic_flags & IEEE80211_F_HTON) == 0) + return; + + /* Check if the peer supports HT. MCS 0-7 are mandatory. */ + if (ni->ni_rxmcs[0] != 0xff) + return; + + if (ic->ic_opmode == IEEE80211_M_STA) { + /* We must support the AP's basic MCS set. */ + for (i = 0; i < IEEE80211_HT_NUM_MCS; i++) { + if (isset(ni->ni_basic_mcs, i) && + !isset(ic->ic_sup_mcs, i)) + return; + } + } + + /* + * Don't allow group cipher (includes WEP) or TKIP + * for pairwise encryption (see 802.11-2012 11.1.6). + */ + if (ic->ic_flags & IEEE80211_F_WEPON) + return; + if ((ic->ic_flags & IEEE80211_F_RSNON) && + (ni->ni_rsnciphers & IEEE80211_CIPHER_USEGROUP || + ni->ni_rsnciphers & IEEE80211_CIPHER_TKIP)) + return; + + ni->ni_flags |= IEEE80211_NODE_HT; +} + +void ieee80211_tx_ba_timeout(void *arg) { struct ieee80211_tx_ba *ba = arg; @@ -849,8 +895,15 @@ justcleanup: /* initialize bss for probe request */ IEEE80211_ADDR_COPY(ni->ni_macaddr, etherbroadcastaddr); IEEE80211_ADDR_COPY(ni->ni_bssid, etherbroadcastaddr); - ni->ni_rates = ic->ic_sup_rates[ - ieee80211_chan2mode(ic, ni->ni_chan)]; +#ifndef IEEE80211_NO_HT + if (ic->ic_curmode == IEEE80211_MODE_11N) + ni->ni_rates = ic->ic_sup_rates[ + IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) ? + IEEE80211_MODE_11G : IEEE80211_MODE_11A]; + else +#endif + ni->ni_rates = ic->ic_sup_rates[ + ieee80211_chan2mode(ic, ni->ni_chan)]; ni->ni_associd = 0; ni->ni_rstamp = 0; switch (ostate) { @@ -971,16 +1024,24 @@ justcleanup: ni->ni_esslen); rate = ni->ni_rates.rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL; - printf(" channel %d start %u%sMb", - ieee80211_chan2ieee(ic, ni->ni_chan), - rate / 2, (rate & 1) ? ".5" : ""); - printf(" %s preamble %s slot time%s\n", + printf(" channel %d", + ieee80211_chan2ieee(ic, ni->ni_chan)); +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) + printf(" start MCS %u", ni->ni_txmcs); + else +#endif + printf(" start %u%sMb", + rate / 2, (rate & 1) ? ".5" : ""); + printf(" %s preamble %s slot time%s%s\n", (ic->ic_flags & IEEE80211_F_SHPREAMBLE) ? "short" : "long", (ic->ic_flags & IEEE80211_F_SHSLOT) ? "short" : "long", (ic->ic_flags & IEEE80211_F_USEPROT) ? - " protection enabled" : ""); + " protection enabled" : "", + (ni->ni_flags & IEEE80211_NODE_HT) ? + " HT enabled" : ""); } if (!(ic->ic_flags & IEEE80211_F_RSNON)) { /* Index: net80211/ieee80211_proto.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_proto.h,v retrieving revision 1.39 diff -u -p -r1.39 ieee80211_proto.h --- net80211/ieee80211_proto.h 21 Nov 2009 18:09:31 -0000 1.39 +++ net80211/ieee80211_proto.h 12 Nov 2015 15:33:07 -0000 @@ -154,6 +154,8 @@ extern void ieee80211_sa_query_timeout(v extern void ieee80211_sa_query_request(struct ieee80211com *, struct ieee80211_node *); #ifndef IEEE80211_NO_HT +extern void ieee80211_ht_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 *, Index: net80211/ieee80211_radiotap.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_radiotap.h,v retrieving revision 1.11 diff -u -p -r1.11 ieee80211_radiotap.h --- net80211/ieee80211_radiotap.h 17 Jul 2010 16:25:09 -0000 1.11 +++ net80211/ieee80211_radiotap.h 12 Nov 2015 15:33:07 -0000 @@ -196,6 +196,7 @@ enum ieee80211_radiotap_type { #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_GFSK 0x0800 /* GFSK channel (FHSS PHY) */ #define IEEE80211_CHAN_XR 0x1000 /* Extended range OFDM channel */ +#define IEEE80211_CHAN_HT 0x2000 /* 11n/HT channel */ #endif /* !_KERNEL */ /* For IEEE80211_RADIOTAP_FLAGS */ Index: net80211/ieee80211_var.h =================================================================== RCS file: /cvs/src/sys/net80211/ieee80211_var.h,v retrieving revision 1.65 diff -u -p -r1.65 ieee80211_var.h --- net80211/ieee80211_var.h 4 Nov 2015 12:12:00 -0000 1.65 +++ net80211/ieee80211_var.h 12 Nov 2015 15:33:07 -0000 @@ -72,9 +72,10 @@ enum ieee80211_phymode { IEEE80211_MODE_11A = 1, /* 5GHz, OFDM */ IEEE80211_MODE_11B = 2, /* 2GHz, CCK */ IEEE80211_MODE_11G = 3, /* 2GHz, OFDM */ - IEEE80211_MODE_TURBO = 4 /* 5GHz, OFDM, 2x clock */ + IEEE80211_MODE_TURBO = 4, /* 5GHz, OFDM, 2x clock */ + IEEE80211_MODE_11N = 5, /* 11n, 2GHz/5GHz */ }; -#define IEEE80211_MODE_MAX (IEEE80211_MODE_TURBO+1) +#define IEEE80211_MODE_MAX (IEEE80211_MODE_11N+1) enum ieee80211_opmode { IEEE80211_M_STA = 1, /* infrastructure station */ @@ -114,6 +115,7 @@ struct ieee80211_channel { #define IEEE80211_CHAN_PASSIVE 0x0200 /* Only passive scan allowed */ #define IEEE80211_CHAN_DYN 0x0400 /* Dynamic CCK-OFDM channel */ #define IEEE80211_CHAN_XR 0x1000 /* Extended range OFDM channel */ +#define IEEE80211_CHAN_HT 0x2000 /* 11n/HT channel */ /* * Useful combinations of channel characteristics. @@ -143,6 +145,8 @@ struct ieee80211_channel { (((_c)->ic_flags & IEEE80211_CHAN_T) == IEEE80211_CHAN_T) #define IEEE80211_IS_CHAN_TG(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_TG) == IEEE80211_CHAN_TG) +#define IEEE80211_IS_CHAN_N(_c) \ + (((_c)->ic_flags & IEEE80211_CHAN_HT) == IEEE80211_CHAN_HT) #define IEEE80211_IS_CHAN_2GHZ(_c) \ (((_c)->ic_flags & IEEE80211_CHAN_2GHZ) != 0) @@ -313,12 +317,17 @@ struct ieee80211com { u_int ic_dtim_period; u_int ic_dtim_count; +#ifndef IEEE80211_NO_HT u_int32_t ic_txbfcaps; u_int16_t ic_htcaps; + u_int8_t ic_sup_mcs[howmany(80, NBBY)]; + u_int16_t ic_max_rxrate; /* in Mb/s, 0 <= rate <= 1023 */ + u_int8_t ic_tx_mcs_set; u_int16_t ic_htxcaps; u_int8_t ic_aselcaps; - u_int8_t ic_sup_mcs[16]; u_int8_t ic_dialog_token; + int ic_fixed_mcs; +#endif LIST_HEAD(, ieee80211_vap) ic_vaps; }; @@ -391,6 +400,11 @@ int ieee80211_fix_rate(struct ieee80211c uint64_t ieee80211_rate2media(struct ieee80211com *, int, enum ieee80211_phymode); int ieee80211_media2rate(uint64_t); +#ifndef IEEE80211_NO_HT +uint64_t ieee80211_mcs2media(struct ieee80211com *, int, + enum ieee80211_phymode); +int ieee80211_media2mcs(uint64_t); +#endif u_int8_t ieee80211_rate2plcp(u_int8_t, enum ieee80211_phymode); u_int8_t ieee80211_plcp2rate(u_int8_t, enum ieee80211_phymode); u_int ieee80211_mhz2ieee(u_int, u_int);