summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Sperling <stsp@cvs.openbsd.org>2020-06-17 08:18:22 +0000
committerStefan Sperling <stsp@cvs.openbsd.org>2020-06-17 08:18:22 +0000
commitff9f7f3f2681a6765ab30ec14f313ea8ec311fab (patch)
tree2e6ab104161100224d9c775c6f955c60c7d87cab
parenta9e039da1fc634502477d43a955029829b7f1ad0 (diff)
Let iwx(4) firmware decide which Tx rate to use.
The firmware will notify the driver when it decides to change Tx rate. Based on those notifications the driver updates the value displayed by ifconfig. This is similar to how bwfm(4) and urtwn(4) handle this. Offloading Tx rate selection should allow us to eventually delete AMRR/MiRA support code from iwx(4). That code is disabled for now, not yet deleted. For now, the driver restricts firmware Tx rate selection to 11n/20MHz mode because that's what net80211 can support.
-rw-r--r--sys/dev/pci/if_iwx.c152
-rw-r--r--sys/dev/pci/if_iwxreg.h164
2 files changed, 309 insertions, 7 deletions
diff --git a/sys/dev/pci/if_iwx.c b/sys/dev/pci/if_iwx.c
index b5cc5abfc62..802619b1f16 100644
--- a/sys/dev/pci/if_iwx.c
+++ b/sys/dev/pci/if_iwx.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwx.c,v 1.26 2020/06/11 11:27:44 stsp Exp $ */
+/* $OpenBSD: if_iwx.c,v 1.27 2020/06/17 08:18:21 stsp Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -409,6 +409,9 @@ int iwx_scan(struct iwx_softc *);
int iwx_bgscan(struct ieee80211com *);
int iwx_umac_scan_abort(struct iwx_softc *);
int iwx_scan_abort(struct iwx_softc *);
+int iwx_rs_rval2idx(uint8_t);
+uint16_t iwx_rs_ht_rates(struct iwx_softc *, struct ieee80211_node *, int);
+int iwx_rs_init(struct iwx_softc *, struct iwx_node *);
int iwx_enable_data_tx_queues(struct iwx_softc *);
int iwx_auth(struct iwx_softc *);
int iwx_deauth(struct iwx_softc *);
@@ -3775,6 +3778,7 @@ iwx_rx_tx_cmd_single(struct iwx_softc *sc, struct iwx_rx_packet *pkt,
in->in_mn.retries += tx_resp->failure_frame;
if (txfail)
in->in_mn.txfail += tx_resp->frame_count;
+#if 0
if (ic->ic_state == IEEE80211_S_RUN && !in->ht_force_cck) {
int otxmcs = ni->ni_txmcs;
@@ -3785,6 +3789,7 @@ iwx_rx_tx_cmd_single(struct iwx_softc *sc, struct iwx_rx_packet *pkt,
otxmcs == 0 && ni->ni_txmcs == 0)
iwx_enable_ht_cck_fallback(sc, in);
}
+#endif
}
if (txfail)
@@ -4294,10 +4299,13 @@ iwx_tx_fill_cmd(struct iwx_softc *sc, struct iwx_node *in,
type != IEEE80211_FC0_TYPE_DATA) {
/* for non-data, use the lowest supported rate */
ridx = min_ridx;
+ flags |= IWX_TX_FLAGS_CMD_RATE;
} else if (ic->ic_fixed_mcs != -1) {
ridx = sc->sc_fixed_ridx;
+ flags |= IWX_TX_FLAGS_CMD_RATE;
} else if (ic->ic_fixed_rate != -1) {
ridx = sc->sc_fixed_ridx;
+ flags |= IWX_TX_FLAGS_CMD_RATE;
} else if ((ni->ni_flags & IEEE80211_NODE_HT) && !in->ht_force_cck) {
ridx = iwx_mcs2ridx[ni->ni_txmcs];
} else {
@@ -4308,7 +4316,7 @@ iwx_tx_fill_cmd(struct iwx_softc *sc, struct iwx_node *in,
ridx = min_ridx;
}
- flags = (IWX_TX_FLAGS_CMD_RATE | IWX_TX_FLAGS_ENCRYPT_DIS);
+ flags |= IWX_TX_FLAGS_ENCRYPT_DIS;
if ((ic->ic_flags & IEEE80211_F_RSNON) &&
ni->ni_rsn_supp_state == RSNA_SUPP_PTKNEGOTIATING)
flags |= IWX_TX_FLAGS_HIGH_PRI;
@@ -5742,6 +5750,120 @@ iwx_enable_data_tx_queues(struct iwx_softc *sc)
}
int
+iwx_rs_rval2idx(uint8_t rval)
+{
+ /* Firmware expects indices which match our 11g rate set. */
+ const struct ieee80211_rateset *rs = &ieee80211_std_rateset_11g;
+ int i;
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ if ((rs->rs_rates[i] & IEEE80211_RATE_VAL) == rval)
+ return i;
+ }
+
+ return -1;
+}
+
+uint16_t
+iwx_rs_ht_rates(struct iwx_softc *sc, struct ieee80211_node *ni, int rsidx)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ const struct ieee80211_ht_rateset *rs;
+ uint16_t htrates = 0;
+ int mcs;
+
+ rs = &ieee80211_std_ratesets_11n[rsidx];
+ for (mcs = rs->min_mcs; mcs <= rs->max_mcs; mcs++) {
+ if (!isset(ni->ni_rxmcs, mcs) ||
+ !isset(ic->ic_sup_mcs, mcs))
+ continue;
+ htrates |= (1 << (mcs - rs->min_mcs));
+ }
+
+ return htrates;
+}
+
+int
+iwx_rs_init(struct iwx_softc *sc, struct iwx_node *in)
+{
+ struct ieee80211_node *ni = &in->in_ni;
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+ struct iwx_tlc_config_cmd cfg_cmd;
+ uint32_t cmd_id;
+ int i;
+
+ memset(&cfg_cmd, 0, sizeof(cfg_cmd));
+
+ for (i = 0; i < rs->rs_nrates; i++) {
+ uint8_t rval = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ int idx = iwx_rs_rval2idx(rval);
+ if (idx == -1)
+ return EINVAL;
+ cfg_cmd.non_ht_rates |= (1 << idx);
+ }
+
+ if (ni->ni_flags & IEEE80211_NODE_HT) {
+ cfg_cmd.mode = IWX_TLC_MNG_MODE_HT;
+ cfg_cmd.ht_rates[IWX_TLC_NSS_1][IWX_TLC_HT_BW_NONE_160] =
+ iwx_rs_ht_rates(sc, ni, IEEE80211_HT_RATESET_SISO);
+ cfg_cmd.ht_rates[IWX_TLC_NSS_2][IWX_TLC_HT_BW_NONE_160] =
+ iwx_rs_ht_rates(sc, ni, IEEE80211_HT_RATESET_MIMO2);
+ } else
+ cfg_cmd.mode = IWX_TLC_MNG_MODE_NON_HT;
+
+ cfg_cmd.sta_id = IWX_STATION_ID;
+ cfg_cmd.max_ch_width = IWX_RATE_MCS_CHAN_WIDTH_20;
+ cfg_cmd.chains = IWX_TLC_MNG_CHAIN_A_MSK | IWX_TLC_MNG_CHAIN_B_MSK;
+ cfg_cmd.max_mpdu_len = IEEE80211_MAX_LEN;
+ if (ieee80211_node_supports_ht_sgi20(ni))
+ cfg_cmd.sgi_ch_width_supp = (1 << IWX_TLC_MNG_CH_WIDTH_20MHZ);
+
+ cmd_id = iwx_cmd_id(IWX_TLC_MNG_CONFIG_CMD, IWX_DATA_PATH_GROUP, 0);
+ return iwx_send_cmd_pdu(sc, cmd_id, IWX_CMD_ASYNC, sizeof(cfg_cmd),
+ &cfg_cmd);
+}
+
+void
+iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_update_notif *notif)
+{
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct ieee80211_rateset *rs = &ni->ni_rates;
+ uint32_t rate_n_flags;
+ int i;
+
+ if (notif->sta_id != IWX_STATION_ID ||
+ (le32toh(notif->flags) & IWX_TLC_NOTIF_FLAG_RATE) == 0)
+ return;
+
+ rate_n_flags = le32toh(notif->rate);
+ if (rate_n_flags & IWX_RATE_MCS_HT_MSK) {
+ ni->ni_txmcs = (rate_n_flags &
+ (IWX_RATE_HT_MCS_RATE_CODE_MSK |
+ IWX_RATE_HT_MCS_NSS_MSK));
+ } else {
+ uint8_t plcp = (rate_n_flags & IWX_RATE_LEGACY_RATE_MSK);
+ uint8_t rval = 0;
+ for (i = IWX_RATE_1M_INDEX; i < nitems(iwx_rates); i++) {
+ if (iwx_rates[i].plcp == plcp) {
+ rval = iwx_rates[i].rate;
+ break;
+ }
+ }
+ if (rval) {
+ uint8_t rv;
+ for (i = 0; i < rs->rs_nrates; i++) {
+ rv = rs->rs_rates[i] & IEEE80211_RATE_VAL;
+ if (rv == rval) {
+ ni->ni_txrate = i;
+ break;
+ }
+ }
+ }
+ }
+}
+
+int
iwx_auth(struct iwx_softc *sc)
{
struct ieee80211com *ic = &sc->sc_ic;
@@ -6025,11 +6147,17 @@ iwx_run(struct iwx_softc *sc)
in->in_ni.ni_txrate = 0;
in->in_ni.ni_txmcs = 0;
- if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_TLC_OFFLOAD))
- DPRINTF(("%s: TODO: Enable firmware rate scaling?\n",
- DEVNAME(sc)));
-
+ if (isset(sc->sc_enabled_capa, IWX_UCODE_TLV_CAPA_TLC_OFFLOAD)) {
+ err = iwx_rs_init(sc, in);
+ if (err) {
+ printf("%s: could not init rate scaling (error %d)\n",
+ DEVNAME(sc), err);
+ return err;
+ }
+ }
+#if 0
timeout_add_msec(&sc->sc_calib_to, 500);
+#endif
return 0;
}
@@ -7451,6 +7579,18 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *data, struct mbuf_list *ml)
case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_RX_NO_DATA_NOTIF):
break; /* happens in monitor mode; ignore for now */
+ case IWX_WIDE_ID(IWX_DATA_PATH_GROUP, IWX_TLC_MNG_CONFIG_CMD):
+ break;
+
+ case IWX_WIDE_ID(IWX_DATA_PATH_GROUP,
+ IWX_TLC_MNG_UPDATE_NOTIF): {
+ struct iwx_tlc_update_notif *notif;
+ SYNC_RESP_STRUCT(notif, pkt);
+ if (iwx_rx_packet_payload_len(pkt) == sizeof(*notif))
+ iwx_rs_update(sc, notif);
+ break;
+ }
+
default:
handled = 0;
printf("%s: unhandled firmware response 0x%x/0x%x "
diff --git a/sys/dev/pci/if_iwxreg.h b/sys/dev/pci/if_iwxreg.h
index c3fe26b44d0..f132fc7deb8 100644
--- a/sys/dev/pci/if_iwxreg.h
+++ b/sys/dev/pci/if_iwxreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwxreg.h,v 1.9 2020/06/11 08:20:33 stsp Exp $ */
+/* $OpenBSD: if_iwxreg.h,v 1.10 2020/06/17 08:18:21 stsp Exp $ */
/*-
* Based on BSD-licensed source modules in the Linux iwlwifi driver,
@@ -1598,7 +1598,9 @@ struct iwx_tx_queue_cfg_rsp {
/* DATA_PATH group subcommand IDs */
#define IWX_DQA_ENABLE_CMD 0x00
+#define IWX_TLC_MNG_CONFIG_CMD 0x0f
#define IWX_RX_NO_DATA_NOTIF 0xf5
+#define IWX_TLC_MNG_UPDATE_NOTIF 0xf7
/* REGULATORY_AND_NVM group subcommand IDs */
#define IWX_NVM_ACCESS_COMPLETE 0x00
@@ -4519,6 +4521,166 @@ enum {
#define IWX_LQ_FLAG_DYNAMIC_BW_POS 6
#define IWX_LQ_FLAG_DYNAMIC_BW_MSK (1 << IWX_LQ_FLAG_DYNAMIC_BW_POS)
+/**
+ * Options for TLC config flags
+ * @IWX_TLC_MNG_CFG_FLAGS_STBC_MSK: enable STBC. For HE this enables STBC for
+ * bandwidths <= 80MHz
+ * @IWX_TLC_MNG_CFG_FLAGS_LDPC_MSK: enable LDPC
+ * @IWX_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK: enable STBC in HE at 160MHz
+ * bandwidth
+ * @IWX_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK: enable HE Dual Carrier Modulation
+ * for BPSK (MCS 0) with 1 spatial
+ * stream
+ * @IWX_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK: enable HE Dual Carrier Modulation
+ * for BPSK (MCS 0) with 2 spatial
+ * streams
+ */
+#define IWX_TLC_MNG_CFG_FLAGS_STBC_MSK (1 << 0)
+#define IWX_TLC_MNG_CFG_FLAGS_LDPC_MSK (1 << 1)
+#define IWX_TLC_MNG_CFG_FLAGS_HE_STBC_160MHZ_MSK (1 << 2)
+#define IWX_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK (1 << 3)
+#define IWX_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_2_MSK (1 << 4)
+
+/**
+ * enum iwx_tlc_mng_cfg_cw - channel width options
+ * @IWX_TLC_MNG_CH_WIDTH_20MHZ: 20MHZ channel
+ * @IWX_TLC_MNG_CH_WIDTH_40MHZ: 40MHZ channel
+ * @IWX_TLC_MNG_CH_WIDTH_80MHZ: 80MHZ channel
+ * @IWX_TLC_MNG_CH_WIDTH_160MHZ: 160MHZ channel
+ * @IWX_TLC_MNG_CH_WIDTH_LAST: maximum value
+ */
+enum iwx_tlc_mng_cfg_cw {
+ IWX_TLC_MNG_CH_WIDTH_20MHZ,
+ IWX_TLC_MNG_CH_WIDTH_40MHZ,
+ IWX_TLC_MNG_CH_WIDTH_80MHZ,
+ IWX_TLC_MNG_CH_WIDTH_160MHZ,
+ IWX_TLC_MNG_CH_WIDTH_LAST = IWX_TLC_MNG_CH_WIDTH_160MHZ,
+};
+
+/**
+ * @IWX_TLC_MNG_CHAIN_A_MSK: chain A
+ * @IWX_TLC_MNG_CHAIN_B_MSK: chain B
+ */
+#define IWX_TLC_MNG_CHAIN_A_MSK (1 << 0)
+#define IWX_TLC_MNG_CHAIN_B_MSK (1 << 1)
+
+/**
+ * enum iwx_tlc_mng_cfg_mode - supported modes
+ * @IWX_TLC_MNG_MODE_CCK: enable CCK
+ * @IWX_TLC_MNG_MODE_OFDM_NON_HT: enable OFDM (non HT)
+ * @IWX_TLC_MNG_MODE_NON_HT: enable non HT
+ * @IWX_TLC_MNG_MODE_HT: enable HT
+ * @IWX_TLC_MNG_MODE_VHT: enable VHT
+ * @IWX_TLC_MNG_MODE_HE: enable HE
+ * @IWX_TLC_MNG_MODE_INVALID: invalid value
+ * @IWX_TLC_MNG_MODE_NUM: a count of possible modes
+ */
+enum iwx_tlc_mng_cfg_mode {
+ IWX_TLC_MNG_MODE_CCK = 0,
+ IWX_TLC_MNG_MODE_OFDM_NON_HT = IWX_TLC_MNG_MODE_CCK,
+ IWX_TLC_MNG_MODE_NON_HT = IWX_TLC_MNG_MODE_CCK,
+ IWX_TLC_MNG_MODE_HT,
+ IWX_TLC_MNG_MODE_VHT,
+ IWX_TLC_MNG_MODE_HE,
+ IWX_TLC_MNG_MODE_INVALID,
+ IWX_TLC_MNG_MODE_NUM = IWX_TLC_MNG_MODE_INVALID,
+};
+
+/**
+ * @IWX_TLC_MNG_HT_RATE_MCS0: index of MCS0
+ * @IWX_TLC_MNG_HT_RATE_MCS1: index of MCS1
+ * @IWX_TLC_MNG_HT_RATE_MCS2: index of MCS2
+ * @IWX_TLC_MNG_HT_RATE_MCS3: index of MCS3
+ * @IWX_TLC_MNG_HT_RATE_MCS4: index of MCS4
+ * @IWX_TLC_MNG_HT_RATE_MCS5: index of MCS5
+ * @IWX_TLC_MNG_HT_RATE_MCS6: index of MCS6
+ * @IWX_TLC_MNG_HT_RATE_MCS7: index of MCS7
+ * @IWX_TLC_MNG_HT_RATE_MCS8: index of MCS8
+ * @IWX_TLC_MNG_HT_RATE_MCS9: index of MCS9
+ * @IWX_TLC_MNG_HT_RATE_MCS10: index of MCS10
+ * @IWX_TLC_MNG_HT_RATE_MCS11: index of MCS11
+ * @IWX_TLC_MNG_HT_RATE_MAX: maximal rate for HT/VHT
+ */
+enum iwx_tlc_mng_ht_rates {
+ IWX_TLC_MNG_HT_RATE_MCS0 = 0,
+ IWX_TLC_MNG_HT_RATE_MCS1,
+ IWX_TLC_MNG_HT_RATE_MCS2,
+ IWX_TLC_MNG_HT_RATE_MCS3,
+ IWX_TLC_MNG_HT_RATE_MCS4,
+ IWX_TLC_MNG_HT_RATE_MCS5,
+ IWX_TLC_MNG_HT_RATE_MCS6,
+ IWX_TLC_MNG_HT_RATE_MCS7,
+ IWX_TLC_MNG_HT_RATE_MCS8,
+ IWX_TLC_MNG_HT_RATE_MCS9,
+ IWX_TLC_MNG_HT_RATE_MCS10,
+ IWX_TLC_MNG_HT_RATE_MCS11,
+ IWX_TLC_MNG_HT_RATE_MAX = IWX_TLC_MNG_HT_RATE_MCS11,
+};
+
+#define IWX_TLC_NSS_1 0
+#define IWX_TLC_NSS_2 1
+#define IWX_TLC_NSS_MAX 2
+
+#define IWX_TLC_HT_BW_NONE_160 0
+#define IWX_TLC_HT_BW_160 1
+
+/**
+ * struct iwx_tlc_config_cmd - TLC configuration
+ * @sta_id: station id
+ * @reserved1: reserved
+ * @max_ch_width: max supported channel width from @enum iwx_tlc_mng_cfg_cw
+ * @mode: &enum iwx_tlc_mng_cfg_mode
+ * @chains: bitmask of IWX_TLC_MNG_CHAIN_*_MSK
+ * @amsdu: 1 = TX amsdu is supported, 0 = not supported
+ * @flags: bitmask of IWX_TLC_MNG_CFG_*
+ * @non_ht_rates: bitmap of supported legacy rates
+ * @ht_rates: bitmap of &enum iwx_tlc_mng_ht_rates, per <nss, channel-width>
+ * pair (0 - 80mhz width and below, 1 - 160mhz).
+ * @max_mpdu_len: max MPDU length, in bytes
+ * @sgi_ch_width_supp: bitmap of SGI support per channel width
+ * use (1 << @enum iwx_tlc_mng_cfg_cw)
+ * @reserved2: reserved
+ */
+struct iwx_tlc_config_cmd {
+ uint8_t sta_id;
+ uint8_t reserved1[3];
+ uint8_t max_ch_width;
+ uint8_t mode;
+ uint8_t chains;
+ uint8_t amsdu;
+ uint16_t flags;
+ uint16_t non_ht_rates;
+ uint16_t ht_rates[IWX_TLC_NSS_MAX][2];
+ uint16_t max_mpdu_len;
+ uint8_t sgi_ch_width_supp;
+ uint8_t reserved2[1];
+} __packed; /* TLC_MNG_CONFIG_CMD_API_S_VER_2 */
+
+/**
+ * @IWX_TLC_NOTIF_FLAG_RATE: last initial rate update
+ * @IWX_TLC_NOTIF_FLAG_AMSDU: umsdu parameters update
+ */
+#define IWX_TLC_NOTIF_FLAG_RATE (1 << 0)
+#define IWX_TLC_NOTIF_FLAG_AMSDU (1 << 1)
+
+/**
+ * struct iwx_tlc_update_notif - TLC notification from FW
+ * @sta_id: station id
+ * @reserved: reserved
+ * @flags: bitmap of notifications reported
+ * @rate: current initial rate
+ * @amsdu_size: Max AMSDU size, in bytes
+ * @amsdu_enabled: bitmap for per-TID AMSDU enablement
+ */
+struct iwx_tlc_update_notif {
+ uint8_t sta_id;
+ uint8_t reserved[3];
+ uint32_t flags;
+ uint32_t rate;
+ uint32_t amsdu_size;
+ uint32_t amsdu_enabled;
+} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */
+
/* Antenna flags. */
#define IWX_ANT_A (1 << 0)
#define IWX_ANT_B (1 << 1)