diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2015-12-14 08:34:57 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2015-12-14 08:34:57 +0000 |
commit | d249327cf40e1c97db78940e930af21c68286bf2 (patch) | |
tree | b99300efa59177eb76e1ee6288ba555cbf869497 | |
parent | 3cdcae4136d298c43f33cc0eaffab247167d9466 (diff) |
Add initial 802.11n support to the iwm(4) driver.
Adds support for HT MCS 0-7 (theoretical limit 65 Mbit/s) and the
reception of A-MSDU and A-MPDU aggregated frames.
None of the optional 11n features are supported for now.
MIMO, 40Mhz channels, short guard interval, etc. are left for future work.
And we're not sending A-MSDU or A-MPDU frames yet either.
Tested with various 11a/b/g/n access points. With some APs I'm seeing
a noticable increase in throughput, especially on 5Ghz.
Also, fix automatic rate selection by using the current Tx rate selected
by AMRR as the upper bound for the firmware's rate table and updating the
firmware's table whenever AMRR switches Tx rate, rather than setting the
table just once after association and ignoring AMRR updates.
ok mpi@ krw@ (earlier version), ok jasper@
-rw-r--r-- | sys/dev/pci/if_iwm.c | 546 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmreg.h | 60 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmvar.h | 17 |
3 files changed, 495 insertions, 128 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index 0511da53828..e96cb93db0f 100644 --- a/sys/dev/pci/if_iwm.c +++ b/sys/dev/pci/if_iwm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwm.c,v 1.70 2015/12/11 16:07:02 mpi Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.71 2015/12/14 08:34:56 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -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 *, uint16_t, uint16_t, uint16_t, 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 *sc, * 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 *sc, const uint16_t * const nvm_ch_flags) 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 8-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. upon 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, struct iwm_node *in, 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,39 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, if (type != IEEE80211_FC0_TYPE_DATA) { /* for non-data, use the lowest supported rate */ - ridx = (ic->ic_curmode == IEEE80211_MODE_11A) ? + ridx = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ? 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 +3897,14 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, 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 +3969,11 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) 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 +4969,30 @@ iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in, 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); @@ -5115,14 +5362,37 @@ iwm_calib_timeout(void *arg) { struct iwm_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; - int s; + struct iwm_node *in = (void *)ic->ic_bss; + struct ieee80211_node *ni = &in->in_ni; + int s, otxrate; 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; +#ifndef IEEE80211_NO_HT + if (ni->ni_flags & IEEE80211_NODE_HT) + otxrate = ni->ni_txmcs; + else +#endif + otxrate = ni->ni_txrate; ieee80211_amrr_choose(&sc->sc_amrr, &in->in_ni, &in->in_amn); + + /* + * If AMRR has chosen a new TX rate we must update + * the firwmare's LQ rate table from process context. + */ +#ifndef IEEE80211_NO_HT + if ((ni->ni_flags & IEEE80211_NODE_HT) && + otxrate != ni->ni_txmcs) + task_add(systq, &sc->setrates_task); + else +#endif + if (otxrate != ni->ni_txrate) + task_add(systq, &sc->setrates_task); } splx(s); @@ -5130,83 +5400,94 @@ iwm_calib_timeout(void *arg) } void +iwm_setrates_task(void *arg) +{ + struct iwm_softc *sc = arg; + struct ieee80211com *ic = &sc->sc_ic; + struct iwm_node *in = (struct iwm_node *)ic->ic_bss; + + /* Update rates table based on new TX rate determined by AMRR. */ + iwm_setrates(in); +} + +void iwm_setrates(struct iwm_node *in) { struct ieee80211_node *ni = &in->in_ni; 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) - 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; - } + struct ieee80211_rateset *rs = &ni->ni_rates; + int i, ridx, j, tab = 0; + struct iwm_host_cmd cmd = { + .id = IWM_LQ_CMD, + .len = { sizeof(in->in_lq), }, + .flags = IWM_CMD_SYNC, + }; - /* then construct a lq_cmd based on those */ 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. - */ - lq->single_stream_ant_msk = 1; - lq->dual_stream_ant_msk = 1; - - /* - * 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). + * Fill the LQ rate selection table with legacy and/or HT rates + * in descending order, i.e. with the node's current TX 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. */ - for (i = 0; i < nrates; i++) { - int nextant; + 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 = ni->ni_txmcs; i >= 0; 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 = ni->ni_txrate; i >= 0; i--) { + if (iwm_rates[ridx].rate == (rs->rs_rates[i] & + IEEE80211_RATE_VAL)) { + tab = iwm_rates[ridx].plcp; + break; + } + } + } - if (txant == 0) - txant = IWM_FW_VALID_TX_ANT(sc); - nextant = 1<<(ffs(txant)-1); - txant &= ~nextant; + if (tab == 0) + continue; - 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); } - /* 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; + /* 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; + + cmd.data[0] = &in->in_lq; + if (iwm_send_cmd(sc, &cmd) != 0) { + DPRINTF(("%s: IWM_LQ_CMD failed\n", DEVNAME(sc))); + } } int @@ -5221,6 +5502,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; @@ -5327,22 +5613,20 @@ iwm_newstate_task(void *psc) break; case IEEE80211_S_RUN: { - struct iwm_host_cmd cmd = { - .id = IWM_LQ_CMD, - .len = { sizeof(in->in_lq), }, - .flags = IWM_CMD_SYNC, - }; - in = (struct iwm_node *)ic->ic_bss; iwm_mvm_power_mac_update_mode(sc, in); iwm_mvm_enable_beacon_filter(sc, in); iwm_mvm_update_quotas(sc, in); - iwm_setrates(in); - cmd.data[0] = &in->in_lq; - if ((error = iwm_send_cmd(sc, &cmd)) != 0) { - DPRINTF(("%s: IWM_LQ_CMD failed\n", DEVNAME(sc))); - } + /* init amrr */ + ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn); + + /* Start at lowest available bit-rate, AMRR will raise. */ + in->in_ni.ni_txrate = 0; +#ifndef IEEE80211_NO_HT + in->in_ni.ni_txmcs = 0; +#endif + iwm_setrates(in); timeout_add_msec(&sc->sc_calib_to, 500); iwm_mvm_led_enable(sc); @@ -5630,6 +5914,10 @@ 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); + task_del(systq, &sc->setrates_task); +#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 +6603,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 +6626,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 +6819,14 @@ iwm_attach(struct device *parent, struct device *self, void *aux) 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 +6863,10 @@ iwm_attach(struct device *parent, struct device *self, void *aux) 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); + task_set(&sc->setrates_task, iwm_setrates_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 diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h index 9347eb517af..fb6b93aaf2a 100644 --- a/sys/dev/pci/if_iwmreg.h +++ b/sys/dev/pci/if_iwmreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmreg.h,v 1.8 2015/12/10 17:34:14 mmcc Exp $ */ +/* $OpenBSD: if_iwmreg.h,v 1.9 2015/12/14 08:34:56 stsp Exp $ */ /****************************************************************************** * @@ -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, }; /* diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h index 62ad6592fbb..9be58f2706d 100644 --- a/sys/dev/pci/if_iwmvar.h +++ b/sys/dev/pci/if_iwmvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmvar.h,v 1.13 2015/12/08 17:10:02 stsp Exp $ */ +/* $OpenBSD: if_iwmvar.h,v 1.14 2015/12/14 08:34:56 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -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; @@ -365,9 +366,18 @@ struct iwm_softc { struct task init_task; struct task newstate_task; + struct task setrates_task; 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 +445,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 +510,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 |