Fix iwn(4) with 4965 devices.
authorstsp <stsp@openbsd.org>
Thu, 11 Nov 2021 13:36:58 +0000 (13:36 +0000)
committerstsp <stsp@openbsd.org>
Thu, 11 Nov 2021 13:36:58 +0000 (13:36 +0000)
Our driver was using the wrong data structure for RXON_ASSOC commands on
4965 devices. This resulted in fatal firmware errors during association.

Problem found and fix tested on 4965 by jsg@.
Patch also tested on 6200 by me.

sys/dev/pci/if_iwn.c
sys/dev/pci/if_iwnreg.h
sys/dev/pci/if_iwnvar.h

index aff55ac..594ec00 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwn.c,v 1.250 2021/10/11 09:01:05 stsp Exp $       */
+/*     $OpenBSD: if_iwn.c,v 1.251 2021/11/11 13:36:58 stsp Exp $       */
 
 /*-
  * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -249,7 +249,9 @@ void                iwn_delete_key(struct ieee80211com *, struct ieee80211_node *,
                    struct ieee80211_key *);
 void           iwn_updateprot(struct ieee80211com *);
 void           iwn_updateslot(struct ieee80211com *);
-void           iwn_update_rxon(struct iwn_softc *);
+void           iwn_update_rxon_restore_power(struct iwn_softc *);
+void           iwn5000_update_rxon(struct iwn_softc *);
+void           iwn4965_update_rxon(struct iwn_softc *);
 int            iwn_ampdu_rx_start(struct ieee80211com *,
                    struct ieee80211_node *, uint8_t);
 void           iwn_ampdu_rx_stop(struct ieee80211com *,
@@ -585,6 +587,7 @@ iwn4965_attach(struct iwn_softc *sc, pci_product_id_t pid)
        ops->nic_config = iwn4965_nic_config;
        ops->reset_sched = iwn4965_reset_sched;
        ops->update_sched = iwn4965_update_sched;
+       ops->update_rxon = iwn4965_update_rxon;
        ops->get_temperature = iwn4965_get_temperature;
        ops->get_rssi = iwn4965_get_rssi;
        ops->set_txpower = iwn4965_set_txpower;
@@ -624,6 +627,7 @@ iwn5000_attach(struct iwn_softc *sc, pci_product_id_t pid)
        ops->nic_config = iwn5000_nic_config;
        ops->reset_sched = iwn5000_reset_sched;
        ops->update_sched = iwn5000_update_sched;
+       ops->update_rxon = iwn5000_update_rxon;
        ops->get_temperature = iwn5000_get_temperature;
        ops->get_rssi = iwn5000_get_rssi;
        ops->set_txpower = iwn5000_set_txpower;
@@ -5668,7 +5672,7 @@ iwn_updateprot(struct ieee80211com *ic)
        sc->rxon.flags &= ~htole32(IWN_RXON_HT_PROTMODE(3));
        sc->rxon.flags |= htole32(IWN_RXON_HT_PROTMODE(htprot));
 
-       iwn_update_rxon(sc);
+       sc->ops.update_rxon(sc);
 }
 
 void
@@ -5689,13 +5693,37 @@ iwn_updateslot(struct ieee80211com *ic)
        else
                sc->rxon.flags &= ~htole32(IWN_RXON_SHPREAMBLE);
 
-       iwn_update_rxon(sc);
+       sc->ops.update_rxon(sc);
 }
+
 void
-iwn_update_rxon(struct iwn_softc *sc)
+iwn_update_rxon_restore_power(struct iwn_softc *sc)
 {
        struct ieee80211com *ic = &sc->sc_ic;
        struct iwn_ops *ops = &sc->ops;
+       int error;
+
+       DELAY(100);
+
+       /* All RXONs wipe the firmware's txpower table. Restore it. */
+       error = ops->set_txpower(sc, 1);
+       if (error != 0)
+               printf("%s: could not set TX power\n", sc->sc_dev.dv_xname);
+
+       DELAY(100);
+
+       /* Restore power saving level */
+       if (ic->ic_flags & IEEE80211_F_PMGTON)
+               error = iwn_set_pslevel(sc, 0, 3, 1);
+       else
+               error = iwn_set_pslevel(sc, 0, 0, 1);
+       if (error != 0)
+               printf("%s: could not set PS level\n", sc->sc_dev.dv_xname);
+}
+
+void
+iwn5000_update_rxon(struct iwn_softc *sc)
+{
        struct iwn_rxon_assoc rxon_assoc;
        int s, error;
 
@@ -5718,22 +5746,35 @@ iwn_update_rxon(struct iwn_softc *sc)
        if (error != 0)
                printf("%s: RXON_ASSOC command failed\n", sc->sc_dev.dv_xname);
 
-       DELAY(100);
+       iwn_update_rxon_restore_power(sc);
 
-       /* All RXONs wipe the firmware's txpower table. Restore it. */
-       error = ops->set_txpower(sc, 1);
-       if (error != 0)
-               printf("%s: could not set TX power\n", sc->sc_dev.dv_xname);
+       splx(s);
+}
 
-       DELAY(100);
+void
+iwn4965_update_rxon(struct iwn_softc *sc)
+{
+       struct iwn4965_rxon_assoc rxon_assoc;
+       int s, error;
 
-       /* Restore power saving level */
-       if (ic->ic_flags & IEEE80211_F_PMGTON)
-               error = iwn_set_pslevel(sc, 0, 3, 1);
-       else
-               error = iwn_set_pslevel(sc, 0, 0, 1);
+       /* Update RXON config. */
+       memset(&rxon_assoc, 0, sizeof(rxon_assoc));
+       rxon_assoc.flags = sc->rxon.flags;
+       rxon_assoc.filter = sc->rxon.filter;
+       rxon_assoc.ofdm_mask = sc->rxon.ofdm_mask;
+       rxon_assoc.cck_mask = sc->rxon.cck_mask;
+       rxon_assoc.ht_single_mask = sc->rxon.ht_single_mask;
+       rxon_assoc.ht_dual_mask = sc->rxon.ht_dual_mask;
+       rxon_assoc.rxchain = sc->rxon.rxchain;
+
+       s = splnet();
+
+       error = iwn_cmd(sc, IWN_CMD_RXON_ASSOC, &rxon_assoc,
+           sizeof(rxon_assoc), 1);
        if (error != 0)
-               printf("%s: could not set PS level\n", sc->sc_dev.dv_xname);
+               printf("%s: RXON_ASSOC command failed\n", sc->sc_dev.dv_xname);
+
+       iwn_update_rxon_restore_power(sc);
 
        splx(s);
 }
index a8fc850..57e8841 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwnreg.h,v 1.56 2019/07/29 10:50:08 stsp Exp $     */
+/*     $OpenBSD: if_iwnreg.h,v 1.57 2021/11/11 13:36:58 stsp Exp $     */
 
 /*-
  * Copyright (c) 2007, 2008
@@ -546,6 +546,17 @@ struct iwn_rxon_assoc {
        uint32_t reserved3;
 } __packed;
 
+struct iwn4965_rxon_assoc {
+       uint32_t flags;
+       uint32_t filter;
+       uint8_t ofdm_mask;
+       uint8_t cck_mask;
+       uint8_t ht_single_mask;
+       uint8_t ht_dual_mask;
+       uint16_t rxchain;
+       uint16_t reserved;
+};
+
 #define IWN4965_RXONSZ (sizeof (struct iwn_rxon) - 6)
 #define IWN5000_RXONSZ (sizeof (struct iwn_rxon))
 
index e1c3e19..dc0534d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_iwnvar.h,v 1.40 2021/03/12 16:27:28 stsp Exp $     */
+/*     $OpenBSD: if_iwnvar.h,v 1.41 2021/11/11 13:36:58 stsp Exp $     */
 
 /*-
  * Copyright (c) 2007, 2008
@@ -171,6 +171,7 @@ struct iwn_ops {
        void            (*reset_sched)(struct iwn_softc *, int, int);
        void            (*update_sched)(struct iwn_softc *, int, int, uint8_t,
                            uint16_t);
+       void            (*update_rxon)(struct iwn_softc *);
        int             (*get_temperature)(struct iwn_softc *);
        int             (*get_rssi)(const struct iwn_rx_stat *);
        int             (*set_txpower)(struct iwn_softc *, int);