Intel has moved Tx rate selection code from the Linux iwlwifi driver into
ax200 device firmware. There is code in iwlwifi/mvm/rs.c which should more
or less match what the firmware is doing, given how the firmware API works.

Equivalent functionality is offered by AMRR/MiRA in our kernel.
During initial development I decided to keep Tx rate selection in the driver.
The firmware still allows the driver to force a Tx rate so I could reuse
Tx rate selection code inherited from iwm(4). But going forward it makes
sense to reduce our differences in behaviour to the Linux driver.

Offloading Tx rate selection should allow us to eventually delete a
non-trivial amount of code from iwx(4).
That code is disabled by my patch, not yet deleted.

For now, the driver restricts the firmware to 11n/20MHz operation because
that's what net80211 can support. Offloading Tx rate selection should make
it somewhat easier to eventually add 11ac support to iwx(4) since we won't
have to worry about adding 11ac support to MiRA first.

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.

The firmware's Tx rate selection algorithm tends to pick "faster" Tx rates
than our kernel. But I don't see much difference compared to AMRR/MiRA.
Keep in mind that because we don't offload WPA2 crypto yet a very fast
CPU is required for best performance.

ok?

diff 6b8d7eb2718cb6a24e8079826c06467f5510a0d7 
9eab1fff69f13bad0dd4fd1b1f9d77e6adca61f7
blob - fdf9db0c88ec9d3c196b298b785a307dfc74d482
blob + 113362f4c0f4a672dc8ef719bd95964a53817db5
--- sys/dev/pci/if_iwx.c
+++ sys/dev/pci/if_iwx.c
@@ -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_
                        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_
                            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 
            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 
                        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;
@@ -5751,6 +5759,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;
@@ -6034,11 +6156,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;
 }
 
@@ -7459,6 +7587,18 @@ iwx_rx_pkt(struct iwx_softc *sc, struct iwx_rx_data *d
 
                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;
blob - 7781f264940a4444644deff624a05a3b9ce2b65d
blob + c49690425cac441d8e3e7068a8159076ffb0b1eb
--- sys/dev/pci/if_iwxreg.h
+++ sys/dev/pci/if_iwxreg.h
@@ -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
@@ -4518,6 +4520,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)

Reply via email to