Fix iwx(4) PHY context updates for newer firmware versions.
authorstsp <stsp@openbsd.org>
Sun, 18 Jul 2021 11:40:31 +0000 (11:40 +0000)
committerstsp <stsp@openbsd.org>
Sun, 18 Jul 2021 11:40:31 +0000 (11:40 +0000)
Firmware which advertises the BINDING_CDB_SUPPORT capability
needs a remove+add dance when the channel band has changed.

See Linux commit 730a18912bcbde0b94ae7f1b554a9908b3424a22
and Linux commit 91109f42d0ad0c0c282d1fa1257a1548977aa895

The same fix was applied to iwm(4) in CVS commit uQ0WjqRUp03vxHg1

sys/dev/pci/if_iwx.c

index 73cf9b8..406042a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwx.c,v 1.63 2021/07/06 15:53:33 stsp Exp $        */
+/*     $OpenBSD: if_iwx.c,v 1.64 2021/07/18 11:40:31 stsp Exp $        */
 
 /*
  * Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -407,6 +407,8 @@ 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_phy_ctxt_update(struct iwx_softc *, struct iwx_phy_ctxt *,
+           struct ieee80211_channel *, uint8_t, uint8_t, uint32_t);
 int    iwx_auth(struct iwx_softc *);
 int    iwx_deauth(struct iwx_softc *);
 int    iwx_assoc(struct iwx_softc *);
@@ -6318,6 +6320,47 @@ iwx_rs_update(struct iwx_softc *sc, struct iwx_tlc_update_notif *notif)
        }
 }
 
+int
+iwx_phy_ctxt_update(struct iwx_softc *sc, struct iwx_phy_ctxt *phyctxt,
+    struct ieee80211_channel *chan, uint8_t chains_static,
+    uint8_t chains_dynamic, uint32_t apply_time)
+{
+       uint16_t band_flags = (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ);
+       int err;
+
+       if (isset(sc->sc_enabled_capa,
+           IWX_UCODE_TLV_CAPA_BINDING_CDB_SUPPORT) &&
+           (phyctxt->channel->ic_flags & band_flags) !=
+           (chan->ic_flags & band_flags)) {
+               err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
+                   chains_dynamic, IWX_FW_CTXT_ACTION_REMOVE, apply_time);
+               if (err) {
+                       printf("%s: could not remove PHY context "
+                           "(error %d)\n", DEVNAME(sc), err);
+                       return err;
+               }
+               phyctxt->channel = chan;
+               err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
+                   chains_dynamic, IWX_FW_CTXT_ACTION_ADD, apply_time);
+               if (err) {
+                       printf("%s: could not remove PHY context "
+                           "(error %d)\n", DEVNAME(sc), err);
+                       return err;
+               }
+       } else {
+               phyctxt->channel = chan;
+               err = iwx_phy_ctxt_cmd(sc, phyctxt, chains_static,
+                   chains_dynamic, IWX_FW_CTXT_ACTION_MODIFY, apply_time);
+               if (err) {
+                       printf("%s: could not update PHY context (error %d)\n",
+                           DEVNAME(sc), err);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
 int
 iwx_auth(struct iwx_softc *sc)
 {
@@ -6328,16 +6371,16 @@ iwx_auth(struct iwx_softc *sc)
 
        splassert(IPL_NET);
 
-       if (ic->ic_opmode == IEEE80211_M_MONITOR)
-               sc->sc_phyctxt[0].channel = ic->ic_ibss_chan;
-       else
-               sc->sc_phyctxt[0].channel = in->in_ni.ni_chan;
-       err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
-           IWX_FW_CTXT_ACTION_MODIFY, 0);
-       if (err) {
-               printf("%s: could not update PHY context (error %d)\n",
-                   DEVNAME(sc), err);
-               return err;
+       if (ic->ic_opmode == IEEE80211_M_MONITOR) {
+               err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
+                   ic->ic_ibss_chan, 1, 1, 0);
+               if (err)
+                       return err;
+       } else {
+               err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
+                   in->in_ni.ni_chan, 1, 1, 0);
+               if (err)
+                       return err;
        }
        in->in_phyctxt = &sc->sc_phyctxt[0];
 
@@ -6461,6 +6504,12 @@ iwx_deauth(struct iwx_softc *sc)
                sc->sc_flags &= ~IWX_FLAG_MAC_ACTIVE;
        }
 
+       /* Move unused PHY context to a default channel. */
+       err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
+           &ic->ic_channels[1], 1, 1, 0);
+       if (err)
+               return err;
+
        return 0;
 }
 
@@ -6535,11 +6584,10 @@ iwx_run(struct iwx_softc *sc)
        if ((ic->ic_opmode == IEEE80211_M_MONITOR ||
            (in->in_ni.ni_flags & IEEE80211_NODE_HT)) &&
            iwx_mimo_enabled(sc)) {
-               err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0],
-                   2, 2, IWX_FW_CTXT_ACTION_MODIFY, 0);
+               err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
+                   in->in_ni.ni_chan, 2, 2, 0);
                if (err) {
-                       printf("%s: failed to update PHY\n",
-                           DEVNAME(sc));
+                       printf("%s: failed to update PHY\n", DEVNAME(sc));
                        return err;
                }
        }
@@ -6655,8 +6703,8 @@ iwx_run_stop(struct iwx_softc *sc)
        /* Reset Tx chains in case MIMO was enabled. */
        if ((in->in_ni.ni_flags & IEEE80211_NODE_HT) &&
            iwx_mimo_enabled(sc)) {
-               err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[0], 1, 1,
-                   IWX_FW_CTXT_ACTION_MODIFY, 0);
+               err = iwx_phy_ctxt_update(sc, &sc->sc_phyctxt[0],
+                  in->in_ni.ni_chan, 1, 1, 0);
                if (err) {
                        printf("%s: failed to update PHY\n", DEVNAME(sc));
                        return err;
@@ -7312,12 +7360,13 @@ iwx_init_hw(struct iwx_softc *sc)
                goto err;
        }
 
-       for (i = 0; i < 1; i++) {
+       for (i = 0; i < IWX_NUM_PHY_CTX; i++) {
                /*
                 * The channel used here isn't relevant as it's
                 * going to be overwritten in the other flows.
                 * For now use the first channel we have.
                 */
+               sc->sc_phyctxt[i].id = i;
                sc->sc_phyctxt[i].channel = &ic->ic_channels[1];
                err = iwx_phy_ctxt_cmd(sc, &sc->sc_phyctxt[i], 1, 1,
                    IWX_FW_CTXT_ACTION_ADD, 0);