diff options
Diffstat (limited to 'sys/dev/pci')
-rw-r--r-- | sys/dev/pci/if_iwm.c | 257 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmvar.h | 8 |
2 files changed, 82 insertions, 183 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index 45e4446d79d..7d7f0697a2b 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.234 2019/01/23 10:08:48 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.235 2019/02/24 09:37:18 stsp Exp $ */ /* * Copyright (c) 2014, 2016 genua gmbh <info@genua.de> @@ -217,6 +217,7 @@ const struct iwm_rate { #define IWM_RIDX_MAX (nitems(iwm_rates)-1) #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) +#define IWM_RVAL_IS_OFDM(_i_) ((_i_) >= 12 && (_i_) != 22) /* Convert an MCS index into an iwm_rates[] index. */ const int iwm_mcs2ridx[] = { @@ -368,6 +369,7 @@ void iwm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *, int iwm_get_noise(const struct iwm_statistics_rx_non_phy *); void iwm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); +void iwm_enable_ht_cck_fallback(struct iwm_softc *, struct iwm_node *); void iwm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); void iwm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, @@ -447,7 +449,6 @@ int iwm_run(struct iwm_softc *); int iwm_run_stop(struct iwm_softc *); struct ieee80211_node *iwm_node_alloc(struct ieee80211com *); void iwm_calib_timeout(void *); -void iwm_setrates(struct iwm_node *); int iwm_media_change(struct ifnet *); void iwm_newstate_task(void *); int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); @@ -3574,6 +3575,37 @@ iwm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, } void +iwm_enable_ht_cck_fallback(struct iwm_softc *sc, struct iwm_node *in) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_node *ni = &in->in_ni; + struct ieee80211_rateset *rs = &ni->ni_rates; + uint8_t rval = (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL); + uint8_t min_rval = ieee80211_min_basic_rate(ic); + int i; + + /* Are CCK frames forbidden in our BSS? */ + if (IWM_RVAL_IS_OFDM(min_rval)) + return; + + in->ht_force_cck = 1; + + ieee80211_mira_cancel_timeouts(&in->in_mn); + ieee80211_mira_node_init(&in->in_mn); + ieee80211_amrr_node_init(&sc->sc_amrr, &in->in_amn); + + /* Choose initial CCK Tx rate. */ + ni->ni_txrate = 0; + for (i = 0; i < rs->rs_nrates; i++) { + rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL); + if (rval == min_rval) { + ni->ni_txrate = i; + break; + } + } +} + +void iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_node *in) { @@ -3590,12 +3622,18 @@ iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, status != IWM_TX_STATUS_DIRECT_DONE); /* Update rate control statistics. */ - if ((ni->ni_flags & IEEE80211_NODE_HT) == 0) { + if ((ni->ni_flags & IEEE80211_NODE_HT) == 0 || in->ht_force_cck) { in->in_amn.amn_txcnt++; - if (tx_resp->failure_frame > 0) + if (in->ht_force_cck) { + /* + * We want to move back to OFDM quickly if possible. + * Only show actual Tx failures to AMRR, not retries. + */ + if (txfail) + in->in_amn.amn_retrycnt++; + } else if (tx_resp->failure_frame > 0) in->in_amn.amn_retrycnt++; } else if (ic->ic_fixed_mcs == -1) { - int omcs = ni->ni_txmcs; in->in_mn.frames += tx_resp->frame_count; in->in_mn.ampdu_size = le16toh(tx_resp->byte_cnt); in->in_mn.agglen = tx_resp->frame_count; @@ -3603,14 +3641,16 @@ iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, in->in_mn.retries += tx_resp->failure_frame; if (txfail) in->in_mn.txfail += tx_resp->frame_count; - if (ic->ic_state == IEEE80211_S_RUN) + if (ic->ic_state == IEEE80211_S_RUN && !in->ht_force_cck) { + int otxmcs = ni->ni_txmcs; + ieee80211_mira_choose(&in->in_mn, ic, &in->in_ni); - /* - * If MiRA has chosen a new TX rate we must update - * the firwmare's LQ rate table from process context. - */ - if (omcs != ni->ni_txmcs) - iwm_add_task(sc, systq, &sc->setrates_task); + + /* Fall back to CCK rates if MCS 0 is failing. */ + if (txfail && IEEE80211_IS_CHAN_2GHZ(ni->ni_chan) && + otxmcs == 0 && ni->ni_txmcs == 0) + iwm_enable_ht_cck_fallback(sc, in); + } } if (txfail) @@ -4112,41 +4152,32 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni = &in->in_ni; + struct ieee80211_rateset *rs = &ni->ni_rates; const struct iwm_rate *rinfo; int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; - int ridx, rate_flags, i; - int nrates = ni->ni_rates.rs_nrates; + int min_ridx = iwm_rval2ridx(ieee80211_min_basic_rate(ic)); + int ridx, rate_flags; tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; - tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; + tx->data_retry_limit = IWM_LOW_RETRY_LIMIT; if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA) { /* for non-data, use the lowest supported rate */ - ridx = iwm_rval2ridx(ieee80211_min_basic_rate(ic)); + ridx = min_ridx; tx->data_retry_limit = IWM_MGMT_DFAULT_RETRY_LIMIT; } else if (ic->ic_fixed_mcs != -1) { ridx = sc->sc_fixed_ridx; } else if (ic->ic_fixed_rate != -1) { ridx = sc->sc_fixed_ridx; + } else if ((ni->ni_flags & IEEE80211_NODE_HT) && !in->ht_force_cck) { + ridx = iwm_mcs2ridx[ni->ni_txmcs]; } else { - /* for data frames, use RS table */ - tx->initial_rate_index = 0; - tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); - if (ni->ni_flags & IEEE80211_NODE_HT) { - ridx = iwm_mcs2ridx[ni->ni_txmcs]; - return &iwm_rates[ridx]; - } - ridx = (IEEE80211_IS_CHAN_5GHZ(ni->ni_chan)) ? - IWM_RIDX_OFDM : IWM_RIDX_CCK; - for (i = 0; i < nrates; i++) { - if (iwm_rates[i].rate == (ni->ni_txrate & - IEEE80211_RATE_VAL)) { - ridx = i; - break; - } - } - return &iwm_rates[ridx]; + uint8_t rval; + rval = (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL); + ridx = iwm_rval2ridx(rval); + if (ridx < min_ridx) + ridx = min_ridx; } rinfo = &iwm_rates[ridx]; @@ -4228,7 +4259,7 @@ iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) if ((ni->ni_flags & IEEE80211_NODE_HT) && !IEEE80211_IS_MULTICAST(wh->i_addr1) && type == IEEE80211_FC0_TYPE_DATA && - rinfo->plcp == IWM_RATE_INVM_PLCP) { + rinfo->ht_plcp != IWM_RATE_HT_SISO_MCS_INV_PLCP) { tap->wt_rate = (0x80 | rinfo->ht_plcp); } else tap->wt_rate = rinfo->rate; @@ -5172,6 +5203,8 @@ iwm_rval2ridx(int rval) int ridx; for (ridx = 0; ridx < nitems(iwm_rates); ridx++) { + if (iwm_rates[ridx].plcp == IWM_RATE_INVM_PLCP) + continue; if (rval == iwm_rates[ridx].rate) break; } @@ -5837,7 +5870,6 @@ iwm_run(struct iwm_softc *sc) /* Start at lowest available bit-rate, AMRR will raise. */ in->in_ni.ni_txrate = 0; in->in_ni.ni_txmcs = 0; - iwm_setrates(in); timeout_add_msec(&sc->sc_calib_to, 500); iwm_led_enable(sc); @@ -5900,157 +5932,25 @@ iwm_calib_timeout(void *arg) struct ieee80211com *ic = &sc->sc_ic; struct iwm_node *in = (void *)ic->ic_bss; struct ieee80211_node *ni = &in->in_ni; - int s, otxrate; + int s; s = splnet(); if ((ic->ic_fixed_rate == -1 || ic->ic_fixed_mcs == -1) && - ((ni->ni_flags & IEEE80211_NODE_HT) == 0) && + ((ni->ni_flags & IEEE80211_NODE_HT) == 0 || in->ht_force_cck) && ic->ic_opmode == IEEE80211_M_STA && ic->ic_bss) { - 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. - */ - if (otxrate != ni->ni_txrate) - iwm_add_task(sc, systq, &sc->setrates_task); - } - splx(s); - - timeout_add_msec(&sc->sc_calib_to, 500); -} - -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; - int s = splnet(); - - if (sc->sc_flags & IWM_FLAG_SHUTDOWN) { - refcnt_rele_wake(&sc->task_refs); - splx(s); - return; - } - - /* Update rates table based on new TX rate determined by AMRR. */ - iwm_setrates(in); - refcnt_rele_wake(&sc->task_refs); - splx(s); -} - -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; - struct ieee80211_rateset *rs = &ni->ni_rates; - int i, ridx, ridx_min, ridx_max, j, sgi_ok = 0, mimo, tab = 0; - struct iwm_host_cmd cmd = { - .id = IWM_LQ_CMD, - .len = { sizeof(in->in_lq), }, - }; - - memset(lq, 0, sizeof(*lq)); - lq->sta_id = IWM_STATION_ID; - - if (ic->ic_flags & IEEE80211_F_USEPROT) - lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; - - if ((ni->ni_flags & IEEE80211_NODE_HT) && - ieee80211_node_supports_ht_sgi20(ni)) { - ni->ni_flags |= IEEE80211_NODE_HT_SGI20; - sgi_ok = 1; - } - - /* - * 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. - */ - j = 0; - ridx_min = iwm_rval2ridx(ieee80211_min_basic_rate(ic)); - mimo = iwm_is_mimo_mcs(ni->ni_txmcs); - ridx_max = (mimo ? IWM_RIDX_MAX : IWM_LAST_HT_SISO_RATE); - for (ridx = ridx_max; ridx >= ridx_min; ridx--) { - uint8_t plcp = iwm_rates[ridx].plcp; - uint8_t ht_plcp = iwm_rates[ridx].ht_plcp; - - if (j >= nitems(lq->rs_table)) - break; - tab = 0; - if (ni->ni_flags & IEEE80211_NODE_HT) { - if (ht_plcp == IWM_RATE_HT_SISO_MCS_INV_PLCP) - continue; - /* Do not mix SISO and MIMO HT rates. */ - if ((mimo && !iwm_is_mimo_ht_plcp(ht_plcp)) || - (!mimo && iwm_is_mimo_ht_plcp(ht_plcp))) - continue; - for (i = ni->ni_txmcs; i >= 0; i--) { - if (isclr(ni->ni_rxmcs, i)) - continue; - if (ridx == iwm_mcs2ridx[i]) { - tab = ht_plcp; - tab |= IWM_RATE_MCS_HT_MSK; - if (sgi_ok) - tab |= IWM_RATE_MCS_SGI_MSK; - break; - } - } - } else if (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 = plcp; - break; - } - } + if (in->ht_force_cck) { + struct ieee80211_rateset *rs = &ni->ni_rates; + uint8_t rv; + rv = (rs->rs_rates[ni->ni_txrate] & IEEE80211_RATE_VAL); + if (IWM_RVAL_IS_OFDM(rv)) + in->ht_force_cck = 0; } - - if (tab == 0) - continue; - - if (iwm_is_mimo_ht_plcp(ht_plcp)) - tab |= IWM_RATE_MCS_ANT_AB_MSK; - else - tab |= IWM_RATE_MCS_ANT_A_MSK; - - if (IWM_RIDX_IS_CCK(ridx)) - tab |= IWM_RATE_MCS_CCK_MSK; - lq->rs_table[j++] = htole32(tab); - } - - lq->mimo_delim = (mimo ? j : 0); - - /* Fill the rest with the lowest possible rate */ - while (j < nitems(lq->rs_table)) { - tab = iwm_rates[ridx_min].plcp; - if (IWM_RIDX_IS_CCK(ridx_min)) - tab |= IWM_RATE_MCS_CCK_MSK; - tab |= IWM_RATE_MCS_ANT_A_MSK; - lq->rs_table[j++] = htole32(tab); } - lq->single_stream_ant_msk = IWM_ANT_A; - lq->dual_stream_ant_msk = IWM_ANT_AB; - - lq->agg_time_limit = htole16(4000); /* 4ms */ - lq->agg_disable_start_th = 3; -#ifdef notyet - lq->agg_frame_cnt_limit = 0x3f; -#else - lq->agg_frame_cnt_limit = 1; /* tx agg disabled */ -#endif + splx(s); - cmd.data[0] = &in->in_lq; - iwm_send_cmd(sc, &cmd); + timeout_add_msec(&sc->sc_calib_to, 500); } int @@ -6196,7 +6096,6 @@ iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg) if (ic->ic_state == IEEE80211_S_RUN) { timeout_del(&sc->sc_calib_to); ieee80211_mira_cancel_timeouts(&in->in_mn); - iwm_del_task(sc, systq, &sc->setrates_task); iwm_del_task(sc, systq, &sc->ba_task); iwm_del_task(sc, systq, &sc->htprot_task); } @@ -6705,7 +6604,6 @@ iwm_stop(struct ifnet *ifp) /* Cancel scheduled tasks and let any stale tasks finish up. */ task_del(systq, &sc->init_task); iwm_del_task(sc, sc->sc_nswq, &sc->newstate_task); - iwm_del_task(sc, systq, &sc->setrates_task); iwm_del_task(sc, systq, &sc->ba_task); iwm_del_task(sc, systq, &sc->htprot_task); KASSERT(sc->task_refs.refs >= 1); @@ -7895,7 +7793,6 @@ 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); task_set(&sc->ba_task, iwm_ba_task, sc); task_set(&sc->htprot_task, iwm_htprot_task, sc); diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h index 7e35820c7ce..e6593fbc840 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.37 2017/12/08 21:16:01 stsp Exp $ */ +/* $OpenBSD: if_iwmvar.h,v 1.38 2019/02/24 09:37:18 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -360,7 +360,6 @@ struct iwm_softc { struct task init_task; /* NB: not reference-counted */ struct refcnt task_refs; struct task newstate_task; - struct task setrates_task; enum ieee80211_state ns_nstate; int ns_arg; @@ -509,9 +508,12 @@ struct iwm_node { uint16_t in_id; uint16_t in_color; - struct iwm_lq_cmd in_lq; struct ieee80211_amrr_node in_amn; struct ieee80211_mira_node in_mn; + + /* Set in 11n mode if we don't receive ACKs for OFDM frames. */ + int ht_force_cck; + }; #define IWM_STATION_ID 0 #define IWM_AUX_STA_ID 1 |