From b630d526b6cb56110fa962bf41d21ee7b14dc5d6 Mon Sep 17 00:00:00 2001 From: stsp Date: Thu, 11 Nov 2021 13:36:58 +0000 Subject: [PATCH] Fix iwn(4) with 4965 devices. 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 | 75 +++++++++++++++++++++++++++++++---------- sys/dev/pci/if_iwnreg.h | 13 ++++++- sys/dev/pci/if_iwnvar.h | 3 +- 3 files changed, 72 insertions(+), 19 deletions(-) diff --git a/sys/dev/pci/if_iwn.c b/sys/dev/pci/if_iwn.c index aff55acd0f1..594ec004b14 100644 --- a/sys/dev/pci/if_iwn.c +++ b/sys/dev/pci/if_iwn.c @@ -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 @@ -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); } diff --git a/sys/dev/pci/if_iwnreg.h b/sys/dev/pci/if_iwnreg.h index a8fc8504b7f..57e8841552c 100644 --- a/sys/dev/pci/if_iwnreg.h +++ b/sys/dev/pci/if_iwnreg.h @@ -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)) diff --git a/sys/dev/pci/if_iwnvar.h b/sys/dev/pci/if_iwnvar.h index e1c3e196d28..dc0534d3237 100644 --- a/sys/dev/pci/if_iwnvar.h +++ b/sys/dev/pci/if_iwnvar.h @@ -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); -- 2.20.1