The branch main has been updated by bz:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=d9f59799fc3e7940c47aa674c25994e640eae45e

commit d9f59799fc3e7940c47aa674c25994e640eae45e
Author:     Bjoern A. Zeeb <b...@freebsd.org>
AuthorDate: 2022-03-22 18:34:13 +0000
Commit:     Bjoern A. Zeeb <b...@freebsd.org>
CommitDate: 2022-03-22 18:51:43 +0000

    LinuxKPI: 802.11: rework sta state machine compatibility
    
    Rework the state machine parts for various reasons:
    (1) to add sta tracing to be able to better follow ni and lsta state
    (2) factor out/implement lkpi_lsta_remove() to unlink the lsta and
        free the ni reference.
    (3) avoid calling lkpi_disassoc() when you would think you should as
        changing BSS_CHANGED_ASSOC setting vif->bss_conf.assoc to false
        triggers a sta removal from firmware in iwlwifi which then triggers
        follow-up errors.  I do not understand why they use flags and state
        and ?? in parallel (too many options and ways to do things?).
    (4) when "roaming" (or being disassoc/deauth) from an AP both net80211
        and apparently so mac80211 re-start with a new node/sta.  This
        results in us losing one or the other state in the compat layer
        or not updating firmware appropriately.  To resolve this make use
        of (a) the newly introduced (*iv_update_bss)() and (b) always tear
        a station down to "State 1" (INIT/SCAN/pre-AUTH) and only if needed
        re-create the new one (if we go to AUTH).
        A slightly earlier version has survived a night of wpa_supplicant
        and hostapd fighting each other over disassoc and deauth and
        re-associating/authorizing.
    
    While there update a few comments and typos and do a few minor auxiliary
    changes which are hard or not worth to extract.
    
    Sponsored by:   The FreeBSD Foundation
    MFC after:      3 days
---
 sys/compat/linuxkpi/common/src/linux_80211.c       | 574 ++++++++++++++++++---
 sys/compat/linuxkpi/common/src/linux_80211.h       |   6 +-
 .../linuxkpi/common/src/linux_80211_macops.c       |   4 +-
 3 files changed, 502 insertions(+), 82 deletions(-)

diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c 
b/sys/compat/linuxkpi/common/src/linux_80211.c
index 5721ff1127ee..1d3e6de375dc 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -95,6 +95,7 @@ SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_80211, 
CTLFLAG_RWTUN,
 #define        D80211_TRACE_RX_BEACONS 0x4000
 #define        D80211_TRACEX           (D80211_TRACE_TX|D80211_TRACE_RX)
 #define        D80211_TRACEX_DUMP      
(D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP)
+#define        D80211_TRACE_STA        0x10000
 #define        UNIMPLEMENTED           if (debug_80211 & D80211_TODO)          
\
     printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__)
 #define        TRACEOK()               if (debug_80211 & D80211_TRACEOK)       
\
@@ -139,6 +140,42 @@ static struct lkpi_sta *lkpi_find_lsta_by_ni(struct 
lkpi_vif *,
 static void lkpi_80211_txq_task(void *, int);
 static void lkpi_ieee80211_free_skb_mbuf(void *);
 
+static void
+lkpi_dump_lsta(struct lkpi_sta *lsta, const char *_f, int _l)
+{
+
+       if ((debug_80211 & D80211_TRACE_STA) == 0)
+               return;
+       if (lsta == NULL)
+               return;
+
+       printf("%s:%d lsta %p ni %p sta %p\n",
+           _f, _l, lsta, lsta->ni, &lsta->sta);
+       if (lsta->ni != NULL)
+               ieee80211_dump_node(NULL, lsta->ni);
+       printf("\ttxq_task txq len %d mtx\n", mbufq_len(&lsta->txq));
+       printf("\tkc %p state %d added_to_drv %d in_mgd %d\n",
+               lsta->kc, lsta->state, lsta->added_to_drv, lsta->in_mgd);
+}
+
+static void
+lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif)
+{
+       struct ieee80211_node *ni;
+
+       ni = lsta->ni;
+
+       LKPI_80211_LVIF_LOCK(lvif);
+       TAILQ_REMOVE(&lvif->lsta_head, lsta, lsta_entry);
+       LKPI_80211_LVIF_UNLOCK(lvif);
+
+       lsta->ni = NULL;
+       ni->ni_drv_data = NULL;
+       ieee80211_free_node(ni);
+
+       IMPROVE("free lsta here?  We won't have a pointer to it from the node 
anymore.");
+}
+
 static struct lkpi_sta *
 lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t 
mac[IEEE80211_ADDR_LEN],
     struct ieee80211_hw *hw, struct ieee80211_node *ni)
@@ -765,6 +802,10 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct 
ieee80211_vif *vif,
                vif->bss_conf.assoc = false;
                vif->bss_conf.aid = 0;
                changed |= BSS_CHANGED_ASSOC;
+               /*
+                * This will remove the sta from firmware for iwlwifi.
+                * So confusing that they use state and flags and ... ^%$%#%$^.
+                */
                IMPROVE();
                hw = LHW_TO_HW(lhw);
                lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf,
@@ -845,6 +886,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        lvif = VAP_TO_LVIF(vap);
        vif = LVIF_TO_VIF(lvif);
 
+       ni = ieee80211_ref_node(vap->iv_bss);
+
        IEEE80211_UNLOCK(vap->iv_ic);
 
        /* Add chanctx (or if exists, change it). */
@@ -872,7 +915,6 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        conf->min_def.center_freq2 = 0;
        IMPROVE("currently 20_NOHT only");
 
-       ni = NULL;
        error = 0;
        if (vif->chanctx_conf != NULL) {
                changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
@@ -905,8 +947,6 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        }
        IMPROVE("update radiotap chan fields too");
 
-       ni = ieee80211_ref_node(vap->iv_bss);
-
        /* Set bss info (bss_info_changed). */
        bss_changed = 0;
        IEEE80211_ADDR_COPY(vif->bss_conf.bssid, ni->ni_bssid);
@@ -926,8 +966,29 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        IMPROVE("bss info: not all needs to come now and rates are missing");
        lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
 
+       /*
+        * This is a bandaid for now.  If we went through (*iv_update_bss)()
+        * and then removed the lsta we end up here without a lsta and have
+        * to manually allocate and link it in as lkpi_ic_node_alloc()/init()
+        * would normally do.
+        * XXX-BZ I do not like this but currently we have no good way of
+        * intercepting the bss swap and state changes and packets going out
+        * workflow so live with this.  It is a compat layer after all.
+        */
+       if (ni->ni_drv_data == NULL) {
+               lsta = lkpi_lsta_alloc(vap, ni->ni_macaddr, hw, ni);
+               if (lsta == NULL) {
+                       error = ENOMEM;
+                       goto out;
+               }
+               lsta->ni = ieee80211_ref_node(ni);
+               LKPI_80211_LVIF_LOCK(lvif);
+               TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry);
+               LKPI_80211_LVIF_UNLOCK(lvif);
+       } else {
+               lsta = ni->ni_drv_data;
+       }
        /* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */
-       lsta = ni->ni_drv_data;
        KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
        KASSERT(lsta->state == IEEE80211_STA_NOTEXIST, ("%s: lsta %p state not "
            "NOTEXIST: %#x\n", __func__, lsta, lsta->state));
@@ -946,6 +1007,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
         * possibly prepare the queue in the driver to be ready for the 1st
         * packet;  lkpi_80211_txq_tx_one() still has a workaround as there
         * is no guarantee or way to check.
+        * XXX-BZ and by now we know that this does not work on all drivers
+        * for all queues.
         */
        lkpi_wake_tx_queues(hw, sta, false, false);
 
@@ -1032,33 +1095,22 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
 
        /* Keep ni around. */
        ni = ieee80211_ref_node(vap->iv_bss);
-
-       IEEE80211_UNLOCK(vap->iv_ic);
        lsta = ni->ni_drv_data;
        sta = LSTA_TO_STA(lsta);
 
-       /* flush, drop. */
-       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
-
-       IEEE80211_LOCK(vap->iv_ic);
-
-       /* Call iv_newstate first so we get potential deauth packet out. */
-       error = lvif->iv_newstate(vap, nstate, arg);
-       if (error != 0)
-               goto outni;
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
 
        IEEE80211_UNLOCK(vap->iv_ic);
 
+       /* flush, drop. */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
+
        /* Wake tx queues to get packet(s) out. */
        lkpi_wake_tx_queues(hw, sta, true, true);
 
        /* flush, no drop */
        lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
 
-       /* Take the station and chan ctx down again. */
-
-       IMPROVE("event callback with failure?");
-
        /* End mgd_complete_tx. */
        if (lsta->in_mgd) {
                memset(&prep_tx_info, 0, sizeof(prep_tx_info));
@@ -1067,18 +1119,18 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
                lsta->in_mgd = false;
        }
 
-#ifdef __not_yet__
        /* sync_rx_queues */
        lkpi_80211_mo_sync_rx_queues(hw);
 
        /* sta_pre_rcu_remove */
         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
-#endif
+
+       /* Take the station down. */
 
        /* Adjust sta and change state (from NONE) to NOTEXIST. */
        KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
        KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
-           "NONE: %#x\n", __func__, lsta, lsta->state));
+           "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, 
nstate, arg));
        error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST);
        if (error != 0) {
                IMPROVE("do we need to undo the chan ctx?");
@@ -1088,8 +1140,13 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        lsta->added_to_drv = false;     /* mo manages. */
 #endif
 
-       IMPROVE("Any bss_info changes to announce?");
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       lkpi_lsta_remove(lsta, lvif);
+
+       /* conf_tx */
 
+       /* Take the chan ctx down. */
        if (vif->chanctx_conf != NULL) {
                struct ieee80211_chanctx_conf *conf;
 
@@ -1103,12 +1160,8 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
                free(conf, M_LKPI80211);
        }
 
-       /* No need to start a scan; ic_scan_start should do. */
-
-       error = EALREADY;
 out:
        IEEE80211_LOCK(vap->iv_ic);
-outni:
        if (ni != NULL)
                ieee80211_free_node(ni);
        return (error);
@@ -1248,7 +1301,7 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int arg)
 }
 
 static int
-lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
+_lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
 {
        struct lkpi_hw *lhw;
        struct ieee80211_hw *hw;
@@ -1258,6 +1311,7 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, in
        struct lkpi_sta *lsta;
        struct ieee80211_sta *sta;
        struct ieee80211_prep_tx_info prep_tx_info;
+       enum ieee80211_bss_changed bss_changed;
        int error;
 
        lhw = vap->iv_ic->ic_softc;
@@ -1267,11 +1321,42 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, in
 
        /* Keep ni around. */
        ni = ieee80211_ref_node(vap->iv_bss);
-
-       IEEE80211_UNLOCK(vap->iv_ic);
        lsta = ni->ni_drv_data;
        sta = LSTA_TO_STA(lsta);
 
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       IEEE80211_UNLOCK(vap->iv_ic);
+
+       /* flush, drop. */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
+
+       IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
+       if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
+           !lsta->in_mgd) {
+               memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+               prep_tx_info.duration = PREP_TX_INFO_DURATION;
+               lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+               lsta->in_mgd = true;
+       }
+
+       IEEE80211_LOCK(vap->iv_ic);
+
+       /* Call iv_newstate first so we get potential DISASSOC packet out. */
+       error = lvif->iv_newstate(vap, nstate, arg);
+       if (error != 0)
+               goto outni;
+
+       IEEE80211_UNLOCK(vap->iv_ic);
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Wake tx queues to get packet(s) out. */
+       lkpi_wake_tx_queues(hw, sta, true, true);
+
+       /* flush, no drop */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
+
        /* End mgd_complete_tx. */
        if (lsta->in_mgd) {
                memset(&prep_tx_info, 0, sizeof(prep_tx_info));
@@ -1280,6 +1365,14 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, in
                lsta->in_mgd = false;
        }
 
+       /* sync_rx_queues */
+       lkpi_80211_mo_sync_rx_queues(hw);
+
+       /* sta_pre_rcu_remove */
+        lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
+
+       /* Take the station down. */
+
        /* Update sta and change state (from AUTH) to NONE. */
        KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
        KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
@@ -1288,23 +1381,84 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum 
ieee80211_state nstate, in
        if (error != 0)
                goto out;
 
-       IMPROVE("anything else?");
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Adjust sta and change state (from NONE) to NOTEXIST. */
+       KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+       KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
+           "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, 
nstate, arg));
+       error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST);
+       if (error != 0) {
+               IMPROVE("do we need to undo the chan ctx?");
+               goto out;
+       }
+#if 0
+       lsta->added_to_drv = false;     /* mo manages. */
+#endif
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Update bss info (bss_info_changed) (assoc, aid, ..). */
+       /* We need to do this now, can only do after sta is 
IEEE80211_STA_NOTEXIST. */
+       lkpi_disassoc(sta, vif, lhw);
+
+       IMPROVE("Any bss_info changes to announce?");
+       bss_changed = 0;
+       vif->bss_conf.qos = 0;
+       bss_changed |= BSS_CHANGED_QOS;
+       vif->bss_conf.ssid_len = 0;
+       memset(vif->bss_conf.ssid, '\0', sizeof(vif->bss_conf.ssid));
+       bss_changed |= BSS_CHANGED_BSSID;
+       lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+
+       lkpi_lsta_remove(lsta, lvif);
+
+       /* conf_tx */
 
+       /* Take the chan ctx down. */
+       if (vif->chanctx_conf != NULL) {
+               struct ieee80211_chanctx_conf *conf;
+
+               conf = vif->chanctx_conf;
+               /* Remove vif context. */
+               lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf);
+               /* NB: vif->chanctx_conf is NULL now. */
+
+               /* Remove chan ctx. */
+               lkpi_80211_mo_remove_chanctx(hw, conf);
+               free(conf, M_LKPI80211);
+       }
+
+       error = EALREADY;
 out:
        IEEE80211_LOCK(vap->iv_ic);
+outni:
        if (ni != NULL)
                ieee80211_free_node(ni);
        return (error);
 }
 
+static int
+lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
+{
+       int error;
+
+       error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
+       if (error != 0 && error != EALREADY)
+               return (error);
+
+       /* At this point iv_bss is long a new node! */
+
+       error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
+       return (error);
+}
+
 static int
 lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
 {
        int error;
 
-       error = lkpi_sta_assoc_to_auth(vap, nstate, arg);
-       if (error == 0)
-               error = lkpi_sta_auth_to_scan(vap, nstate, arg);
+       error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
        return (error);
 }
 
@@ -1313,9 +1467,7 @@ lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum 
ieee80211_state nstate, in
 {
        int error;
 
-       error = lkpi_sta_assoc_to_scan(vap, nstate, arg);
-       if (error == 0)
-               error = lkpi_sta_scan_to_init(vap, nstate, arg);
+       error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
        return (error);
 }
 
@@ -1472,6 +1624,10 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        struct ieee80211_node *ni;
        struct lkpi_sta *lsta;
        struct ieee80211_sta *sta;
+       struct ieee80211_prep_tx_info prep_tx_info;
+#if 0
+       enum ieee80211_bss_changed bss_changed;
+#endif
        int error;
 
        lhw = vap->iv_ic->ic_softc;
@@ -1481,11 +1637,60 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
 
        /* Keep ni around. */
        ni = ieee80211_ref_node(vap->iv_bss);
-
-       IEEE80211_UNLOCK(vap->iv_ic);
        lsta = ni->ni_drv_data;
        sta = LSTA_TO_STA(lsta);
 
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       IEEE80211_UNLOCK(vap->iv_ic);
+
+       /* flush, drop. */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
+
+       IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
+       if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
+           !lsta->in_mgd) {
+               memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+               prep_tx_info.duration = PREP_TX_INFO_DURATION;
+               lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+               lsta->in_mgd = true;
+       }
+
+       IEEE80211_LOCK(vap->iv_ic);
+
+       /* Call iv_newstate first so we get potential DISASSOC packet out. */
+       error = lvif->iv_newstate(vap, nstate, arg);
+       if (error != 0)
+               goto outni;
+
+       IEEE80211_UNLOCK(vap->iv_ic);
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Wake tx queues to get packet(s) out. */
+       lkpi_wake_tx_queues(hw, sta, true, true);
+
+       /* flush, no drop */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
+
+       /* End mgd_complete_tx. */
+       if (lsta->in_mgd) {
+               memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+               prep_tx_info.success = false;
+               lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+               lsta->in_mgd = false;
+       }
+
+#if 0
+       /* sync_rx_queues */
+       lkpi_80211_mo_sync_rx_queues(hw);
+
+       /* sta_pre_rcu_remove */
+        lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
+#endif
+
+       /* Take the station down. */
+
        /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
        KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
        KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state 
not "
@@ -1494,61 +1699,217 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int
        if (error != 0)
                goto out;
 
-       /* Update bss info (bss_info_changed) (assoc, aid, ..). */
-       lkpi_disassoc(sta, vif, lhw);
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
 
        /* Update sta_state (ASSOC to AUTH). */
        KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
        KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
            "ASSOC: %#x\n", __func__, lsta, lsta->state));
-       sta = LSTA_TO_STA(lsta);
-       sta->aid = 0;
        error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH);
        if (error != 0)
                goto out;
 
-       IMPROVE("if ASSOC is final state, prep_tx_info?");
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
 
+#if 0
+       /* Update bss info (bss_info_changed) (assoc, aid, ..). */
+       lkpi_disassoc(sta, vif, lhw);
+#endif
+
+       error = EALREADY;
 out:
        IEEE80211_LOCK(vap->iv_ic);
+outni:
        if (ni != NULL)
                ieee80211_free_node(ni);
        return (error);
 }
 
 static int
-lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
+lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
 {
+       struct lkpi_hw *lhw;
+       struct ieee80211_hw *hw;
+       struct lkpi_vif *lvif;
+       struct ieee80211_vif *vif;
+       struct ieee80211_node *ni;
+       struct lkpi_sta *lsta;
+       struct ieee80211_sta *sta;
+       struct ieee80211_prep_tx_info prep_tx_info;
+       enum ieee80211_bss_changed bss_changed;
        int error;
 
-       error = lkpi_sta_run_to_assoc(vap, nstate, arg);
-       if (error == 0)
-               error = lkpi_sta_assoc_to_auth(vap, nstate, arg);
+       lhw = vap->iv_ic->ic_softc;
+       hw = LHW_TO_HW(lhw);
+       lvif = VAP_TO_LVIF(vap);
+       vif = LVIF_TO_VIF(lvif);
+
+       /* Keep ni around. */
+       ni = ieee80211_ref_node(vap->iv_bss);
+       lsta = ni->ni_drv_data;
+       sta = LSTA_TO_STA(lsta);
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       IEEE80211_UNLOCK(vap->iv_ic);
+
+       /* flush, drop. */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
+
+       IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
+       if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
+           !lsta->in_mgd) {
+               memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+               prep_tx_info.duration = PREP_TX_INFO_DURATION;
+               lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
+               lsta->in_mgd = true;
+       }
+
+       IEEE80211_LOCK(vap->iv_ic);
+
+       /* Call iv_newstate first so we get potential DISASSOC packet out. */
+       error = lvif->iv_newstate(vap, nstate, arg);
+       if (error != 0)
+               goto outni;
+
+       IEEE80211_UNLOCK(vap->iv_ic);
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Wake tx queues to get packet(s) out. */
+       lkpi_wake_tx_queues(hw, sta, true, true);
+
+       /* flush, no drop */
+       lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
+
+       /* End mgd_complete_tx. */
+       if (lsta->in_mgd) {
+               memset(&prep_tx_info, 0, sizeof(prep_tx_info));
+               prep_tx_info.success = false;
+               lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
+               lsta->in_mgd = false;
+       }
+
+       /* sync_rx_queues */
+       lkpi_80211_mo_sync_rx_queues(hw);
+
+       /* sta_pre_rcu_remove */
+        lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
+
+       /* Take the station down. */
+
+       /* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
+       KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+       KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state 
not "
+           "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
+       error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC);
+       if (error != 0)
+               goto out;
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Update sta_state (ASSOC to AUTH). */
+       KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+       KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
+           "ASSOC: %#x\n", __func__, lsta, lsta->state));
+       error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH);
+       if (error != 0)
+               goto out;
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Update sta and change state (from AUTH) to NONE. */
+       KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+       KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
+           "AUTH: %#x\n", __func__, lsta, lsta->state));
+       error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE);
+       if (error != 0)
+               goto out;
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Adjust sta and change state (from NONE) to NOTEXIST. */
+       KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
+       KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
+           "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, 
nstate, arg));
+       error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST);
+       if (error != 0) {
+               IMPROVE("do we need to undo the chan ctx?");
+               goto out;
+       }
+#if 0
+       lsta->added_to_drv = false;     /* mo manages. */
+#endif
+
+       lkpi_dump_lsta(lsta, __func__, __LINE__);
+
+       /* Update bss info (bss_info_changed) (assoc, aid, ..). */
+       /*
+        * One would expect this to happen when going off AUTHORIZED.
+        * See comment there; removes the sta from fw.
+        */
+       lkpi_disassoc(sta, vif, lhw);
+
+       IMPROVE("Any bss_info changes to announce?");
+       bss_changed = 0;
+       vif->bss_conf.qos = 0;
+       bss_changed |= BSS_CHANGED_QOS;
+       vif->bss_conf.ssid_len = 0;
+       memset(vif->bss_conf.ssid, '\0', sizeof(vif->bss_conf.ssid));
+       bss_changed |= BSS_CHANGED_BSSID;
+       lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+
+       lkpi_lsta_remove(lsta, lvif);
+
+       /* conf_tx */
+
+       /* Take the chan ctx down. */
+       if (vif->chanctx_conf != NULL) {
+               struct ieee80211_chanctx_conf *conf;
+
+               conf = vif->chanctx_conf;
+               /* Remove vif context. */
+               lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf);
+               /* NB: vif->chanctx_conf is NULL now. */
+
+               /* Remove chan ctx. */
+               lkpi_80211_mo_remove_chanctx(hw, conf);
+               free(conf, M_LKPI80211);
+       }
+
+       error = EALREADY;
+out:
+       IEEE80211_LOCK(vap->iv_ic);
+outni:
+       if (ni != NULL)
+               ieee80211_free_node(ni);
        return (error);
 }
 
 static int
 lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
 {
-       int error;
 
-       error = lkpi_sta_run_to_auth(vap, nstate, arg);
-       if (error == 0)
-               error = lkpi_sta_auth_to_scan(vap, nstate, arg);
-       return (error);
+       return (lkpi_sta_run_to_init(vap, nstate, arg));
 }
 
 static int
-lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
+lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, 
int arg)
 {
        int error;
 
-       error = lkpi_sta_run_to_scan(vap, nstate, arg);
-       if (error == 0)
-               error = lkpi_sta_scan_to_init(vap, nstate, arg);
+       error = lkpi_sta_run_to_init(vap, nstate, arg);
+       if (error != 0 && error != EALREADY)
+               return (error);
+
+       /* At this point iv_bss is long a new node! */
+
+       error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
        return (error);
 }
 
+/* -------------------------------------------------------------------------- 
*/
+
 /*
  * The matches the documented state changes in net80211::sta_newstate().
  * XXX (1) without CSA and SLEEP yet, * XXX (2) not all unhandled cases
@@ -1563,24 +1924,24 @@ struct fsm_state {
        { IEEE80211_S_INIT,     IEEE80211_S_INIT, lkpi_sta_state_do_nada },
        { IEEE80211_S_SCAN,     IEEE80211_S_INIT, lkpi_sta_state_do_nada },     
/* scan_to_init */
        { IEEE80211_S_AUTH,     IEEE80211_S_INIT, lkpi_sta_auth_to_init },      
/* not explicitly in sta_newstate() */
-       { IEEE80211_S_ASSOC,    IEEE80211_S_INIT, lkpi_sta_assoc_to_init },
-       { IEEE80211_S_RUN,      IEEE80211_S_INIT, lkpi_sta_run_to_init },
+       { IEEE80211_S_ASSOC,    IEEE80211_S_INIT, lkpi_sta_assoc_to_init },     
/* Send DEAUTH. */
+       { IEEE80211_S_RUN,      IEEE80211_S_INIT, lkpi_sta_run_to_init },       
/* Send DISASSOC. */
 
        { IEEE80211_S_INIT,     IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
        { IEEE80211_S_SCAN,     IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
        { IEEE80211_S_AUTH,     IEEE80211_S_SCAN, lkpi_sta_auth_to_scan },
        { IEEE80211_S_ASSOC,    IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan },
-       { IEEE80211_S_RUN,      IEEE80211_S_SCAN, lkpi_sta_run_to_scan },
+       { IEEE80211_S_RUN,      IEEE80211_S_SCAN, lkpi_sta_run_to_scan },       
/* Beacon miss. */
 
-       { IEEE80211_S_INIT,     IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },
-       { IEEE80211_S_SCAN,     IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },
-       { IEEE80211_S_AUTH,     IEEE80211_S_AUTH, lkpi_sta_a_to_a },
-       { IEEE80211_S_ASSOC,    IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth },
-       { IEEE80211_S_RUN,      IEEE80211_S_AUTH, lkpi_sta_run_to_auth },
+       { IEEE80211_S_INIT,     IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },      
/* Send AUTH. */
+       { IEEE80211_S_SCAN,     IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },      
/* Send AUTH. */
+       { IEEE80211_S_AUTH,     IEEE80211_S_AUTH, lkpi_sta_a_to_a },            
/* Send ?AUTH. */
+       { IEEE80211_S_ASSOC,    IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth },     
/* Send ?AUTH. */
+       { IEEE80211_S_RUN,      IEEE80211_S_AUTH, lkpi_sta_run_to_auth },       
/* Send ?AUTH. */
 
-       { IEEE80211_S_AUTH,     IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc },
-       { IEEE80211_S_ASSOC,    IEEE80211_S_ASSOC, lkpi_sta_a_to_a },
-       { IEEE80211_S_RUN,      IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc },
+       { IEEE80211_S_AUTH,     IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc },    
/* Send ASSOCREQ. */
+       { IEEE80211_S_ASSOC,    IEEE80211_S_ASSOC, lkpi_sta_a_to_a },           
/* Send ASSOCREQ. */
+       { IEEE80211_S_RUN,      IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc },     
/* Send ASSOCREQ/REASSOCREQ. */
 
        { IEEE80211_S_AUTH,     IEEE80211_S_RUN, lkpi_sta_auth_to_run },
        { IEEE80211_S_ASSOC,    IEEE80211_S_RUN, lkpi_sta_assoc_to_run },
@@ -1630,6 +1991,11 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int arg)
        error = 0;
        for (; s->handler != NULL; s++) {
                if (ostate == s->ostate && nstate == s->nstate) {
+                       if (debug_80211 & D80211_TRACE)
+                               ic_printf(vap->iv_ic, "%s: new state %d (%s) ->"
+                                   " %d (%s): arg %d.\n", __func__,
+                                   ostate, ieee80211_state_name[ostate],
+                                   nstate, ieee80211_state_name[nstate], arg);
                        error = s->handler(vap, nstate, arg);
                        break;
                }
@@ -1637,7 +2003,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int arg)
        IEEE80211_LOCK_ASSERT(vap->iv_ic);
 
        if (s->handler == NULL) {
-               IMPROVE("thurn this into a KASSERT\n");
+               IMPROVE("turn this into a KASSERT\n");
                ic_printf(vap->iv_ic, "%s: unsupported state transition "
                    "%d (%s) -> %d (%s)\n", __func__,
                    ostate, ieee80211_state_name[ostate],
@@ -1646,12 +2012,11 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int arg)
        }
 
        if (error == EALREADY) {
-               IMPROVE("make this a debug log later");
-               ic_printf(vap->iv_ic, "%s: error %d during state transition "
-                   "%d (%s) -> %d (%s): iv_newstate already handled.\n",
-                   __func__, error,
-                   ostate, ieee80211_state_name[ostate],
-                   nstate, ieee80211_state_name[nstate]);
+               if (debug_80211 & D80211_TRACE)
+                       ic_printf(vap->iv_ic, "%s: state transition %d (%s) -> "
+                           "%d (%s): iv_newstate already handled: %d.\n",
+                           __func__, ostate, ieee80211_state_name[ostate],
+                           nstate, ieee80211_state_name[nstate], error);
                return (0);
        }
 
@@ -1665,7 +2030,8 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int arg)
        }
 
        if (debug_80211 & D80211_TRACE)
-               ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x calling 
net80211 parent\n",
+               ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x "
+                   "calling net80211 parent\n",
                    __func__, __LINE__, vap, nstate, arg);
 
        return (lvif->iv_newstate(vap, nstate, arg));
@@ -1673,6 +2039,53 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum 
ieee80211_state nstate, int arg)
 
 /* -------------------------------------------------------------------------- 
*/
 
+/*
+ * We overload (*iv_update_bss) as otherwise we have cases in, e.g.,
+ * net80211::ieee80211_sta_join1() where vap->iv_bss gets replaced by a
+ * new node without us knowing and thus our ni/lsta are out of sync.
+ */
+static struct ieee80211_node *
+lkpi_iv_update_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
+{
+       struct lkpi_vif *lvif;
+       struct ieee80211_node *obss;
+       struct lkpi_sta *lsta;
+
+       lvif = VAP_TO_LVIF(vap);
+       obss = vap->iv_bss;
+
+       /* Nothing to copy from.  Just return. */
+       if (obss == NULL || obss->ni_drv_data == NULL)
+               goto out;
+
+       /* Nothing to copy to.  Just return. */
+       IMPROVE("clearing the obss might still be needed?");
+       if (ni == NULL)
+               goto out;
+
+       /* Nothing changed? panic? */
+       if (obss == ni)
+               goto out;
+
+       if (debug_80211 & D80211_TRACE)
+               ic_printf(vap->iv_ic, "%s: obss %p ni_drv_data %p "
+                   "ni %p ni_drv_data %p\n", __func__,
+                   obss, (obss != NULL) ? obss->ni_drv_data : NULL,
+                   ni, (ni != NULL) ? ni->ni_drv_data : NULL);
+
+       lsta = obss->ni_drv_data;
+       obss->ni_drv_data = ni->ni_drv_data;
+       ni->ni_drv_data = lsta;
+       if (lsta != NULL)
+               lsta->ni = ni;
+       lsta = obss->ni_drv_data;
+       if (lsta != NULL)
+               lsta->ni = obss;
+
+out:
+       return (lvif->iv_update_bss(vap, ni));
+}
+
 static int
 lkpi_ic_wme_update(struct ieee80211com *ic)
 {
@@ -1840,6 +2253,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char 
name[IFNAMSIZ],
        /* Override with LinuxKPI method so we can drive mac80211/cfg80211. */
        lvif->iv_newstate = vap->iv_newstate;
        vap->iv_newstate = lkpi_iv_newstate;
+       lvif->iv_update_bss = vap->iv_update_bss;
+       vap->iv_update_bss = lkpi_iv_update_bss;
 
        /* Key management. */
        if (lhw->ops->set_key != NULL) {
@@ -2361,6 +2776,8 @@ lkpi_ic_node_free(struct ieee80211_node *ni)
        ic = ni->ni_ic;
        lhw = ic->ic_softc;
        lsta = ni->ni_drv_data;
+       if (lsta == NULL)
+               goto out;
 
        /* XXX-BZ free resources, ... */
        IMPROVE();
@@ -2380,10 +2797,11 @@ lkpi_ic_node_free(struct ieee80211_node *ni)
 
        /* remove ref from lsta node... */
 
+       /* Free lsta. */
+
+out:
        if (lhw->ic_node_free != NULL)
                lhw->ic_node_free(ni);
-
-       /* Free lsta. */
 }
 
 static int
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h 
b/sys/compat/linuxkpi/common/src/linux_80211.h
index 9538a1284201..fabc90e07a87 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2020-2021 The FreeBSD Foundation
+ * Copyright (c) 2020-2022 The FreeBSD Foundation
  * Copyright (c) 2020-2021 Bjoern A. Zeeb
  *
  * This software was developed by Björn Zeeb under sponsorship from
@@ -96,7 +96,7 @@ struct lkpi_sta {
        struct ieee80211_key_conf *kc;
        enum ieee80211_sta_state state;
        bool                    added_to_drv;                   /* Driver 
knows; i.e. we called ...(). */
-       bool                    in_mgd;
+       bool                    in_mgd;                         /* XXX-BZ 
should this be per-vif? */
 
        /* Must be last! */
        struct ieee80211_sta    sta __aligned(CACHE_LINE_SIZE);
@@ -114,6 +114,8 @@ struct lkpi_vif {
        /* Other local stuff. */
        int                     (*iv_newstate)(struct ieee80211vap *,
                                    enum ieee80211_state, int);
+       struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *,
+                                   struct ieee80211_node *);
        TAILQ_HEAD(, lkpi_sta)  lsta_head;
        bool                    added_to_drv;                   /* Driver 
knows; i.e. we called add_interface(). */
 
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c 
b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index 0004967f350e..68e9ca47634b 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2021 The FreeBSD Foundation
+ * Copyright (c) 2021-2022 The FreeBSD Foundation
  *
  * This software was developed by Björn Zeeb under sponsorship from
  * the FreeBSD Foundation.
@@ -272,7 +272,7 @@ lkpi_80211_mo_configure_filter(struct ieee80211_hw *hw, 
unsigned int changed_fla
 
 /*
  * So far we only called sta_{add,remove} as an alternative to sta_state.
- * Let's keep the implementation simpler and hid sta_{add,remove} under the
+ * Let's keep the implementation simpler and hide sta_{add,remove} under the
  * hood here calling them if state_state is not available from mo_sta_state.
  */
 static int

Reply via email to