summaryrefslogtreecommitdiff
path: root/sys/dev/pci
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci')
-rw-r--r--sys/dev/pci/if_iwm.c257
-rw-r--r--sys/dev/pci/if_iwmvar.h8
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