Download raw body.
fix qwx roaming (Re: sys/qwx,sys/qwz: report RSSI as percentage)
On Fri, May 22, 2026 at 12:58:40AM +0200, Kirill A. Korinsky wrote:
> Next, roaming. It doesn't work. It never jumped to AP in direct view of
> laptop when I saw -90 dBm. And iwx laptop jumped to it.
This fixes roaming on qwx for me. ok?
M sys/dev/ic/qwx.c | 210+ 31-
M sys/dev/ic/qwxvar.h | 7+ 0-
M sys/dev/pci/if_qwx_pci.c | 1+ 0-
3 files changed, 218 insertions(+), 31 deletions(-)
commit - ca362689551c3ca46673929394339d3a1d8b4983
commit + f605e0317ada28d37acd9064fd79920e6b9ca1cb
blob - e10ad8ce38428ef4a0dd316e271561c1b91c5f7d
blob + 023f95a3cada9608773b35112d54f81e489ad8c2
--- sys/dev/ic/qwx.c
+++ sys/dev/ic/qwx.c
@@ -323,6 +323,7 @@ qwx_init(struct ifnet *ifp)
return error;
ifp->if_flags |= IFF_RUNNING;
+ sc->ops.irq_enable(sc);
ieee80211_begin_scan(ifp);
}
@@ -396,6 +397,18 @@ qwx_mfp_leave(struct qwx_softc *sc)
}
void
+qwx_del_task_all(struct qwx_softc *sc)
+{
+ qwx_del_task(sc, sc->sc_nswq, &sc->newstate_task);
+ qwx_del_task(sc, systq, &sc->setkey_task);
+ qwx_setkey_clear(sc);
+ qwx_del_task(sc, systq, &sc->ba_task);
+ qwx_del_task(sc, systq, &sc->bgscan_task);
+ qwx_del_task(sc, systq, &sc->bgscan_done_task);
+ qwx_del_task(sc, systq, &sc->set_cc_task);
+}
+
+void
qwx_stop(struct ifnet *ifp)
{
struct qwx_softc *sc = ifp->if_softc;
@@ -419,20 +432,18 @@ qwx_stop(struct ifnet *ifp)
/* Cancel scheduled tasks and let any stale tasks finish up. */
task_del(systq, &sc->init_task);
- qwx_del_task(sc, sc->sc_nswq, &sc->newstate_task);
- qwx_del_task(sc, systq, &sc->setkey_task);
- qwx_del_task(sc, systq, &sc->ba_task);
- qwx_del_task(sc, systq, &sc->bgscan_task);
- qwx_del_task(sc, systq, &sc->set_cc_task);
+ qwx_del_task_all(sc);
refcnt_finalize(&sc->task_refs, "qwxstop");
- qwx_setkey_clear(sc);
-
ifp->if_timer = sc->sc_tx_timer = 0;
ifp->if_flags &= ~IFF_RUNNING;
ifq_clr_oactive(&ifp->if_snd);
+ free(sc->bgscan_unref_arg, M_DEVBUF, sc->bgscan_unref_arg_size);
+ sc->bgscan_unref_arg = NULL;
+ sc->bgscan_unref_arg_size = 0;
+
clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
/*
@@ -455,6 +466,8 @@ qwx_stop(struct ifnet *ifp)
sc->bss_peer_id = HAL_INVALID_PEERID;
}
+ sc->sc_flags &= ~QWX_FLAG_ROAMING;
+
sc->scan.state = ATH11K_SCAN_IDLE;
sc->vdev_id_11d_scan = QWX_11D_INVALID_VDEV_ID;
sc->pdevs_active = 0;
@@ -1098,9 +1111,7 @@ qwx_newstate(struct ieee80211com *ic, enum ieee80211_s
qwx_setkey_clear(sc);
qwx_del_task(sc, systq, &sc->bgscan_task);
-#if 0
qwx_del_task(sc, systq, &sc->bgscan_done_task);
-#endif
qwx_del_task(sc, systq, &sc->set_cc_task);
}
@@ -1144,6 +1155,15 @@ qwx_newstate_task(void *arg)
if (nstate <= ostate) {
switch (ostate) {
case IEEE80211_S_RUN:
+ if (sc->sc_flags & QWX_FLAG_ROAMING) {
+ /*
+ * Previous association state has already
+ * been torn down. Don't try do to it again.
+ */
+ sc->sc_flags &= ~QWX_FLAG_ROAMING;
+ break;
+ }
+
if (ic->ic_opmode == IEEE80211_M_STA &&
(ifp->if_flags & IFF_RUNNING) &&
(ic->ic_bss->ni_flags & IEEE80211_NODE_MFP) &&
@@ -1195,14 +1215,8 @@ next_scan:
printf("%s: %s -> %s\n", ifp->if_xname,
ieee80211_state_name[ic->ic_state],
ieee80211_state_name[IEEE80211_S_SCAN]);
-#if 0
- if ((sc->sc_flags & QWX_FLAG_BGSCAN) == 0) {
-#endif
- ieee80211_set_link_state(ic, LINK_STATE_DOWN);
- ieee80211_node_cleanup(ic, ic->ic_bss);
-#if 0
- }
-#endif
+ ieee80211_set_link_state(ic, LINK_STATE_DOWN);
+ ieee80211_node_cleanup(ic, ic->ic_bss);
ic->ic_state = IEEE80211_S_SCAN;
refcnt_rele_wake(&sc->task_refs);
splx(s);
@@ -13707,8 +13721,11 @@ qwx_wmi_process_mgmt_tx_comp(struct qwx_softc *sc,
ieee80211_release_node(ic, tx_data->ni);
tx_data->ni = NULL;
- if (arvif->txmgmt.queued > 0)
+ if (arvif->txmgmt.queued > 0) {
arvif->txmgmt.queued--;
+ if (arvif->txmgmt.queued == 0)
+ wakeup(&arvif->txmgmt.queued);
+ }
if (tx_compl_param->status != 0)
ifp->if_oerrors++;
@@ -15980,8 +15997,11 @@ qwx_dp_tx_free_txbuf(struct qwx_softc *sc, int msdu_id
m_freem(tx_data->m);
tx_data->m = NULL;
- if (tx_ring->queued > 0)
+ if (tx_ring->queued > 0) {
tx_ring->queued--;
+ if (tx_ring->queued == 0)
+ wakeup(&tx_ring->queued);
+ }
}
if (tx_data->ni) {
@@ -16133,8 +16153,11 @@ qwx_dp_tx_complete_msdu(struct qwx_softc *sc, struct d
m_freem(tx_data->m);
tx_data->m = NULL;
- if (tx_ring->queued > 0)
+ if (tx_ring->queued > 0) {
tx_ring->queued--;
+ if (tx_ring->queued == 0)
+ wakeup(&tx_ring->queued);
+ }
}
if (tx_data->ni == NULL)
@@ -20926,7 +20949,6 @@ qwx_core_qmi_firmware_ready(struct qwx_softc *sc)
goto err_core_stop;
}
- sc->ops.irq_enable(sc);
#if 0
mutex_unlock(&ab->core_lock);
#endif
@@ -26304,6 +26326,171 @@ qwx_bgscan(struct ieee80211com *ic)
return 0;
}
+
+void
+qwx_bgscan_done_task(void *arg)
+{
+ struct qwx_softc *sc = arg;
+ struct qwx_dp *dp = &sc->dp;
+ struct qwx_vif *arvif = TAILQ_FIRST(&sc->vif_list); /* XXX */
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = &ic->ic_if;
+ struct ieee80211_node *ni = ic->ic_bss;
+ struct qwx_node *nq = (struct qwx_node *)ni;
+ int err = 0, s, i;
+
+ s = splnet();
+
+ /* Prevent races with ifconfig commands. */
+ if (rw_enter(&sc->ioctl_rwl, RW_WRITE | RW_NOSLEEP) != 0) {
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
+
+ /* Ensure that we start in expected state. */
+ if (test_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags) ||
+ (ifp->if_flags & IFF_RUNNING) == 0 ||
+ (ic->ic_flags & IEEE80211_F_BGSCAN) == 0 ||
+ (ic->ic_xflags & IEEE80211_F_TX_MGMT_ONLY) == 0 ||
+ (sc->sc_flags & QWX_FLAG_ROAMING) ||
+ ic->ic_state != IEEE80211_S_RUN) {
+ /* Don't touch the device, just return. */
+ rw_exit(&sc->ioctl_rwl);
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
+
+ /* Send a DEAUTH frame to our old AP. */
+ err = IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH,
+ IEEE80211_REASON_AUTH_LEAVE);
+ if (err)
+ goto done;
+
+ /* Prevent state changes due to received frames. */
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ /* Disallow new tasks. */
+ set_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
+
+ qwx_del_task_all(sc);
+
+ /* Wait for Tx queues to drain. */
+ for (i = 0; i < sc->hw_params.max_tx_ring; i++) {
+ struct dp_tx_ring *tx_ring = &dp->tx_ring[i];
+
+ while (tx_ring->queued > 0) {
+ err = tsleep_nsec(&tx_ring->queued, 0, "qwxtxdr",
+ SEC_TO_NSEC(1));
+ if (err) {
+ if (tx_ring->queued == 0) {
+ err = 0;
+ break;
+ }
+ printf("%s: Tx ring %d has %d frames queued\n",
+ __func__, i, tx_ring->queued);
+ goto done;
+ }
+ }
+ }
+ while (arvif->txmgmt.queued > 0) {
+ err = tsleep_nsec(&arvif->txmgmt.queued, 0, "qwxtxdr",
+ MSEC_TO_NSEC(500));
+ if (err) {
+ if (arvif->txmgmt.queued == 0) {
+ err = 0;
+ break;
+ }
+ printf("%s: %d management frames still queued\n",
+ __func__, arvif->txmgmt.queued);
+ goto done;
+ }
+ }
+
+ /*
+ * Remove installed crypto keys while we still have access to them.
+ * Once qwx_newstate() is entered ic_bss will already contain
+ * information about our next AP.
+ */
+ if (ic->ic_flags & IEEE80211_F_RSNON) {
+ struct ieee80211_key *k;
+
+
+ if (nq->flags & QWX_NODE_FLAG_HAVE_PAIRWISE_KEY)
+ (*ic->ic_delete_key)(ic, ni, &ni->ni_pairwise_key);
+
+ if ((nq->flags & QWX_NODE_FLAG_HAVE_GROUP_KEY) &&
+ (ic->ic_def_txkey == 1 || ic->ic_def_txkey == 2)) {
+ k = &ic->ic_nw_keys[ic->ic_def_txkey];
+ if ((k->k_flags & IEEE80211_KEY_GROUP) &&
+ k->k_cipher == IEEE80211_CIPHER_CCMP)
+ (*ic->ic_delete_key)(ic, ni, k);
+ }
+
+ if (ic->ic_igtk_kid == 4 || ic->ic_igtk_kid == 5) {
+ k = &ic->ic_nw_keys[ic->ic_igtk_kid];
+ if (k->k_flags & IEEE80211_KEY_IGTK)
+ (*ic->ic_delete_key)(ic, ni, k);
+ }
+
+ ni->ni_port_valid = 0;
+ ni->ni_flags &= ~IEEE80211_NODE_TXRXPROT;
+ ni->ni_flags &= ~IEEE80211_NODE_TXMGMTPROT;
+ ni->ni_flags &= ~IEEE80211_NODE_RXMGMTPROT;
+ ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE;
+ }
+
+ /*
+ * XXX: This needs to be unset for vdev shutdown to work.
+ * Perhaps we need a separate flag?
+ */
+ clear_bit(ATH11K_FLAG_CRASH_FLUSH, sc->sc_flags);
+
+ /* Clear association to our old AP in firmware. */
+ err = qwx_run_stop(sc);
+ if (err)
+ goto done;
+
+ err = qwx_deauth(sc);
+ if (err)
+ goto done;
+
+ /* Allow roaming to proceed. */
+ sc->sc_flags |= QWX_FLAG_ROAMING;
+ ifp->if_flags |= IFF_RUNNING;
+ ni->ni_unref_arg = sc->bgscan_unref_arg;
+ ni->ni_unref_arg_size = sc->bgscan_unref_arg_size;
+ sc->bgscan_unref_arg = NULL;
+ sc->bgscan_unref_arg_size = 0;
+ ieee80211_node_switch_bss(ic, ni);
+done:
+ if (err) {
+ free(sc->bgscan_unref_arg, M_DEVBUF, sc->bgscan_unref_arg_size);
+ sc->bgscan_unref_arg = NULL;
+ sc->bgscan_unref_arg_size = 0;
+
+ ifp->if_flags |= IFF_RUNNING;
+ task_add(systq, &sc->init_task);
+ }
+
+ rw_exit(&sc->ioctl_rwl);
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+}
+
+void
+qwx_bgscan_done(struct ieee80211com *ic,
+ struct ieee80211_node_switch_bss_arg *arg, size_t arg_size)
+{
+ struct qwx_softc *sc = ic->ic_softc;
+
+ free(sc->bgscan_unref_arg, M_DEVBUF, sc->bgscan_unref_arg_size);
+ sc->bgscan_unref_arg = arg;
+ sc->bgscan_unref_arg_size = arg_size;
+ qwx_add_task(sc, systq, &sc->bgscan_done_task);
+}
+
/*
* Find a pdev which corresponds to a given channel.
* This doesn't exactly match the semantics of the Linux driver
@@ -26407,7 +26594,7 @@ qwx_auth(struct qwx_softc *sc)
qwx_recalculate_mgmt_rate(sc, ni, arvif->vdev_id, pdev->pdev_id);
ni->ni_txrate = 0;
-
+
/* Start vdev. */
ret = qwx_mac_vdev_start(sc, arvif, pdev->pdev_id);
if (ret) {
@@ -27013,13 +27200,6 @@ qwx_assoc(struct qwx_softc *sc)
IEEE80211_ADDR_COPY(arvif->bssid, ni->ni_bssid);
sc->bss_peer_id = nq->peer_id;
- /*
- * Enable reception of data frames now, if not already enabled.
- * We may need to receive EAPOL data frames very soon after the
- * AP sends a response to our assoc request.
- */
- sc->ops.irq_enable(sc);
-
return 0;
}
@@ -27112,8 +27292,6 @@ qwx_run_stop(struct qwx_softc *sc)
struct qwx_node *nq = (void *)ni;
int ret;
- sc->ops.irq_disable(sc);
-
ret = qwx_wmi_set_peer_param(sc, ni->ni_macaddr, arvif->vdev_id,
pdev_id, WMI_PEER_AUTHORIZE, 0);
if (ret) {
@@ -27167,6 +27345,7 @@ qwx_attach(struct qwx_softc *sc)
task_set(&sc->setkey_task, qwx_setkey_task, sc);
task_set(&sc->ba_task, qwx_ba_task, sc);
task_set(&sc->bgscan_task, qwx_bgscan_task, sc);
+ task_set(&sc->bgscan_done_task, qwx_bgscan_done_task, sc);
task_set(&sc->set_cc_task, qwx_set_cc_task, sc);
timeout_set_proc(&sc->scan.timeout, qwx_scan_timeout, sc);
#if NBPFILTER > 0
blob - 9635f26dbbea069ee04706a17f5119161eb3ed27
blob + a4e9ac3ccc564f7cb0fc1d6e23b61155e59f3c93
--- sys/dev/ic/qwxvar.h
+++ sys/dev/ic/qwxvar.h
@@ -412,6 +412,8 @@ enum ath11k_dev_flags {
ATH11K_FLAG_FIXED_MEM_RGN,
ATH11K_FLAG_DEVICE_INIT_DONE,
ATH11K_FLAG_MULTI_MSI_VECTORS,
+
+ QWX_FLAG_ROAMING,
};
enum ath11k_scan_state {
@@ -1886,6 +1888,9 @@ struct qwx_softc {
u_int scan_channel;
struct qwx_survey_info survey[IEEE80211_CHAN_MAX];
struct task bgscan_task;
+ struct task bgscan_done_task;
+ struct ieee80211_node_switch_bss_arg *bgscan_unref_arg;
+ size_t bgscan_unref_arg_size;
int attached;
struct {
@@ -2032,6 +2037,8 @@ void qwx_init_task(void *);
int qwx_newstate(struct ieee80211com *, enum ieee80211_state, int);
void qwx_newstate_task(void *);
int qwx_bgscan(struct ieee80211com *);
+void qwx_bgscan_done(struct ieee80211com *,
+ struct ieee80211_node_switch_bss_arg *, size_t);
void qwx_updatechan(struct ieee80211com *);
struct qwx_node {
blob - 2c3164ff3d02a115c326d8d2c8868cd71ab67386
blob + f23e4bd271d5fe321ba30b9c2da3c3a60e10064b
--- sys/dev/pci/if_qwx_pci.c
+++ sys/dev/pci/if_qwx_pci.c
@@ -1134,6 +1134,7 @@ unsupported_wcn6855_soc:
ic->ic_ampdu_tx_start = qwx_ampdu_tx_start;
ic->ic_ampdu_tx_stop = NULL;
ic->ic_bgscan_start = qwx_bgscan;
+ ic->ic_bgscan_done = qwx_bgscan_done;
/*
* We cannot read the MAC address without loading the
fix qwx roaming (Re: sys/qwx,sys/qwz: report RSSI as percentage)