git: ecb691143d2e - releng/13.1 - LinuxKPI: 802.11: various updates for iwlwifi stability and upcoming rtw

From: Bjoern A. Zeeb <bz_at_FreeBSD.org>
Date: Wed, 30 Mar 2022 15:49:47 UTC
The branch releng/13.1 has been updated by bz:

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

commit ecb691143d2e4fe1100086ff73b5687c0cb29963
Author:     Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2022-03-22 15:02:45 +0000
Commit:     Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2022-03-30 15:47:32 +0000

    LinuxKPI: 802.11: various updates for iwlwifi stability and upcoming rtw
    
    Various updates to the internals of the LinuxKPI 802.11 implementation.
    Most notably improving state machine handling for de-assoc/de-auth and
    always creating a new station as net80211 does and mac80211 seems to
    do.  This avoids firmware crashes for iwlwifi and often panics and
    lead to more stability.
    Some other secondary changes are in order to support upcoming drivers
    so that we could possibly provide them out-of-tree for 13.1.
    
    - implement ieee80211_beacon_loss()
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit bb81db90f7e00f56df9ea14132a54ee983e7177f)
    (cherry picked from commit 24c2117bafdba3c6998ddd407d643e142c5deda5)
    
    - factor out lkpi_lsta_alloc() from lkpi_ic_node_alloc()
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 4f61ef8b78b25761b1b859ce39213f92fcbb24ce)
    (cherry picked from commit 34f8e38ea7a5fccdf7f5de2cdcfabbdf68825480)
    
    - factor out dtim/tsf updates
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit fa8f007de913623c066bf8f09213f92ee8cbca3a)
    (cherry picked from commit bc472e0a3ef06cb8eb309b00e7fbdae64a21626a)
    
    - rework sta state machine compatibility
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit d9f59799fc3e7940c47aa674c25994e640eae45e)
    (cherry picked from commit 67674c1cb11512f09873c6d8e463af31548ef87f)
    (cherry picked from commit 471a438a007eff18df3d0bd9dd8849faa9f2d6ff)
    
    - improve lkpi_80211_mo_sta_state() for non (*sta_state)
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 878fb1e5754cc5bfbec57bc24859b07936f82a41)
    (cherry picked from commit eb580265974013be43fa17d37e22124174a7ef86)
    
    - use cfg80211_chandef_create()
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 4a07abdeb8ce5271996d85f648832e3d7bc741a7)
    (cherry picked from commit 4836b8ac0ed3a3b155a7fb5f2322221f48c81687)
    
    - make lkpi_ic_set_channel() unconditional
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit b2cf3c2125bf6f34cb01296e7eda93f73bc4b715)
    (cherry picked from commit beaed0d4b5d0f901f78fc196f4b86d426a589792)
    
    - check IEEE80211_FEXT_SCAN_OFFLOAD on vap
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 768dd32a164b35fb2f66fc8a0250cee84c79471e)
    (cherry picked from commit 39bc4b2949e2ac32ad211aa6f1859559e21c7eb2)
    
    - remove special handling for (*ic_scan_curchan)
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 321735f451d1eaa710a768d9e35ff87acb089d35)
    (cherry picked from commit 5f88604cf7d9df1edbc662c4278e01476534f3ef)
    
    - improve hw_scan fallback to sw_scan
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 196cfd0b2eb640a63d007a7e9c3a74e4256255f3)
    (cherry picked from commit a1272e7d9621f95ca53f1bdef5ed78be12cac277)
    
    - 802.11: cleanup debugging
    
    Sponsored by:   The FreeBSD Foundation
    
    (cherry picked from commit 9d9ba2b79b3196935431879efd9094eb9bfedd95)
    (cherry picked from commit e269958566ff0df8d479996507636ed14ff6078a)
    
    Approved by:    re (gjb)
---
 .../linuxkpi/common/include/linux/ieee80211.h      |    8 +-
 sys/compat/linuxkpi/common/include/net/cfg80211.h  |    8 +-
 sys/compat/linuxkpi/common/include/net/mac80211.h  |    3 +-
 sys/compat/linuxkpi/common/src/linux_80211.c       | 1128 ++++++++++++++------
 sys/compat/linuxkpi/common/src/linux_80211.h       |    6 +-
 .../linuxkpi/common/src/linux_80211_macops.c       |   18 +-
 6 files changed, 846 insertions(+), 325 deletions(-)

diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index 55e31a7a88ae..e0aa558fe273 100644
--- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
@@ -38,9 +38,6 @@
 #include <linux/bitops.h>
 #include <linux/if_ether.h>
 
-/* linux_80211.c */
-extern int debug_80211;
-
 
 /* 9.4.2.55 Management MIC element (CMAC-256, GMAC-128, and GMAC-256). */
 struct ieee80211_mmie_16 {
@@ -544,9 +541,8 @@ ieee80211_hdrlen(__le16 fc)
 
 	if (ieee80211_is_mgmt(fc)) {
 #ifdef __notyet__
-		if (debug_80211 > 0)
-			printf("XXX-BZ %s: TODO? fc %#04x size %u\n",
-			    __func__, fc, size);
+		printf("XXX-BZ %s: TODO? fc %#04x size %u\n",
+		    __func__, fc, size);
 #endif
 		;
 	}
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index 896559247620..29fe65395385 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -44,16 +44,16 @@
 #include <net/regulatory.h>
 
 /* linux_80211.c */
-extern int debug_80211;
+extern int linuxkpi_debug_80211;
 #ifndef	D80211_TODO
 #define	D80211_TODO		0x1
 #endif
 #ifndef	D80211_IMPROVE
 #define	D80211_IMPROVE		0x2
 #endif
-#define	TODO()		if (debug_80211 & D80211_TODO)			\
+#define	TODO()		if (linuxkpi_debug_80211 & D80211_TODO)		\
     printf("%s:%d: XXX LKPI80211 TODO\n", __func__, __LINE__)
-#define	IMPROVE(...)	if (debug_80211 & D80211_IMPROVE)		\
+#define	IMPROVE(...)	if (linuxkpi_debug_80211 & D80211_IMPROVE)	\
     printf("%s:%d: XXX LKPI80211 IMPROVE\n", __func__, __LINE__)
 
 enum rfkill_hard_block_reasons {
@@ -1166,9 +1166,9 @@ cfg80211_chandef_create(struct cfg80211_chan_def *chandef,
 	KASSERT(chandef != NULL, ("%s: chandef is NULL\n", __func__));
 	KASSERT(chan != NULL, ("%s: chan is NULL\n", __func__));
 
+	memset(chandef, 0, sizeof(*chandef));
 	chandef->chan = chan;
 	chandef->center_freq2 = 0;	/* Set here and only overwrite if needed. */
-        chandef->chan = chan;
 
 	switch (chan_flag) {
 	case NL80211_CHAN_NO_HT:
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 11e8b031a97e..14404d4723f8 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -904,6 +904,7 @@ void linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *, uint64_t *,
     uint64_t *);
 struct wireless_dev *linuxkpi_ieee80211_vif_to_wdev(struct ieee80211_vif *);
 void linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *);
+void linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *);
 
 /* -------------------------------------------------------------------------- */
 
@@ -1403,7 +1404,7 @@ ieee80211_beacon_get_template(struct ieee80211_hw *hw,
 static __inline void
 ieee80211_beacon_loss(struct ieee80211_vif *vif)
 {
-	TODO();
+	linuxkpi_ieee80211_beacon_loss(vif);
 }
 
 static __inline void
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 2fe8b4ebddd0..3a70d8bbfcab 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -70,16 +70,21 @@ __FBSDID("$FreeBSD$");
 #include <linux/workqueue.h>
 #include "linux_80211.h"
 
-static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "Linux KPI 80211 compat");
+static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
 
 /* -------------------------------------------------------------------------- */
 
-int debug_80211;
-SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_80211, CTLFLAG_RWTUN,
-    &debug_80211, 0, "80211 debug Level");
+/* Keep public for as long as header files are using it too. */
+int linuxkpi_debug_80211;
 
-#define LINUXKPI_DEBUG_80211
 #ifdef LINUXKPI_DEBUG_80211
+SYSCTL_DECL(_compat_linuxkpi);
+SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
+    "LinuxKPI 802.11 compatibility layer");
+
+SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN,
+    &linuxkpi_debug_80211, 0, "LinuxKPI 802.11 debug level");
+
 #ifndef	D80211_TODO
 #define	D80211_TODO		0x1
 #endif
@@ -95,9 +100,10 @@ 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	UNIMPLEMENTED		if (debug_80211 & D80211_TODO)		\
+#define	D80211_TRACE_STA	0x10000
+#define	UNIMPLEMENTED		if (linuxkpi_debug_80211 & D80211_TODO)		\
     printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__)
-#define	TRACEOK()		if (debug_80211 & D80211_TRACEOK)	\
+#define	TRACEOK()		if (linuxkpi_debug_80211 & D80211_TRACEOK)	\
     printf("XXX-TODO %s:%d: TRACEPOINT\n", __func__, __LINE__)
 #else
 #define	UNIMPLEMENTED		do { } while (0)
@@ -112,6 +118,20 @@ SYSCTL_INT(_compat_linuxkpi, OID_AUTO, debug_80211, CTLFLAG_RWTUN,
 /* This is DSAP | SSAP | CTRL | ProtoID/OrgCode{3}. */
 const uint8_t rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
 
+const uint8_t tid_to_mac80211_ac[] = {
+	IEEE80211_AC_BE,
+	IEEE80211_AC_BK,
+	IEEE80211_AC_BK,
+	IEEE80211_AC_BE,
+	IEEE80211_AC_VI,
+	IEEE80211_AC_VI,
+	IEEE80211_AC_VO,
+	IEEE80211_AC_VO,
+#if 0
+	IEEE80211_AC_VO, /* We treat MGMT as TID 8, which is set as AC_VO */
+#endif
+};
+
 const struct cfg80211_ops linuxkpi_mac80211cfgops = {
 	/*
 	 * XXX TODO need a "glue layer" to link cfg80211 ops to
@@ -125,6 +145,118 @@ 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_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
+    const char *_f, int _l)
+{
+
+#ifdef LINUXKPI_DEBUG_80211
+	if ((linuxkpi_debug_80211 & D80211_TRACE_STA) == 0)
+		return;
+	if (lsta == NULL)
+		return;
+
+	printf("%s:%d lsta %p ni %p sta %p\n",
+	    _f, _l, lsta, ni, &lsta->sta);
+	if (ni != NULL)
+		ieee80211_dump_node(NULL, 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);
+#endif
+}
+
+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)
+{
+	struct lkpi_sta *lsta;
+	struct lkpi_vif *lvif;
+	struct ieee80211_vif *vif;
+	struct ieee80211_sta *sta;
+	int tid;
+
+	lsta = malloc(sizeof(*lsta) + hw->sta_data_size, M_LKPI80211,
+	    M_NOWAIT | M_ZERO);
+	if (lsta == NULL)
+		return (NULL);
+
+	lsta->added_to_drv = false;
+	lsta->state = IEEE80211_STA_NOTEXIST;
+#if 0
+	/*
+	 * This needs to be done in node_init() as ieee80211_alloc_node()
+	 * will initialise the refcount after us.
+	 */
+	lsta->ni = ieee80211_ref_node(ni);
+#endif
+	/* The back-pointer "drv_data" to net80211_node let's us get lsta. */
+	ni->ni_drv_data = lsta;
+
+	lvif = VAP_TO_LVIF(vap);
+	vif = LVIF_TO_VIF(lvif);
+	sta = LSTA_TO_STA(lsta);
+
+	IEEE80211_ADDR_COPY(sta->addr, mac);
+	for (tid = 0; tid < nitems(sta->txq); tid++) {
+		struct lkpi_txq *ltxq;
+
+		/*
+		 * We are neither limiting ourselves to hw.queues here,
+		 * nor do we check if driver wants IEEE80211_NUM_TIDS queue.
+		 */
+
+		ltxq = malloc(sizeof(*ltxq) + hw->txq_data_size,
+		    M_LKPI80211, M_NOWAIT | M_ZERO);
+		if (ltxq == NULL)
+			goto cleanup;
+		ltxq->seen_dequeue = false;
+		skb_queue_head_init(&ltxq->skbq);
+		/* iwlwifi//mvm/sta.c::tid_to_mac80211_ac[] */
+		if (tid == IEEE80211_NUM_TIDS) {
+			IMPROVE();
+			ltxq->txq.ac = IEEE80211_AC_VO;
+		} else {
+			ltxq->txq.ac = tid_to_mac80211_ac[tid & 7];
+		}
+		ltxq->txq.tid = tid;
+		ltxq->txq.sta = sta;
+		ltxq->txq.vif = vif;
+		sta->txq[tid] = &ltxq->txq;
+	}
+
+	/* Deferred TX path. */
+	mtx_init(&lsta->txq_mtx, "lsta_txq", NULL, MTX_DEF);
+	TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta);
+	mbufq_init(&lsta->txq, IFQ_MAXLEN);
+
+	return (lsta);
+
+cleanup:
+	for (; tid >= 0; tid--)
+		free(sta->txq[tid], M_LKPI80211);
+	free(lsta, M_LKPI80211);
+	return (NULL);
+}
+
 static enum nl80211_band
 lkpi_net80211_chan_to_nl80211_band(struct ieee80211_channel *c)
 {
@@ -501,9 +633,11 @@ lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
 	list_add(&addr->addr_list, &mc_list->addr_list);
 	mc_list->count++;
 
-	if (debug_80211 & D80211_TRACE)
+#ifdef LINUXKPI_DEBUG_80211
+	if (linuxkpi_debug_80211 & D80211_TRACE)
 		printf("%s:%d: mc_list count %d: added %6D\n",
 		    __func__, __LINE__, mc_list->count, addr->addr, ":");
+#endif
 
 	return (1);
 }
@@ -549,9 +683,11 @@ lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
 	total_flags = changed_flags;
 	lkpi_80211_mo_configure_filter(hw, changed_flags, &total_flags, mc);
 
-	if (debug_80211 & D80211_TRACE)
+#ifdef LINUXKPI_DEBUG_80211
+	if (linuxkpi_debug_80211 & D80211_TRACE)
 		printf("%s: changed_flags %#06x count %d total_flags %#010x\n",
 		    __func__, changed_flags, mc_list.count, total_flags);
+#endif
 
 	if (mc_list.count != 0) {
 		list_for_each_safe(le, next, &mc_list.addr_list) {
@@ -564,19 +700,61 @@ lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
 	    __func__, &mc_list, mc_list.count));
 }
 
-const uint8_t tid_to_mac80211_ac[] = {
-	IEEE80211_AC_BE,
-	IEEE80211_AC_BK,
-	IEEE80211_AC_BK,
-	IEEE80211_AC_BE,
-	IEEE80211_AC_VI,
-	IEEE80211_AC_VI,
-	IEEE80211_AC_VO,
-	IEEE80211_AC_VO,
-#if 0
-	IEEE80211_AC_VO, /* We treat MGMT as TID 8, which is set as AC_VO */
+static enum ieee80211_bss_changed
+lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
+    struct ieee80211vap *vap, const char *_f, int _l)
+{
+	enum ieee80211_bss_changed bss_changed;
+
+	bss_changed = 0;
+
+#ifdef LINUXKPI_DEBUG_80211
+	if (linuxkpi_debug_80211 & D80211_TRACE)
+		printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
+		    "dtim_period %u sync_dtim_count %u sync_tsf %ju "
+		    "sync_device_ts %u bss_changed %#08x\n",
+			__func__, __LINE__, _f, _l,
+			vif->bss_conf.assoc, vif->bss_conf.aid,
+			vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
+			vif->bss_conf.sync_dtim_count,
+			(uintmax_t)vif->bss_conf.sync_tsf,
+			vif->bss_conf.sync_device_ts,
+			bss_changed);
 #endif
-};
+
+	if (vif->bss_conf.beacon_int != ni->ni_intval) {
+		vif->bss_conf.beacon_int = ni->ni_intval;
+		/* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */
+		if (vif->bss_conf.beacon_int < 16)
+			vif->bss_conf.beacon_int = 16;
+		bss_changed |= BSS_CHANGED_BEACON_INT;
+	}
+	if (vif->bss_conf.dtim_period != vap->iv_dtim_period &&
+	    vap->iv_dtim_period > 0) {
+		vif->bss_conf.dtim_period = vap->iv_dtim_period;
+		bss_changed |= BSS_CHANGED_BEACON_INFO;
+	}
+
+	vif->bss_conf.sync_dtim_count = vap->iv_dtim_count;
+	vif->bss_conf.sync_tsf = le64toh(ni->ni_tstamp.tsf);
+	/* vif->bss_conf.sync_device_ts = set in linuxkpi_ieee80211_rx. */
+
+#ifdef LINUXKPI_DEBUG_80211
+	if (linuxkpi_debug_80211 & D80211_TRACE)
+		printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
+		    "dtim_period %u sync_dtim_count %u sync_tsf %ju "
+		    "sync_device_ts %u bss_changed %#08x\n",
+			__func__, __LINE__, _f, _l,
+			vif->bss_conf.assoc, vif->bss_conf.aid,
+			vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
+			vif->bss_conf.sync_dtim_count,
+			(uintmax_t)vif->bss_conf.sync_tsf,
+			vif->bss_conf.sync_device_ts,
+			bss_changed);
+#endif
+
+	return (bss_changed);
+}
 
 static void
 lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
@@ -640,6 +818,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,
@@ -720,6 +902,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). */
@@ -747,7 +931,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;
@@ -780,8 +963,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);
@@ -790,26 +971,40 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 	bss_changed |= BSS_CHANGED_TXPOWER;
 	vif->bss_conf.idle = false;
 	bss_changed |= BSS_CHANGED_IDLE;
-	vif->bss_conf.beacon_int = ni->ni_intval;
-	/* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */
-	if (vif->bss_conf.beacon_int < 16)
-		vif->bss_conf.beacon_int = 16;
-	bss_changed |= BSS_CHANGED_BEACON_INT;
-	vif->bss_conf.dtim_period = vap->iv_dtim_period;
-	bss_changed |= BSS_CHANGED_BEACON_INFO;
-	vif->bss_conf.sync_dtim_count = vap->iv_dtim_count;
-	vif->bss_conf.sync_tsf = le64toh(ni->ni_tstamp.tsf);
-	/* vif->bss_conf.sync_device_ts = set in linuxkpi_ieee80211_rx. */
 
 	/* Should almost assert it is this. */
 	vif->bss_conf.assoc = false;
 	vif->bss_conf.aid = 0;
+
+	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
+
 	/* RATES */
 	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));
@@ -823,11 +1018,15 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 	lsta->added_to_drv = true;	/* mo manages. */
 #endif
 
+	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+
 	/*
 	 * Wakeup all queues now that sta is there so we have as much time to
 	 * 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);
 
@@ -914,33 +1113,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_lsta_dump(lsta, ni, __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));
@@ -949,18 +1137,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?");
@@ -970,8 +1158,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_lsta_dump(lsta, ni, __func__, __LINE__);
 
+	lkpi_lsta_remove(lsta, lvif);
+
+	/* conf_tx */
+
+	/* Take the chan ctx down. */
 	if (vif->chanctx_conf != NULL) {
 		struct ieee80211_chanctx_conf *conf;
 
@@ -985,12 +1178,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);
@@ -1130,7 +1319,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;
@@ -1140,6 +1329,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;
@@ -1149,11 +1339,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_lsta_dump(lsta, ni, __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_lsta_dump(lsta, ni, __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));
@@ -1162,6 +1383,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 "
@@ -1170,23 +1399,84 @@ lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, in
 	if (error != 0)
 		goto out;
 
-	IMPROVE("anything else?");
+	lkpi_lsta_dump(lsta, ni, __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_lsta_dump(lsta, ni, __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);
 }
 
@@ -1195,9 +1485,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);
 }
 
@@ -1266,18 +1554,9 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 		vif->bss_conf.qos ^= 1;
 		bss_changed |= BSS_CHANGED_QOS;
 	}
-	if (vif->bss_conf.beacon_int != ni->ni_intval) {
-		vif->bss_conf.beacon_int = ni->ni_intval;
-		/* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */
-		if (vif->bss_conf.beacon_int < 16)
-			vif->bss_conf.beacon_int = 16;
-		bss_changed |= BSS_CHANGED_BEACON_INT;
-	}
-	if (vif->bss_conf.dtim_period != vap->iv_dtim_period &&
-	    vap->iv_dtim_period > 0) {
-		vif->bss_conf.dtim_period = vap->iv_dtim_period;
-		bss_changed |= BSS_CHANGED_BEACON_INFO;
-	}
+
+	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
+
 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
 
 	/* - change_chanctx (if needed)
@@ -1331,6 +1610,10 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 	 */
 	IMPROVE("Need that bssid setting, and the keys");
 
+	bss_changed = 0;
+	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
+	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+
 out:
 	IEEE80211_LOCK(vap->iv_ic);
 	if (ni != NULL)
@@ -1359,6 +1642,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;
@@ -1368,11 +1655,167 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
 
 	/* Keep ni around. */
 	ni = ieee80211_ref_node(vap->iv_bss);
+	lsta = ni->ni_drv_data;
+	sta = LSTA_TO_STA(lsta);
+
+	lkpi_lsta_dump(lsta, ni, __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_lsta_dump(lsta, ni, __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 "
+	    "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_lsta_dump(lsta, ni, __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_lsta_dump(lsta, ni, __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_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;
*** 999 LINES SKIPPED ***