xref: /freebsd/sys/compat/linuxkpi/common/src/linux_80211.c (revision fa4e4257943650c0b5f58c01bb0bdfadea61dfb2)
16b4cac81SBjoern A. Zeeb /*-
28ac540d3SBjoern A. Zeeb  * Copyright (c) 2020-2023 The FreeBSD Foundation
3086be6a8SBjoern A. Zeeb  * Copyright (c) 2020-2022 Bjoern A. Zeeb
46b4cac81SBjoern A. Zeeb  *
56b4cac81SBjoern A. Zeeb  * This software was developed by Björn Zeeb under sponsorship from
66b4cac81SBjoern A. Zeeb  * the FreeBSD Foundation.
76b4cac81SBjoern A. Zeeb  *
86b4cac81SBjoern A. Zeeb  * Redistribution and use in source and binary forms, with or without
96b4cac81SBjoern A. Zeeb  * modification, are permitted provided that the following conditions
106b4cac81SBjoern A. Zeeb  * are met:
116b4cac81SBjoern A. Zeeb  * 1. Redistributions of source code must retain the above copyright
126b4cac81SBjoern A. Zeeb  *    notice, this list of conditions and the following disclaimer.
136b4cac81SBjoern A. Zeeb  * 2. Redistributions in binary form must reproduce the above copyright
146b4cac81SBjoern A. Zeeb  *    notice, this list of conditions and the following disclaimer in the
156b4cac81SBjoern A. Zeeb  *    documentation and/or other materials provided with the distribution.
166b4cac81SBjoern A. Zeeb  *
176b4cac81SBjoern A. Zeeb  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
186b4cac81SBjoern A. Zeeb  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
196b4cac81SBjoern A. Zeeb  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
206b4cac81SBjoern A. Zeeb  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
216b4cac81SBjoern A. Zeeb  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
226b4cac81SBjoern A. Zeeb  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
236b4cac81SBjoern A. Zeeb  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
246b4cac81SBjoern A. Zeeb  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
256b4cac81SBjoern A. Zeeb  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
266b4cac81SBjoern A. Zeeb  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
276b4cac81SBjoern A. Zeeb  * SUCH DAMAGE.
286b4cac81SBjoern A. Zeeb  */
296b4cac81SBjoern A. Zeeb 
306b4cac81SBjoern A. Zeeb /*
316b4cac81SBjoern A. Zeeb  * Public functions are called linuxkpi_*().
326b4cac81SBjoern A. Zeeb  * Internal (static) functions are called lkpi_*().
336b4cac81SBjoern A. Zeeb  *
346b4cac81SBjoern A. Zeeb  * The internal structures holding metadata over public structures are also
356b4cac81SBjoern A. Zeeb  * called lkpi_xxx (usually with a member at the end called xxx).
366b4cac81SBjoern A. Zeeb  * Note: we do not replicate the structure names but the general variable names
376b4cac81SBjoern A. Zeeb  * for these (e.g., struct hw -> struct lkpi_hw, struct sta -> struct lkpi_sta).
386b4cac81SBjoern A. Zeeb  * There are macros to access one from the other.
396b4cac81SBjoern A. Zeeb  * We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta).
406b4cac81SBjoern A. Zeeb  */
416b4cac81SBjoern A. Zeeb 
426b4cac81SBjoern A. Zeeb #include <sys/param.h>
436b4cac81SBjoern A. Zeeb #include <sys/types.h>
446b4cac81SBjoern A. Zeeb #include <sys/kernel.h>
456b4cac81SBjoern A. Zeeb #include <sys/errno.h>
466b4cac81SBjoern A. Zeeb #include <sys/malloc.h>
476b4cac81SBjoern A. Zeeb #include <sys/module.h>
486b4cac81SBjoern A. Zeeb #include <sys/mutex.h>
496b4cac81SBjoern A. Zeeb #include <sys/socket.h>
506b4cac81SBjoern A. Zeeb #include <sys/sysctl.h>
516b4cac81SBjoern A. Zeeb #include <sys/queue.h>
526b4cac81SBjoern A. Zeeb #include <sys/taskqueue.h>
53527687a9SBjoern A. Zeeb #include <sys/libkern.h>
546b4cac81SBjoern A. Zeeb 
556b4cac81SBjoern A. Zeeb #include <net/if.h>
566b4cac81SBjoern A. Zeeb #include <net/if_var.h>
576b4cac81SBjoern A. Zeeb #include <net/if_media.h>
586b4cac81SBjoern A. Zeeb #include <net/ethernet.h>
596b4cac81SBjoern A. Zeeb 
606b4cac81SBjoern A. Zeeb #include <net80211/ieee80211_var.h>
616b4cac81SBjoern A. Zeeb #include <net80211/ieee80211_proto.h>
626b4cac81SBjoern A. Zeeb #include <net80211/ieee80211_ratectl.h>
636b4cac81SBjoern A. Zeeb #include <net80211/ieee80211_radiotap.h>
649fb91463SBjoern A. Zeeb #include <net80211/ieee80211_vht.h>
656b4cac81SBjoern A. Zeeb 
666b4cac81SBjoern A. Zeeb #define	LINUXKPI_NET80211
676b4cac81SBjoern A. Zeeb #include <net/mac80211.h>
686b4cac81SBjoern A. Zeeb 
696b4cac81SBjoern A. Zeeb #include <linux/workqueue.h>
706b4cac81SBjoern A. Zeeb #include "linux_80211.h"
716b4cac81SBjoern A. Zeeb 
724a67f1dfSBjoern A. Zeeb #define	LKPI_80211_WME
73b35f6cd0SBjoern A. Zeeb /* #define	LKPI_80211_HW_CRYPTO */
749fb91463SBjoern A. Zeeb /* #define	LKPI_80211_VHT */
759fb91463SBjoern A. Zeeb /* #define	LKPI_80211_HT */
769fb91463SBjoern A. Zeeb #if defined(LKPI_80211_VHT) && !defined(LKPI_80211_HT)
779fb91463SBjoern A. Zeeb #define	LKPI_80211_HT
789fb91463SBjoern A. Zeeb #endif
79b35f6cd0SBjoern A. Zeeb 
806b4cac81SBjoern A. Zeeb static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
816b4cac81SBjoern A. Zeeb 
825a9a0d78SBjoern A. Zeeb /* XXX-BZ really want this and others in queue.h */
835a9a0d78SBjoern A. Zeeb #define	TAILQ_ELEM_INIT(elm, field) do {				\
845a9a0d78SBjoern A. Zeeb 	(elm)->field.tqe_next = NULL;					\
855a9a0d78SBjoern A. Zeeb 	(elm)->field.tqe_prev = NULL;					\
865a9a0d78SBjoern A. Zeeb } while (0)
875a9a0d78SBjoern A. Zeeb 
886b4cac81SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
896b4cac81SBjoern A. Zeeb 
909d9ba2b7SBjoern A. Zeeb /* Keep public for as long as header files are using it too. */
919d9ba2b7SBjoern A. Zeeb int linuxkpi_debug_80211;
926b4cac81SBjoern A. Zeeb 
936b4cac81SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
949d9ba2b7SBjoern A. Zeeb SYSCTL_DECL(_compat_linuxkpi);
959d9ba2b7SBjoern A. Zeeb SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
969d9ba2b7SBjoern A. Zeeb     "LinuxKPI 802.11 compatibility layer");
979d9ba2b7SBjoern A. Zeeb 
989d9ba2b7SBjoern A. Zeeb SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN,
999d9ba2b7SBjoern A. Zeeb     &linuxkpi_debug_80211, 0, "LinuxKPI 802.11 debug level");
1009d9ba2b7SBjoern A. Zeeb 
1019d9ba2b7SBjoern A. Zeeb #define	UNIMPLEMENTED		if (linuxkpi_debug_80211 & D80211_TODO)		\
1026b4cac81SBjoern A. Zeeb     printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__)
1039d9ba2b7SBjoern A. Zeeb #define	TRACEOK()		if (linuxkpi_debug_80211 & D80211_TRACEOK)	\
1046b4cac81SBjoern A. Zeeb     printf("XXX-TODO %s:%d: TRACEPOINT\n", __func__, __LINE__)
1056b4cac81SBjoern A. Zeeb #else
1066b4cac81SBjoern A. Zeeb #define	UNIMPLEMENTED		do { } while (0)
1076b4cac81SBjoern A. Zeeb #define	TRACEOK()		do { } while (0)
1086b4cac81SBjoern A. Zeeb #endif
1096b4cac81SBjoern A. Zeeb 
1106b4cac81SBjoern A. Zeeb /* #define	PREP_TX_INFO_DURATION	(IEEE80211_TRANS_WAIT * 1000) */
1116b4cac81SBjoern A. Zeeb #ifndef PREP_TX_INFO_DURATION
1126b4cac81SBjoern A. Zeeb #define	PREP_TX_INFO_DURATION	0 /* Let the driver do its thing. */
1136b4cac81SBjoern A. Zeeb #endif
1146b4cac81SBjoern A. Zeeb 
1156b4cac81SBjoern A. Zeeb /* This is DSAP | SSAP | CTRL | ProtoID/OrgCode{3}. */
1166b4cac81SBjoern A. Zeeb const uint8_t rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
1176b4cac81SBjoern A. Zeeb 
118b0f73768SBjoern A. Zeeb /* IEEE 802.11-05/0257r1 */
119b0f73768SBjoern A. Zeeb const uint8_t bridge_tunnel_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
120b0f73768SBjoern A. Zeeb 
121fb3c249eSBjoern A. Zeeb /* IEEE 802.11e Table 20i-UP-to-AC mappings. */
122fb3c249eSBjoern A. Zeeb static const uint8_t ieee80211e_up_to_ac[] = {
1234f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_BE,
1244f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_BK,
1254f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_BK,
1264f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_BE,
1274f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_VI,
1284f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_VI,
1294f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_VO,
1304f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_VO,
1314f61ef8bSBjoern A. Zeeb #if 0
1324f61ef8bSBjoern A. Zeeb 	IEEE80211_AC_VO, /* We treat MGMT as TID 8, which is set as AC_VO */
1334f61ef8bSBjoern A. Zeeb #endif
1344f61ef8bSBjoern A. Zeeb };
1354f61ef8bSBjoern A. Zeeb 
1366b4cac81SBjoern A. Zeeb const struct cfg80211_ops linuxkpi_mac80211cfgops = {
1376b4cac81SBjoern A. Zeeb 	/*
1386b4cac81SBjoern A. Zeeb 	 * XXX TODO need a "glue layer" to link cfg80211 ops to
1396b4cac81SBjoern A. Zeeb 	 * mac80211 and to the driver or net80211.
1406b4cac81SBjoern A. Zeeb 	 * Can we pass some on 1:1? Need to compare the (*f)().
1416b4cac81SBjoern A. Zeeb 	 */
1426b4cac81SBjoern A. Zeeb };
1436b4cac81SBjoern A. Zeeb 
1446b4cac81SBjoern A. Zeeb static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *,
1456b4cac81SBjoern A. Zeeb     struct ieee80211_node *);
1466b4cac81SBjoern A. Zeeb static void lkpi_80211_txq_task(void *, int);
1476b4cac81SBjoern A. Zeeb static void lkpi_ieee80211_free_skb_mbuf(void *);
1484a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
1494a67f1dfSBjoern A. Zeeb static int lkpi_wme_update(struct lkpi_hw *, struct ieee80211vap *, bool);
1504a67f1dfSBjoern A. Zeeb #endif
1516b4cac81SBjoern A. Zeeb 
1529fb91463SBjoern A. Zeeb #if defined(LKPI_80211_HT)
1539fb91463SBjoern A. Zeeb static void
1549fb91463SBjoern A. Zeeb lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *ht_rx_nss)
1559fb91463SBjoern A. Zeeb {
1569fb91463SBjoern A. Zeeb 	struct ieee80211vap *vap;
1579fb91463SBjoern A. Zeeb 	uint8_t *ie;
1589fb91463SBjoern A. Zeeb 	struct ieee80211_ht_cap *htcap;
1599fb91463SBjoern A. Zeeb 	int i, rx_nss;
1609fb91463SBjoern A. Zeeb 
1619fb91463SBjoern A. Zeeb 	if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
1629fb91463SBjoern A. Zeeb 		return;
1639fb91463SBjoern A. Zeeb 
1649fb91463SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
1659fb91463SBjoern A. Zeeb 	    IEEE80211_IS_CHAN_HT40(ni->ni_chan))
1669fb91463SBjoern A. Zeeb 		sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40;
1679fb91463SBjoern A. Zeeb 
1689fb91463SBjoern A. Zeeb 	sta->deflink.ht_cap.ht_supported = true;
1699fb91463SBjoern A. Zeeb 
1709fb91463SBjoern A. Zeeb 	/* htcap->ampdu_params_info */
1719fb91463SBjoern A. Zeeb 	vap = ni->ni_vap;
1729fb91463SBjoern A. Zeeb 	sta->deflink.ht_cap.ampdu_density = _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
1739fb91463SBjoern A. Zeeb 	if (sta->deflink.ht_cap.ampdu_density > vap->iv_ampdu_density)
1749fb91463SBjoern A. Zeeb 		sta->deflink.ht_cap.ampdu_density = vap->iv_ampdu_density;
1759fb91463SBjoern A. Zeeb 	sta->deflink.ht_cap.ampdu_factor = _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
1769fb91463SBjoern A. Zeeb 	if (sta->deflink.ht_cap.ampdu_factor > vap->iv_ampdu_rxmax)
1779fb91463SBjoern A. Zeeb 		sta->deflink.ht_cap.ampdu_factor = vap->iv_ampdu_rxmax;
1789fb91463SBjoern A. Zeeb 
1799fb91463SBjoern A. Zeeb 	ie = ni->ni_ies.htcap_ie;
1809fb91463SBjoern A. Zeeb 	KASSERT(ie != NULL, ("%s: HT but no htcap_ie on ni %p\n", __func__, ni));
1819fb91463SBjoern A. Zeeb 	if (ie[0] == IEEE80211_ELEMID_VENDOR)
1829fb91463SBjoern A. Zeeb 		ie += 4;
1839fb91463SBjoern A. Zeeb 	ie += 2;
1849fb91463SBjoern A. Zeeb 	htcap = (struct ieee80211_ht_cap *)ie;
1859fb91463SBjoern A. Zeeb 	sta->deflink.ht_cap.cap = htcap->cap_info;
1869fb91463SBjoern A. Zeeb 	sta->deflink.ht_cap.mcs = htcap->mcs;
1879fb91463SBjoern A. Zeeb 
1889fb91463SBjoern A. Zeeb 	rx_nss = 0;
1899fb91463SBjoern A. Zeeb 	for (i = 0; i < nitems(htcap->mcs.rx_mask); i++) {
1909fb91463SBjoern A. Zeeb 		if (htcap->mcs.rx_mask[i])
1919fb91463SBjoern A. Zeeb 			rx_nss++;
1929fb91463SBjoern A. Zeeb 	}
1939fb91463SBjoern A. Zeeb 	if (ht_rx_nss != NULL)
1949fb91463SBjoern A. Zeeb 		*ht_rx_nss = rx_nss;
1959fb91463SBjoern A. Zeeb 
1969fb91463SBjoern A. Zeeb 	IMPROVE("sta->wme, sta->deflink.agg.max*");
1979fb91463SBjoern A. Zeeb }
1989fb91463SBjoern A. Zeeb #endif
1999fb91463SBjoern A. Zeeb 
2009fb91463SBjoern A. Zeeb #if defined(LKPI_80211_VHT)
2019fb91463SBjoern A. Zeeb static void
2029fb91463SBjoern A. Zeeb lkpi_sta_sync_vht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *vht_rx_nss)
2039fb91463SBjoern A. Zeeb {
2049fb91463SBjoern A. Zeeb 
2059fb91463SBjoern A. Zeeb 	if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0)
2069fb91463SBjoern A. Zeeb 		return;
2079fb91463SBjoern A. Zeeb 
2089fb91463SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
2099fb91463SBjoern A. Zeeb #ifdef __notyet__
2109fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
2119fb91463SBjoern A. Zeeb 			sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; /* XXX? */
2129fb91463SBjoern A. Zeeb 		} else
2139fb91463SBjoern A. Zeeb #endif
2149fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
2159fb91463SBjoern A. Zeeb 			sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160;
2169fb91463SBjoern A. Zeeb 		else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
2179fb91463SBjoern A. Zeeb 			sta->deflink.bandwidth = IEEE80211_STA_RX_BW_80;
2189fb91463SBjoern A. Zeeb 	}
2199fb91463SBjoern A. Zeeb 
2209fb91463SBjoern A. Zeeb 	IMPROVE("VHT sync ni to sta");
2219fb91463SBjoern A. Zeeb 	return;
2229fb91463SBjoern A. Zeeb }
2239fb91463SBjoern A. Zeeb #endif
2249fb91463SBjoern A. Zeeb 
225d9f59799SBjoern A. Zeeb static void
22667674c1cSBjoern A. Zeeb lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
22767674c1cSBjoern A. Zeeb     const char *_f, int _l)
228d9f59799SBjoern A. Zeeb {
229d9f59799SBjoern A. Zeeb 
2309d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
2319d9ba2b7SBjoern A. Zeeb 	if ((linuxkpi_debug_80211 & D80211_TRACE_STA) == 0)
232d9f59799SBjoern A. Zeeb 		return;
233d9f59799SBjoern A. Zeeb 	if (lsta == NULL)
234d9f59799SBjoern A. Zeeb 		return;
235d9f59799SBjoern A. Zeeb 
236d9f59799SBjoern A. Zeeb 	printf("%s:%d lsta %p ni %p sta %p\n",
23767674c1cSBjoern A. Zeeb 	    _f, _l, lsta, ni, &lsta->sta);
23867674c1cSBjoern A. Zeeb 	if (ni != NULL)
23967674c1cSBjoern A. Zeeb 		ieee80211_dump_node(NULL, ni);
240d9f59799SBjoern A. Zeeb 	printf("\ttxq_task txq len %d mtx\n", mbufq_len(&lsta->txq));
241d9f59799SBjoern A. Zeeb 	printf("\tkc %p state %d added_to_drv %d in_mgd %d\n",
242d9f59799SBjoern A. Zeeb 		lsta->kc, lsta->state, lsta->added_to_drv, lsta->in_mgd);
2439d9ba2b7SBjoern A. Zeeb #endif
244d9f59799SBjoern A. Zeeb }
245d9f59799SBjoern A. Zeeb 
246d9f59799SBjoern A. Zeeb static void
247d9f59799SBjoern A. Zeeb lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif)
248d9f59799SBjoern A. Zeeb {
249d9f59799SBjoern A. Zeeb 
250d9f59799SBjoern A. Zeeb 
251d9f59799SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
2520936c648SBjoern A. Zeeb 	KASSERT(lsta->lsta_entry.tqe_prev != NULL,
2530936c648SBjoern A. Zeeb 	    ("%s: lsta %p lsta_entry.tqe_prev %p ni %p\n", __func__,
2540936c648SBjoern A. Zeeb 	    lsta, lsta->lsta_entry.tqe_prev, lsta->ni));
255d9f59799SBjoern A. Zeeb 	TAILQ_REMOVE(&lvif->lsta_head, lsta, lsta_entry);
256d9f59799SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
257d9f59799SBjoern A. Zeeb }
258d9f59799SBjoern A. Zeeb 
2594f61ef8bSBjoern A. Zeeb static struct lkpi_sta *
2604f61ef8bSBjoern A. Zeeb lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
2614f61ef8bSBjoern A. Zeeb     struct ieee80211_hw *hw, struct ieee80211_node *ni)
2624f61ef8bSBjoern A. Zeeb {
2634f61ef8bSBjoern A. Zeeb 	struct lkpi_sta *lsta;
2644f61ef8bSBjoern A. Zeeb 	struct lkpi_vif *lvif;
2654f61ef8bSBjoern A. Zeeb 	struct ieee80211_vif *vif;
2664f61ef8bSBjoern A. Zeeb 	struct ieee80211_sta *sta;
267b6b352e4SBjoern A. Zeeb 	int band, i, tid;
2689fb91463SBjoern A. Zeeb 	int ht_rx_nss;
2699fb91463SBjoern A. Zeeb 	int vht_rx_nss;
2704f61ef8bSBjoern A. Zeeb 
2714f61ef8bSBjoern A. Zeeb 	lsta = malloc(sizeof(*lsta) + hw->sta_data_size, M_LKPI80211,
2724f61ef8bSBjoern A. Zeeb 	    M_NOWAIT | M_ZERO);
2734f61ef8bSBjoern A. Zeeb 	if (lsta == NULL)
2744f61ef8bSBjoern A. Zeeb 		return (NULL);
2754f61ef8bSBjoern A. Zeeb 
2764f61ef8bSBjoern A. Zeeb 	lsta->added_to_drv = false;
2774f61ef8bSBjoern A. Zeeb 	lsta->state = IEEE80211_STA_NOTEXIST;
2784f61ef8bSBjoern A. Zeeb 	/*
2790936c648SBjoern A. Zeeb 	 * Link the ni to the lsta here without taking a reference.
2800936c648SBjoern A. Zeeb 	 * For one we would have to take the reference in node_init()
2810936c648SBjoern A. Zeeb 	 * as ieee80211_alloc_node() will initialise the refcount after us.
2820936c648SBjoern A. Zeeb 	 * For the other a ni and an lsta are 1:1 mapped and always together
2830936c648SBjoern A. Zeeb 	 * from [ic_]node_alloc() to [ic_]node_free() so we are essentally
2840936c648SBjoern A. Zeeb 	 * using the ni references for the lsta as well despite it being
2850936c648SBjoern A. Zeeb 	 * two separate allocations.
2864f61ef8bSBjoern A. Zeeb 	 */
2870936c648SBjoern A. Zeeb 	lsta->ni = ni;
2884f61ef8bSBjoern A. Zeeb 	/* The back-pointer "drv_data" to net80211_node let's us get lsta. */
2894f61ef8bSBjoern A. Zeeb 	ni->ni_drv_data = lsta;
2904f61ef8bSBjoern A. Zeeb 
2914f61ef8bSBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
2924f61ef8bSBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
2934f61ef8bSBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
2944f61ef8bSBjoern A. Zeeb 
2954f61ef8bSBjoern A. Zeeb 	IEEE80211_ADDR_COPY(sta->addr, mac);
296b6b352e4SBjoern A. Zeeb 
297b6b352e4SBjoern A. Zeeb 	/* TXQ */
2984f61ef8bSBjoern A. Zeeb 	for (tid = 0; tid < nitems(sta->txq); tid++) {
2994f61ef8bSBjoern A. Zeeb 		struct lkpi_txq *ltxq;
3004f61ef8bSBjoern A. Zeeb 
301d0d29110SBjoern A. Zeeb 		/* We are not limiting ourselves to hw.queues here. */
3024f61ef8bSBjoern A. Zeeb 		ltxq = malloc(sizeof(*ltxq) + hw->txq_data_size,
3034f61ef8bSBjoern A. Zeeb 		    M_LKPI80211, M_NOWAIT | M_ZERO);
3044f61ef8bSBjoern A. Zeeb 		if (ltxq == NULL)
3054f61ef8bSBjoern A. Zeeb 			goto cleanup;
3064f61ef8bSBjoern A. Zeeb 		/* iwlwifi//mvm/sta.c::tid_to_mac80211_ac[] */
3074f61ef8bSBjoern A. Zeeb 		if (tid == IEEE80211_NUM_TIDS) {
308d0d29110SBjoern A. Zeeb 			if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ)) {
309d0d29110SBjoern A. Zeeb 				free(ltxq, M_LKPI80211);
310d0d29110SBjoern A. Zeeb 				continue;
311d0d29110SBjoern A. Zeeb 			}
312d0d29110SBjoern A. Zeeb 			IMPROVE("AP/if we support non-STA here too");
3134f61ef8bSBjoern A. Zeeb 			ltxq->txq.ac = IEEE80211_AC_VO;
3144f61ef8bSBjoern A. Zeeb 		} else {
315fb3c249eSBjoern A. Zeeb 			ltxq->txq.ac = ieee80211e_up_to_ac[tid & 7];
3164f61ef8bSBjoern A. Zeeb 		}
317d0d29110SBjoern A. Zeeb 		ltxq->seen_dequeue = false;
3180cbcfa19SBjoern A. Zeeb 		ltxq->stopped = false;
319d0d29110SBjoern A. Zeeb 		ltxq->txq.vif = vif;
3204f61ef8bSBjoern A. Zeeb 		ltxq->txq.tid = tid;
3214f61ef8bSBjoern A. Zeeb 		ltxq->txq.sta = sta;
3220cbcfa19SBjoern A. Zeeb 		TAILQ_ELEM_INIT(ltxq, txq_entry);
323d0d29110SBjoern A. Zeeb 		skb_queue_head_init(&ltxq->skbq);
324eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_LOCK_INIT(ltxq);
3254f61ef8bSBjoern A. Zeeb 		sta->txq[tid] = &ltxq->txq;
3264f61ef8bSBjoern A. Zeeb 	}
3274f61ef8bSBjoern A. Zeeb 
328b6b352e4SBjoern A. Zeeb 	/* Deflink information. */
329b6b352e4SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
330b6b352e4SBjoern A. Zeeb 		struct ieee80211_supported_band *supband;
331b6b352e4SBjoern A. Zeeb 
332b6b352e4SBjoern A. Zeeb 		supband = hw->wiphy->bands[band];
333b6b352e4SBjoern A. Zeeb 		if (supband == NULL)
334b6b352e4SBjoern A. Zeeb 			continue;
335b6b352e4SBjoern A. Zeeb 
336b6b352e4SBjoern A. Zeeb 		for (i = 0; i < supband->n_bitrates; i++) {
337b6b352e4SBjoern A. Zeeb 
338b6b352e4SBjoern A. Zeeb 			IMPROVE("Further supband->bitrates[i]* checks?");
339b6b352e4SBjoern A. Zeeb 			/* or should we get them from the ni? */
340b6b352e4SBjoern A. Zeeb 			sta->deflink.supp_rates[band] |= BIT(i);
341b6b352e4SBjoern A. Zeeb 		}
342b6b352e4SBjoern A. Zeeb 	}
3439fb91463SBjoern A. Zeeb 
3446ffb7bd4SBjoern A. Zeeb 	sta->deflink.smps_mode = IEEE80211_SMPS_OFF;
3459fb91463SBjoern A. Zeeb 	sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
3469fb91463SBjoern A. Zeeb 	sta->deflink.rx_nss = 0;
3479fb91463SBjoern A. Zeeb 
3489fb91463SBjoern A. Zeeb 	ht_rx_nss = 0;
3499fb91463SBjoern A. Zeeb #if defined(LKPI_80211_HT)
3509fb91463SBjoern A. Zeeb 	lkpi_sta_sync_ht_from_ni(sta, ni, &ht_rx_nss);
3519fb91463SBjoern A. Zeeb #endif
3529fb91463SBjoern A. Zeeb 	vht_rx_nss = 0;
3539fb91463SBjoern A. Zeeb #if defined(LKPI_80211_VHT)
3549fb91463SBjoern A. Zeeb 	lkpi_sta_sync_vht_from_ni(sta, ni, &vht_rx_nss);
3559fb91463SBjoern A. Zeeb #endif
3569fb91463SBjoern A. Zeeb 
3579fb91463SBjoern A. Zeeb 	sta->deflink.rx_nss = MAX(ht_rx_nss, sta->deflink.rx_nss);
3589fb91463SBjoern A. Zeeb 	sta->deflink.rx_nss = MAX(vht_rx_nss, sta->deflink.rx_nss);
3599fb91463SBjoern A. Zeeb 	IMPROVE("he, ... smps_mode, ..");
360b6b352e4SBjoern A. Zeeb 
3616ffb7bd4SBjoern A. Zeeb 	/* Link configuration. */
3626ffb7bd4SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(sta->deflink.addr, sta->addr);
3636ffb7bd4SBjoern A. Zeeb 	sta->link[0] = &sta->deflink;
3646ffb7bd4SBjoern A. Zeeb 	for (i = 1; i < nitems(sta->link); i++) {
3656ffb7bd4SBjoern A. Zeeb 		IMPROVE("more links; only link[0] = deflink currently.");
3666ffb7bd4SBjoern A. Zeeb 	}
3676ffb7bd4SBjoern A. Zeeb 
3684f61ef8bSBjoern A. Zeeb 	/* Deferred TX path. */
369*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_LOCK_INIT(lsta);
3704f61ef8bSBjoern A. Zeeb 	TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta);
3714f61ef8bSBjoern A. Zeeb 	mbufq_init(&lsta->txq, IFQ_MAXLEN);
3720936c648SBjoern A. Zeeb 	lsta->txq_ready = true;
3734f61ef8bSBjoern A. Zeeb 
3744f61ef8bSBjoern A. Zeeb 	return (lsta);
3754f61ef8bSBjoern A. Zeeb 
3764f61ef8bSBjoern A. Zeeb cleanup:
377eac3646fSBjoern A. Zeeb 	for (; tid >= 0; tid--) {
378eac3646fSBjoern A. Zeeb 		struct lkpi_txq *ltxq;
379eac3646fSBjoern A. Zeeb 
380eac3646fSBjoern A. Zeeb 		ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
381eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_LOCK_DESTROY(ltxq);
3824f61ef8bSBjoern A. Zeeb 		free(sta->txq[tid], M_LKPI80211);
383eac3646fSBjoern A. Zeeb 	}
3844f61ef8bSBjoern A. Zeeb 	free(lsta, M_LKPI80211);
3854f61ef8bSBjoern A. Zeeb 	return (NULL);
3864f61ef8bSBjoern A. Zeeb }
3874f61ef8bSBjoern A. Zeeb 
3880936c648SBjoern A. Zeeb static void
3890936c648SBjoern A. Zeeb lkpi_lsta_free(struct lkpi_sta *lsta, struct ieee80211_node *ni)
3900936c648SBjoern A. Zeeb {
3910936c648SBjoern A. Zeeb 	struct mbuf *m;
3920936c648SBjoern A. Zeeb 
3930936c648SBjoern A. Zeeb 	if (lsta->added_to_drv)
3940936c648SBjoern A. Zeeb 		panic("%s: Trying to free an lsta still known to firmware: "
3950936c648SBjoern A. Zeeb 		    "lsta %p ni %p added_to_drv %d\n",
3960936c648SBjoern A. Zeeb 		    __func__, lsta, ni, lsta->added_to_drv);
3970936c648SBjoern A. Zeeb 
3980936c648SBjoern A. Zeeb 	/* XXX-BZ free resources, ... */
3990936c648SBjoern A. Zeeb 	IMPROVE();
4000936c648SBjoern A. Zeeb 
401*fa4e4257SBjoern A. Zeeb 	/* Drain sta->txq[] */
402*fa4e4257SBjoern A. Zeeb 
403*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_LOCK(lsta);
4040936c648SBjoern A. Zeeb 	lsta->txq_ready = false;
405*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
4060936c648SBjoern A. Zeeb 
4070936c648SBjoern A. Zeeb 	/* Drain taskq, won't be restarted until added_to_drv is set again. */
4080936c648SBjoern A. Zeeb 	while (taskqueue_cancel(taskqueue_thread, &lsta->txq_task, NULL) != 0)
4090936c648SBjoern A. Zeeb 		taskqueue_drain(taskqueue_thread, &lsta->txq_task);
4100936c648SBjoern A. Zeeb 
4110936c648SBjoern A. Zeeb 	/* Flush mbufq (make sure to release ni refs!). */
4120936c648SBjoern A. Zeeb 	m = mbufq_dequeue(&lsta->txq);
4130936c648SBjoern A. Zeeb 	while (m != NULL) {
4140936c648SBjoern A. Zeeb 		struct ieee80211_node *nim;
4150936c648SBjoern A. Zeeb 
4160936c648SBjoern A. Zeeb 		nim = (struct ieee80211_node *)m->m_pkthdr.rcvif;
4170936c648SBjoern A. Zeeb 		if (nim != NULL)
4180936c648SBjoern A. Zeeb 			ieee80211_free_node(nim);
4190936c648SBjoern A. Zeeb 		m_freem(m);
4200936c648SBjoern A. Zeeb 		m = mbufq_dequeue(&lsta->txq);
4210936c648SBjoern A. Zeeb 	}
4220936c648SBjoern A. Zeeb 	KASSERT(mbufq_empty(&lsta->txq), ("%s: lsta %p has txq len %d != 0\n",
4230936c648SBjoern A. Zeeb 	    __func__, lsta, mbufq_len(&lsta->txq)));
424*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_LOCK_DESTROY(lsta);
4250936c648SBjoern A. Zeeb 
4260936c648SBjoern A. Zeeb 	/* Remove lsta from vif; that is done by the state machine.  Should assert it? */
4270936c648SBjoern A. Zeeb 
4280936c648SBjoern A. Zeeb 	IMPROVE("Make sure everything is cleaned up.");
4290936c648SBjoern A. Zeeb 
4300936c648SBjoern A. Zeeb 	/* Free lsta. */
4310936c648SBjoern A. Zeeb 	lsta->ni = NULL;
4320936c648SBjoern A. Zeeb 	ni->ni_drv_data = NULL;
4330936c648SBjoern A. Zeeb 	free(lsta, M_LKPI80211);
4340936c648SBjoern A. Zeeb }
4350936c648SBjoern A. Zeeb 
4360936c648SBjoern A. Zeeb 
4376b4cac81SBjoern A. Zeeb static enum nl80211_band
4386b4cac81SBjoern A. Zeeb lkpi_net80211_chan_to_nl80211_band(struct ieee80211_channel *c)
4396b4cac81SBjoern A. Zeeb {
4406b4cac81SBjoern A. Zeeb 
4416b4cac81SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_2GHZ(c))
4426b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_2GHZ);
4436b4cac81SBjoern A. Zeeb 	else if (IEEE80211_IS_CHAN_5GHZ(c))
4446b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_5GHZ);
4456b4cac81SBjoern A. Zeeb #ifdef __notyet__
4466b4cac81SBjoern A. Zeeb 	else if ()
4476b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_6GHZ);
4486b4cac81SBjoern A. Zeeb 	else if ()
4496b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_60GHZ);
4506b4cac81SBjoern A. Zeeb 	else if (IEEE80211_IS_CHAN_GSM(c))
4516b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_XXX);
4526b4cac81SBjoern A. Zeeb #endif
4536b4cac81SBjoern A. Zeeb 	else
4546b4cac81SBjoern A. Zeeb 		panic("%s: unsupported band. c %p flags %#x\n",
4556b4cac81SBjoern A. Zeeb 		    __func__, c, c->ic_flags);
4566b4cac81SBjoern A. Zeeb }
4576b4cac81SBjoern A. Zeeb 
4586b4cac81SBjoern A. Zeeb static uint32_t
4596b4cac81SBjoern A. Zeeb lkpi_nl80211_band_to_net80211_band(enum nl80211_band band)
4606b4cac81SBjoern A. Zeeb {
4616b4cac81SBjoern A. Zeeb 
4626b4cac81SBjoern A. Zeeb 	/* XXX-BZ this is just silly; net80211 is too convoluted. */
4636b4cac81SBjoern A. Zeeb 	/* IEEE80211_CHAN_A / _G / .. doesn't really work either. */
4646b4cac81SBjoern A. Zeeb 	switch (band) {
4656b4cac81SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
4666b4cac81SBjoern A. Zeeb 		return (IEEE80211_CHAN_2GHZ);
4676b4cac81SBjoern A. Zeeb 		break;
4686b4cac81SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
4696b4cac81SBjoern A. Zeeb 		return (IEEE80211_CHAN_5GHZ);
4706b4cac81SBjoern A. Zeeb 		break;
4716b4cac81SBjoern A. Zeeb 	case NL80211_BAND_60GHZ:
4726b4cac81SBjoern A. Zeeb 		break;
4736b4cac81SBjoern A. Zeeb 	case NL80211_BAND_6GHZ:
4746b4cac81SBjoern A. Zeeb 		break;
4756b4cac81SBjoern A. Zeeb 	default:
4766b4cac81SBjoern A. Zeeb 		panic("%s: unsupported band %u\n", __func__, band);
4776b4cac81SBjoern A. Zeeb 		break;
4786b4cac81SBjoern A. Zeeb 	}
4796b4cac81SBjoern A. Zeeb 
4806b4cac81SBjoern A. Zeeb 	IMPROVE();
4816b4cac81SBjoern A. Zeeb 	return (0x00);
4826b4cac81SBjoern A. Zeeb }
4836b4cac81SBjoern A. Zeeb 
484e3a0b120SBjoern A. Zeeb #if 0
4856b4cac81SBjoern A. Zeeb static enum ieee80211_ac_numbers
4866b4cac81SBjoern A. Zeeb lkpi_ac_net_to_l80211(int ac)
4876b4cac81SBjoern A. Zeeb {
4886b4cac81SBjoern A. Zeeb 
4896b4cac81SBjoern A. Zeeb 	switch (ac) {
4906b4cac81SBjoern A. Zeeb 	case WME_AC_VO:
4916b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_VO);
4926b4cac81SBjoern A. Zeeb 	case WME_AC_VI:
4936b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_VI);
4946b4cac81SBjoern A. Zeeb 	case WME_AC_BE:
4956b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_BE);
4966b4cac81SBjoern A. Zeeb 	case WME_AC_BK:
4976b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_BK);
4986b4cac81SBjoern A. Zeeb 	default:
4996b4cac81SBjoern A. Zeeb 		printf("%s: invalid WME_AC_* input: ac = %d\n", __func__, ac);
5006b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_BE);
5016b4cac81SBjoern A. Zeeb 	}
5026b4cac81SBjoern A. Zeeb }
503e3a0b120SBjoern A. Zeeb #endif
5046b4cac81SBjoern A. Zeeb 
5056b4cac81SBjoern A. Zeeb static enum nl80211_iftype
5066b4cac81SBjoern A. Zeeb lkpi_opmode_to_vif_type(enum ieee80211_opmode opmode)
5076b4cac81SBjoern A. Zeeb {
5086b4cac81SBjoern A. Zeeb 
5096b4cac81SBjoern A. Zeeb 	switch (opmode) {
5106b4cac81SBjoern A. Zeeb 	case IEEE80211_M_IBSS:
5116b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_ADHOC);
5126b4cac81SBjoern A. Zeeb 		break;
5136b4cac81SBjoern A. Zeeb 	case IEEE80211_M_STA:
5146b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_STATION);
5156b4cac81SBjoern A. Zeeb 		break;
5166b4cac81SBjoern A. Zeeb 	case IEEE80211_M_WDS:
5176b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_WDS);
5186b4cac81SBjoern A. Zeeb 		break;
5196b4cac81SBjoern A. Zeeb 	case IEEE80211_M_HOSTAP:
5206b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_AP);
5216b4cac81SBjoern A. Zeeb 		break;
5226b4cac81SBjoern A. Zeeb 	case IEEE80211_M_MONITOR:
5236b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_MONITOR);
5246b4cac81SBjoern A. Zeeb 		break;
5256b4cac81SBjoern A. Zeeb 	case IEEE80211_M_MBSS:
5266b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_MESH_POINT);
5276b4cac81SBjoern A. Zeeb 		break;
5286b4cac81SBjoern A. Zeeb 	case IEEE80211_M_AHDEMO:
5296b4cac81SBjoern A. Zeeb 		/* FALLTHROUGH */
5306b4cac81SBjoern A. Zeeb 	default:
5316b4cac81SBjoern A. Zeeb 		printf("ERROR: %s: unsupported opmode %d\n", __func__, opmode);
5326b4cac81SBjoern A. Zeeb 		/* FALLTHROUGH */
5336b4cac81SBjoern A. Zeeb 	}
5346b4cac81SBjoern A. Zeeb 	return (NL80211_IFTYPE_UNSPECIFIED);
5356b4cac81SBjoern A. Zeeb }
5366b4cac81SBjoern A. Zeeb 
537b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
5386b4cac81SBjoern A. Zeeb static uint32_t
5396b4cac81SBjoern A. Zeeb lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite)
5406b4cac81SBjoern A. Zeeb {
5416b4cac81SBjoern A. Zeeb 
5426b4cac81SBjoern A. Zeeb 	switch (wlan_cipher_suite) {
5436b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP40:
5446b4cac81SBjoern A. Zeeb 		return (IEEE80211_CRYPTO_WEP);
5456b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_TKIP:
5466b4cac81SBjoern A. Zeeb 		return (IEEE80211_CRYPTO_TKIP);
5476b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP:
5486b4cac81SBjoern A. Zeeb 		return (IEEE80211_CIPHER_AES_CCM);
5496b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP104:
5506b4cac81SBjoern A. Zeeb 		return (IEEE80211_CRYPTO_WEP);
5516b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_AES_CMAC:
5526b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_GCMP:
5536b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_GCMP_256:
5546b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP_256:
5556b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
5566b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
5576b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
5586b4cac81SBjoern A. Zeeb 		printf("%s: unsupported WLAN Cipher Suite %#08x | %u\n", __func__,
5596b4cac81SBjoern A. Zeeb 		    wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff);
5606b4cac81SBjoern A. Zeeb 		break;
5616b4cac81SBjoern A. Zeeb 	default:
5626b4cac81SBjoern A. Zeeb 		printf("%s: unknown WLAN Cipher Suite %#08x | %u\n", __func__,
5636b4cac81SBjoern A. Zeeb 		    wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff);
5646b4cac81SBjoern A. Zeeb 	}
5656b4cac81SBjoern A. Zeeb 
5666b4cac81SBjoern A. Zeeb 	return (0);
5676b4cac81SBjoern A. Zeeb }
5686b4cac81SBjoern A. Zeeb 
5696b4cac81SBjoern A. Zeeb static uint32_t
5706b4cac81SBjoern A. Zeeb lkpi_net80211_to_l80211_cipher_suite(uint32_t cipher, uint8_t keylen)
5716b4cac81SBjoern A. Zeeb {
5726b4cac81SBjoern A. Zeeb 
5736b4cac81SBjoern A. Zeeb 	switch (cipher) {
5746b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_TKIP:
5756b4cac81SBjoern A. Zeeb 		return (WLAN_CIPHER_SUITE_TKIP);
5766b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_AES_CCM:
5776b4cac81SBjoern A. Zeeb 		return (WLAN_CIPHER_SUITE_CCMP);
5786b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_WEP:
5796b4cac81SBjoern A. Zeeb 		if (keylen < 8)
5806b4cac81SBjoern A. Zeeb 			return (WLAN_CIPHER_SUITE_WEP40);
5816b4cac81SBjoern A. Zeeb 		else
5826b4cac81SBjoern A. Zeeb 			return (WLAN_CIPHER_SUITE_WEP104);
5836b4cac81SBjoern A. Zeeb 		break;
5846b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_AES_OCB:
5856b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_TKIPMIC:
5866b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_CKIP:
5876b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_NONE:
5886b4cac81SBjoern A. Zeeb 		printf("%s: unsupported cipher %#010x\n", __func__, cipher);
5896b4cac81SBjoern A. Zeeb 		break;
5906b4cac81SBjoern A. Zeeb 	default:
5916b4cac81SBjoern A. Zeeb 		printf("%s: unknown cipher %#010x\n", __func__, cipher);
5926b4cac81SBjoern A. Zeeb 	};
5936b4cac81SBjoern A. Zeeb 	return (0);
5946b4cac81SBjoern A. Zeeb }
5956b4cac81SBjoern A. Zeeb #endif
5966b4cac81SBjoern A. Zeeb 
5976b4cac81SBjoern A. Zeeb #ifdef __notyet__
5986b4cac81SBjoern A. Zeeb static enum ieee80211_sta_state
5996b4cac81SBjoern A. Zeeb lkpi_net80211_state_to_sta_state(enum ieee80211_state state)
6006b4cac81SBjoern A. Zeeb {
6016b4cac81SBjoern A. Zeeb 
6026b4cac81SBjoern A. Zeeb 	/*
6036b4cac81SBjoern A. Zeeb 	 * XXX-BZ The net80211 states are "try to ..", the lkpi8011 states are
6046b4cac81SBjoern A. Zeeb 	 * "done".  Also ASSOC/AUTHORIZED are both "RUN" then?
6056b4cac81SBjoern A. Zeeb 	 */
6066b4cac81SBjoern A. Zeeb 	switch (state) {
6076b4cac81SBjoern A. Zeeb 	case IEEE80211_S_INIT:
6086b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_NOTEXIST);
6096b4cac81SBjoern A. Zeeb 	case IEEE80211_S_SCAN:
6106b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_NONE);
6116b4cac81SBjoern A. Zeeb 	case IEEE80211_S_AUTH:
6126b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_AUTH);
6136b4cac81SBjoern A. Zeeb 	case IEEE80211_S_ASSOC:
6146b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_ASSOC);
6156b4cac81SBjoern A. Zeeb 	case IEEE80211_S_RUN:
6166b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_AUTHORIZED);
6176b4cac81SBjoern A. Zeeb 	case IEEE80211_S_CAC:
6186b4cac81SBjoern A. Zeeb 	case IEEE80211_S_CSA:
6196b4cac81SBjoern A. Zeeb 	case IEEE80211_S_SLEEP:
6206b4cac81SBjoern A. Zeeb 	default:
6216b4cac81SBjoern A. Zeeb 		UNIMPLEMENTED;
6226b4cac81SBjoern A. Zeeb 	};
6236b4cac81SBjoern A. Zeeb 
6246b4cac81SBjoern A. Zeeb 	return (IEEE80211_STA_NOTEXIST);
6256b4cac81SBjoern A. Zeeb }
6266b4cac81SBjoern A. Zeeb #endif
6276b4cac81SBjoern A. Zeeb 
6286b4cac81SBjoern A. Zeeb static struct linuxkpi_ieee80211_channel *
6296b4cac81SBjoern A. Zeeb lkpi_find_lkpi80211_chan(struct lkpi_hw *lhw,
6306b4cac81SBjoern A. Zeeb     struct ieee80211_channel *c)
6316b4cac81SBjoern A. Zeeb {
6326b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
6336b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *channels;
6346b4cac81SBjoern A. Zeeb 	enum nl80211_band band;
6356b4cac81SBjoern A. Zeeb 	int i, nchans;
6366b4cac81SBjoern A. Zeeb 
6376b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
6386b4cac81SBjoern A. Zeeb 	band = lkpi_net80211_chan_to_nl80211_band(c);
6396b4cac81SBjoern A. Zeeb 	if (hw->wiphy->bands[band] == NULL)
6406b4cac81SBjoern A. Zeeb 		return (NULL);
6416b4cac81SBjoern A. Zeeb 
6426b4cac81SBjoern A. Zeeb 	nchans = hw->wiphy->bands[band]->n_channels;
6436b4cac81SBjoern A. Zeeb 	if (nchans <= 0)
6446b4cac81SBjoern A. Zeeb 		return (NULL);
6456b4cac81SBjoern A. Zeeb 
6466b4cac81SBjoern A. Zeeb 	channels = hw->wiphy->bands[band]->channels;
6476b4cac81SBjoern A. Zeeb 	for (i = 0; i < nchans; i++) {
6486b4cac81SBjoern A. Zeeb 		if (channels[i].hw_value == c->ic_ieee)
6496b4cac81SBjoern A. Zeeb 			return (&channels[i]);
6506b4cac81SBjoern A. Zeeb 	}
6516b4cac81SBjoern A. Zeeb 
6526b4cac81SBjoern A. Zeeb 	return (NULL);
6536b4cac81SBjoern A. Zeeb }
6546b4cac81SBjoern A. Zeeb 
6552ac8a218SBjoern A. Zeeb #if 0
6566b4cac81SBjoern A. Zeeb static struct linuxkpi_ieee80211_channel *
6576b4cac81SBjoern A. Zeeb lkpi_get_lkpi80211_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
6586b4cac81SBjoern A. Zeeb {
6596b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
6606b4cac81SBjoern A. Zeeb 	struct ieee80211_channel *c;
6616b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
6626b4cac81SBjoern A. Zeeb 
6636b4cac81SBjoern A. Zeeb 	chan = NULL;
6646b4cac81SBjoern A. Zeeb 	if (ni != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC)
6656b4cac81SBjoern A. Zeeb 		c = ni->ni_chan;
6666b4cac81SBjoern A. Zeeb 	else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
6676b4cac81SBjoern A. Zeeb 		c = ic->ic_bsschan;
6686b4cac81SBjoern A. Zeeb 	else if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
6696b4cac81SBjoern A. Zeeb 		c = ic->ic_curchan;
6706b4cac81SBjoern A. Zeeb 	else
6716b4cac81SBjoern A. Zeeb 		c = NULL;
6726b4cac81SBjoern A. Zeeb 
6736b4cac81SBjoern A. Zeeb 	if (c != NULL && c != IEEE80211_CHAN_ANYC) {
6746b4cac81SBjoern A. Zeeb 		lhw = ic->ic_softc;
6756b4cac81SBjoern A. Zeeb 		chan = lkpi_find_lkpi80211_chan(lhw, c);
6766b4cac81SBjoern A. Zeeb 	}
6776b4cac81SBjoern A. Zeeb 
6786b4cac81SBjoern A. Zeeb 	return (chan);
6796b4cac81SBjoern A. Zeeb }
6802ac8a218SBjoern A. Zeeb #endif
6816b4cac81SBjoern A. Zeeb 
6822e183d99SBjoern A. Zeeb struct linuxkpi_ieee80211_channel *
6832e183d99SBjoern A. Zeeb linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
6842e183d99SBjoern A. Zeeb {
6852e183d99SBjoern A. Zeeb 	enum nl80211_band band;
6862e183d99SBjoern A. Zeeb 
6872e183d99SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
6882e183d99SBjoern A. Zeeb 		struct ieee80211_supported_band *supband;
6892e183d99SBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *channels;
6902e183d99SBjoern A. Zeeb 		int i;
6912e183d99SBjoern A. Zeeb 
6922e183d99SBjoern A. Zeeb 		supband = wiphy->bands[band];
6932e183d99SBjoern A. Zeeb 		if (supband == NULL || supband->n_channels == 0)
6942e183d99SBjoern A. Zeeb 			continue;
6952e183d99SBjoern A. Zeeb 
6962e183d99SBjoern A. Zeeb 		channels = supband->channels;
6972e183d99SBjoern A. Zeeb 		for (i = 0; i < supband->n_channels; i++) {
6982e183d99SBjoern A. Zeeb 			if (channels[i].center_freq == freq)
6992e183d99SBjoern A. Zeeb 				return (&channels[i]);
7002e183d99SBjoern A. Zeeb 		}
7012e183d99SBjoern A. Zeeb 	}
7022e183d99SBjoern A. Zeeb 
7032e183d99SBjoern A. Zeeb 	return (NULL);
7042e183d99SBjoern A. Zeeb }
7052e183d99SBjoern A. Zeeb 
706b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
7076b4cac81SBjoern A. Zeeb static int
7086b4cac81SBjoern A. Zeeb _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
7096b4cac81SBjoern A. Zeeb     enum set_key_cmd cmd)
7106b4cac81SBjoern A. Zeeb {
7116b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
7126b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
7136b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
7146b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
7156b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
7166b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
7176b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
7186b4cac81SBjoern A. Zeeb 	struct ieee80211_key_conf *kc;
7196b4cac81SBjoern A. Zeeb 	int error;
7206b4cac81SBjoern A. Zeeb 
7216b4cac81SBjoern A. Zeeb 	/* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */
7226b4cac81SBjoern A. Zeeb 
7236b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
7246b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
7256b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
7266b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
7276b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
7286b4cac81SBjoern A. Zeeb 
7296b4cac81SBjoern A. Zeeb 	memset(&kc, 0, sizeof(kc));
7306b4cac81SBjoern A. Zeeb 	kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO);
7316b4cac81SBjoern A. Zeeb 	kc->cipher = lkpi_net80211_to_l80211_cipher_suite(
7326b4cac81SBjoern A. Zeeb 	    k->wk_cipher->ic_cipher, k->wk_keylen);
7336b4cac81SBjoern A. Zeeb 	kc->keyidx = k->wk_keyix;
7346b4cac81SBjoern A. Zeeb #if 0
7356b4cac81SBjoern A. Zeeb 	kc->hw_key_idx = /* set by hw and needs to be passed for TX */;
7366b4cac81SBjoern A. Zeeb #endif
7376b4cac81SBjoern A. Zeeb 	atomic64_set(&kc->tx_pn, k->wk_keytsc);
7386b4cac81SBjoern A. Zeeb 	kc->keylen = k->wk_keylen;
7396b4cac81SBjoern A. Zeeb 	memcpy(kc->key, k->wk_key, k->wk_keylen);
7406b4cac81SBjoern A. Zeeb 
7416b4cac81SBjoern A. Zeeb 	switch (kc->cipher) {
7426b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP:
7436b4cac81SBjoern A. Zeeb 		kc->iv_len = k->wk_cipher->ic_header;
7446b4cac81SBjoern A. Zeeb 		kc->icv_len = k->wk_cipher->ic_trailer;
7456b4cac81SBjoern A. Zeeb 		break;
7466b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_TKIP:
7476b4cac81SBjoern A. Zeeb 	default:
7486b4cac81SBjoern A. Zeeb 		IMPROVE();
7496b4cac81SBjoern A. Zeeb 		return (0);
7506b4cac81SBjoern A. Zeeb 	};
7516b4cac81SBjoern A. Zeeb 
7526b4cac81SBjoern A. Zeeb 	ni = vap->iv_bss;
7536b4cac81SBjoern A. Zeeb 	sta = ieee80211_find_sta(vif, ni->ni_bssid);
7546b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
7556b4cac81SBjoern A. Zeeb 		struct lkpi_sta *lsta;
7566b4cac81SBjoern A. Zeeb 
7576b4cac81SBjoern A. Zeeb 		lsta = STA_TO_LSTA(sta);
7586b4cac81SBjoern A. Zeeb 		lsta->kc = kc;
7596b4cac81SBjoern A. Zeeb 	}
7606b4cac81SBjoern A. Zeeb 
7616b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_set_key(hw, cmd, vif, sta, kc);
7626b4cac81SBjoern A. Zeeb 	if (error != 0) {
7636b4cac81SBjoern A. Zeeb 		/* XXX-BZ leaking kc currently */
7646b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: set_key failed: %d\n", __func__, error);
7656b4cac81SBjoern A. Zeeb 		return (0);
7666b4cac81SBjoern A. Zeeb 	} else {
7676b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: set_key succeeded: keyidx %u hw_key_idx %u "
7686b4cac81SBjoern A. Zeeb 		    "flags %#10x\n", __func__,
7696b4cac81SBjoern A. Zeeb 		    kc->keyidx, kc->hw_key_idx, kc->flags);
7706b4cac81SBjoern A. Zeeb 		return (1);
7716b4cac81SBjoern A. Zeeb 	}
7726b4cac81SBjoern A. Zeeb }
7736b4cac81SBjoern A. Zeeb 
7746b4cac81SBjoern A. Zeeb static int
7756b4cac81SBjoern A. Zeeb lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
7766b4cac81SBjoern A. Zeeb {
7776b4cac81SBjoern A. Zeeb 
7786b4cac81SBjoern A. Zeeb 	/* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
7796b4cac81SBjoern A. Zeeb 	return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY));
7806b4cac81SBjoern A. Zeeb }
7816b4cac81SBjoern A. Zeeb static  int
7826b4cac81SBjoern A. Zeeb lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
7836b4cac81SBjoern A. Zeeb {
7846b4cac81SBjoern A. Zeeb 
7856b4cac81SBjoern A. Zeeb 	return (_lkpi_iv_key_set_delete(vap, k, SET_KEY));
7866b4cac81SBjoern A. Zeeb }
7876b4cac81SBjoern A. Zeeb #endif
7886b4cac81SBjoern A. Zeeb 
7896b4cac81SBjoern A. Zeeb static u_int
7906b4cac81SBjoern A. Zeeb lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
7916b4cac81SBjoern A. Zeeb {
7926b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr_list *mc_list;
7936b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr *addr;
7946b4cac81SBjoern A. Zeeb 
7956b4cac81SBjoern A. Zeeb 	KASSERT(arg != NULL && sdl != NULL, ("%s: arg %p sdl %p cnt %u\n",
7966b4cac81SBjoern A. Zeeb 	    __func__, arg, sdl, cnt));
7976b4cac81SBjoern A. Zeeb 
7986b4cac81SBjoern A. Zeeb 	mc_list = arg;
7996b4cac81SBjoern A. Zeeb 	/* If it is on the list already skip it. */
8006b4cac81SBjoern A. Zeeb 	netdev_hw_addr_list_for_each(addr, mc_list) {
8016b4cac81SBjoern A. Zeeb 		if (!memcmp(addr->addr, LLADDR(sdl), sdl->sdl_alen))
8026b4cac81SBjoern A. Zeeb 			return (0);
8036b4cac81SBjoern A. Zeeb 	}
8046b4cac81SBjoern A. Zeeb 
8056b4cac81SBjoern A. Zeeb 	addr = malloc(sizeof(*addr), M_LKPI80211, M_NOWAIT | M_ZERO);
8066b4cac81SBjoern A. Zeeb 	if (addr == NULL)
8076b4cac81SBjoern A. Zeeb 		return (0);
8086b4cac81SBjoern A. Zeeb 
8096b4cac81SBjoern A. Zeeb 	INIT_LIST_HEAD(&addr->addr_list);
8106b4cac81SBjoern A. Zeeb 	memcpy(addr->addr, LLADDR(sdl), sdl->sdl_alen);
8116b4cac81SBjoern A. Zeeb 	/* XXX this should be a netdev function? */
8126b4cac81SBjoern A. Zeeb 	list_add(&addr->addr_list, &mc_list->addr_list);
8136b4cac81SBjoern A. Zeeb 	mc_list->count++;
8146b4cac81SBjoern A. Zeeb 
8159d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
8169d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
8176b4cac81SBjoern A. Zeeb 		printf("%s:%d: mc_list count %d: added %6D\n",
8186b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, mc_list->count, addr->addr, ":");
8199d9ba2b7SBjoern A. Zeeb #endif
8206b4cac81SBjoern A. Zeeb 
8216b4cac81SBjoern A. Zeeb 	return (1);
8226b4cac81SBjoern A. Zeeb }
8236b4cac81SBjoern A. Zeeb 
8246b4cac81SBjoern A. Zeeb static void
8256b4cac81SBjoern A. Zeeb lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
8266b4cac81SBjoern A. Zeeb {
8276b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
8286b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
8296b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr_list mc_list;
8306b4cac81SBjoern A. Zeeb 	struct list_head *le, *next;
8316b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr *addr;
8326b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
8336b4cac81SBjoern A. Zeeb 	u64 mc;
8346b4cac81SBjoern A. Zeeb 	unsigned int changed_flags, total_flags;
8356b4cac81SBjoern A. Zeeb 
8366b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
8376b4cac81SBjoern A. Zeeb 
8386b4cac81SBjoern A. Zeeb 	if (lhw->ops->prepare_multicast == NULL ||
8396b4cac81SBjoern A. Zeeb 	    lhw->ops->configure_filter == NULL)
8406b4cac81SBjoern A. Zeeb 		return;
8416b4cac81SBjoern A. Zeeb 
8426b4cac81SBjoern A. Zeeb 	if (!lhw->update_mc && !force)
8436b4cac81SBjoern A. Zeeb 		return;
8446b4cac81SBjoern A. Zeeb 
8456b4cac81SBjoern A. Zeeb 	changed_flags = total_flags = 0;
8466b4cac81SBjoern A. Zeeb 	mc_list.count = 0;
8476b4cac81SBjoern A. Zeeb 	INIT_LIST_HEAD(&mc_list.addr_list);
8486b4cac81SBjoern A. Zeeb 	if (ic->ic_allmulti == 0) {
8496b4cac81SBjoern A. Zeeb 		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
8506b4cac81SBjoern A. Zeeb 			if_foreach_llmaddr(vap->iv_ifp,
8516b4cac81SBjoern A. Zeeb 			    lkpi_ic_update_mcast_copy, &mc_list);
8526b4cac81SBjoern A. Zeeb 	} else {
8536b4cac81SBjoern A. Zeeb 		changed_flags |= FIF_ALLMULTI;
8546b4cac81SBjoern A. Zeeb 	}
8556b4cac81SBjoern A. Zeeb 
8566b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
8576b4cac81SBjoern A. Zeeb 	mc = lkpi_80211_mo_prepare_multicast(hw, &mc_list);
8586b4cac81SBjoern A. Zeeb 	/*
8596b4cac81SBjoern A. Zeeb 	 * XXX-BZ make sure to get this sorted what is a change,
8606b4cac81SBjoern A. Zeeb 	 * what gets all set; what was already set?
8616b4cac81SBjoern A. Zeeb 	 */
8626b4cac81SBjoern A. Zeeb 	total_flags = changed_flags;
8636b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_configure_filter(hw, changed_flags, &total_flags, mc);
8646b4cac81SBjoern A. Zeeb 
8659d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
8669d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
8676b4cac81SBjoern A. Zeeb 		printf("%s: changed_flags %#06x count %d total_flags %#010x\n",
8686b4cac81SBjoern A. Zeeb 		    __func__, changed_flags, mc_list.count, total_flags);
8699d9ba2b7SBjoern A. Zeeb #endif
8706b4cac81SBjoern A. Zeeb 
8716b4cac81SBjoern A. Zeeb 	if (mc_list.count != 0) {
8726b4cac81SBjoern A. Zeeb 		list_for_each_safe(le, next, &mc_list.addr_list) {
8736b4cac81SBjoern A. Zeeb 			addr = list_entry(le, struct netdev_hw_addr, addr_list);
8746b4cac81SBjoern A. Zeeb 			free(addr, M_LKPI80211);
8756b4cac81SBjoern A. Zeeb 			mc_list.count--;
8766b4cac81SBjoern A. Zeeb 		}
8776b4cac81SBjoern A. Zeeb 	}
8786b4cac81SBjoern A. Zeeb 	KASSERT(mc_list.count == 0, ("%s: mc_list %p count %d != 0\n",
8796b4cac81SBjoern A. Zeeb 	    __func__, &mc_list, mc_list.count));
8806b4cac81SBjoern A. Zeeb }
8816b4cac81SBjoern A. Zeeb 
882fa8f007dSBjoern A. Zeeb static enum ieee80211_bss_changed
883fa8f007dSBjoern A. Zeeb lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
884fa8f007dSBjoern A. Zeeb     struct ieee80211vap *vap, const char *_f, int _l)
885fa8f007dSBjoern A. Zeeb {
886fa8f007dSBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
887fa8f007dSBjoern A. Zeeb 
888fa8f007dSBjoern A. Zeeb 	bss_changed = 0;
889fa8f007dSBjoern A. Zeeb 
8909d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
8919d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
892fa8f007dSBjoern A. Zeeb 		printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
893fa8f007dSBjoern A. Zeeb 		    "dtim_period %u sync_dtim_count %u sync_tsf %ju "
894fa8f007dSBjoern A. Zeeb 		    "sync_device_ts %u bss_changed %#08x\n",
895fa8f007dSBjoern A. Zeeb 			__func__, __LINE__, _f, _l,
896616e1330SBjoern A. Zeeb 			vif->cfg.assoc, vif->cfg.aid,
897fa8f007dSBjoern A. Zeeb 			vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
898fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_dtim_count,
899fa8f007dSBjoern A. Zeeb 			(uintmax_t)vif->bss_conf.sync_tsf,
900fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_device_ts,
901fa8f007dSBjoern A. Zeeb 			bss_changed);
9029d9ba2b7SBjoern A. Zeeb #endif
903fa8f007dSBjoern A. Zeeb 
904fa8f007dSBjoern A. Zeeb 	if (vif->bss_conf.beacon_int != ni->ni_intval) {
905fa8f007dSBjoern A. Zeeb 		vif->bss_conf.beacon_int = ni->ni_intval;
906fa8f007dSBjoern A. Zeeb 		/* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */
907fa8f007dSBjoern A. Zeeb 		if (vif->bss_conf.beacon_int < 16)
908fa8f007dSBjoern A. Zeeb 			vif->bss_conf.beacon_int = 16;
909fa8f007dSBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_BEACON_INT;
910fa8f007dSBjoern A. Zeeb 	}
911fa8f007dSBjoern A. Zeeb 	if (vif->bss_conf.dtim_period != vap->iv_dtim_period &&
912fa8f007dSBjoern A. Zeeb 	    vap->iv_dtim_period > 0) {
913fa8f007dSBjoern A. Zeeb 		vif->bss_conf.dtim_period = vap->iv_dtim_period;
914fa8f007dSBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_BEACON_INFO;
915fa8f007dSBjoern A. Zeeb 	}
916fa8f007dSBjoern A. Zeeb 
917fa8f007dSBjoern A. Zeeb 	vif->bss_conf.sync_dtim_count = vap->iv_dtim_count;
918fa8f007dSBjoern A. Zeeb 	vif->bss_conf.sync_tsf = le64toh(ni->ni_tstamp.tsf);
919fa8f007dSBjoern A. Zeeb 	/* vif->bss_conf.sync_device_ts = set in linuxkpi_ieee80211_rx. */
920fa8f007dSBjoern A. Zeeb 
9219d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
9229d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
923fa8f007dSBjoern A. Zeeb 		printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
924fa8f007dSBjoern A. Zeeb 		    "dtim_period %u sync_dtim_count %u sync_tsf %ju "
925fa8f007dSBjoern A. Zeeb 		    "sync_device_ts %u bss_changed %#08x\n",
926fa8f007dSBjoern A. Zeeb 			__func__, __LINE__, _f, _l,
927616e1330SBjoern A. Zeeb 			vif->cfg.assoc, vif->cfg.aid,
928fa8f007dSBjoern A. Zeeb 			vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
929fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_dtim_count,
930fa8f007dSBjoern A. Zeeb 			(uintmax_t)vif->bss_conf.sync_tsf,
931fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_device_ts,
932fa8f007dSBjoern A. Zeeb 			bss_changed);
9339d9ba2b7SBjoern A. Zeeb #endif
934fa8f007dSBjoern A. Zeeb 
935fa8f007dSBjoern A. Zeeb 	return (bss_changed);
936fa8f007dSBjoern A. Zeeb }
937fa8f007dSBjoern A. Zeeb 
9386b4cac81SBjoern A. Zeeb static void
9396b4cac81SBjoern A. Zeeb lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
9406b4cac81SBjoern A. Zeeb {
9416b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
9426b4cac81SBjoern A. Zeeb 	int error;
9438ac540d3SBjoern A. Zeeb 	bool cancel;
9446b4cac81SBjoern A. Zeeb 
9458ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
9468ac540d3SBjoern A. Zeeb 	cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
9478ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
9488ac540d3SBjoern A. Zeeb 	if (!cancel)
9496b4cac81SBjoern A. Zeeb 		return;
9506b4cac81SBjoern A. Zeeb 
9516b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
9526b4cac81SBjoern A. Zeeb 
953bec76628SBjoern A. Zeeb 	IEEE80211_UNLOCK(lhw->ic);
954bec76628SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
9556b4cac81SBjoern A. Zeeb 	/* Need to cancel the scan. */
9566b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_cancel_hw_scan(hw, vif);
9578ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
9586b4cac81SBjoern A. Zeeb 
9596b4cac81SBjoern A. Zeeb 	/* Need to make sure we see ieee80211_scan_completed. */
9608ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
9618ac540d3SBjoern A. Zeeb 	if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0)
9628ac540d3SBjoern A. Zeeb 		error = msleep(lhw, &lhw->scan_mtx, 0, "lhwscanstop", hz/2);
9638ac540d3SBjoern A. Zeeb 	cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
9648ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
9658ac540d3SBjoern A. Zeeb 
966bec76628SBjoern A. Zeeb 	IEEE80211_LOCK(lhw->ic);
9676b4cac81SBjoern A. Zeeb 
9688ac540d3SBjoern A. Zeeb 	if (cancel)
9696b4cac81SBjoern A. Zeeb 		ic_printf(lhw->ic, "%s: failed to cancel scan: %d (%p, %p)\n",
9706b4cac81SBjoern A. Zeeb 		    __func__, error, lhw, vif);
9716b4cac81SBjoern A. Zeeb }
9726b4cac81SBjoern A. Zeeb 
9736b4cac81SBjoern A. Zeeb static void
974086be6a8SBjoern A. Zeeb lkpi_hw_conf_idle(struct ieee80211_hw *hw, bool new)
975086be6a8SBjoern A. Zeeb {
976086be6a8SBjoern A. Zeeb 	struct lkpi_hw *lhw;
977086be6a8SBjoern A. Zeeb 	int error;
978086be6a8SBjoern A. Zeeb 	bool old;
979086be6a8SBjoern A. Zeeb 
980086be6a8SBjoern A. Zeeb 	old = hw->conf.flags & IEEE80211_CONF_IDLE;
981086be6a8SBjoern A. Zeeb 	if (old == new)
982086be6a8SBjoern A. Zeeb 		return;
983086be6a8SBjoern A. Zeeb 
984086be6a8SBjoern A. Zeeb 	hw->conf.flags ^= IEEE80211_CONF_IDLE;
985086be6a8SBjoern A. Zeeb 	error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_IDLE);
986086be6a8SBjoern A. Zeeb 	if (error != 0 && error != EOPNOTSUPP) {
987086be6a8SBjoern A. Zeeb 		lhw = HW_TO_LHW(hw);
988086be6a8SBjoern A. Zeeb 		ic_printf(lhw->ic, "ERROR: %s: config %#0x returned %d\n",
989086be6a8SBjoern A. Zeeb 		    __func__, IEEE80211_CONF_CHANGE_IDLE, error);
990086be6a8SBjoern A. Zeeb 	}
991086be6a8SBjoern A. Zeeb }
992086be6a8SBjoern A. Zeeb 
993086be6a8SBjoern A. Zeeb static void
9946b4cac81SBjoern A. Zeeb lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
9956b4cac81SBjoern A. Zeeb     struct lkpi_hw *lhw)
9966b4cac81SBjoern A. Zeeb {
9976b4cac81SBjoern A. Zeeb 	sta->aid = 0;
998616e1330SBjoern A. Zeeb 	if (vif->cfg.assoc) {
9996b4cac81SBjoern A. Zeeb 		struct ieee80211_hw *hw;
10006b4cac81SBjoern A. Zeeb 		enum ieee80211_bss_changed changed;
10016b4cac81SBjoern A. Zeeb 
10026b4cac81SBjoern A. Zeeb 		lhw->update_mc = true;
10036b4cac81SBjoern A. Zeeb 		lkpi_update_mcast_filter(lhw->ic, true);
10046b4cac81SBjoern A. Zeeb 
10056b4cac81SBjoern A. Zeeb 		changed = 0;
1006616e1330SBjoern A. Zeeb 		vif->cfg.assoc = false;
1007616e1330SBjoern A. Zeeb 		vif->cfg.aid = 0;
10086b4cac81SBjoern A. Zeeb 		changed |= BSS_CHANGED_ASSOC;
1009d9f59799SBjoern A. Zeeb 		/*
1010d9f59799SBjoern A. Zeeb 		 * This will remove the sta from firmware for iwlwifi.
1011d9f59799SBjoern A. Zeeb 		 * So confusing that they use state and flags and ... ^%$%#%$^.
1012d9f59799SBjoern A. Zeeb 		 */
10136b4cac81SBjoern A. Zeeb 		IMPROVE();
10146b4cac81SBjoern A. Zeeb 		hw = LHW_TO_HW(lhw);
10156b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf,
10166b4cac81SBjoern A. Zeeb 		    changed);
1017086be6a8SBjoern A. Zeeb 
1018086be6a8SBjoern A. Zeeb 		lkpi_hw_conf_idle(hw, true);
10196b4cac81SBjoern A. Zeeb 	}
10206b4cac81SBjoern A. Zeeb }
10216b4cac81SBjoern A. Zeeb 
10226b4cac81SBjoern A. Zeeb static void
10236b4cac81SBjoern A. Zeeb lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
10246b4cac81SBjoern A. Zeeb     bool dequeue_seen, bool no_emptyq)
10256b4cac81SBjoern A. Zeeb {
10266b4cac81SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
10276b4cac81SBjoern A. Zeeb 	int tid;
1028eac3646fSBjoern A. Zeeb 	bool ltxq_empty;
10296b4cac81SBjoern A. Zeeb 
10306b4cac81SBjoern A. Zeeb 	/* Wake up all queues to know they are allocated in the driver. */
10316b4cac81SBjoern A. Zeeb 	for (tid = 0; tid < nitems(sta->txq); tid++) {
10326b4cac81SBjoern A. Zeeb 
10336b4cac81SBjoern A. Zeeb 		if (tid == IEEE80211_NUM_TIDS) {
10346b4cac81SBjoern A. Zeeb 			IMPROVE("station specific?");
10356b4cac81SBjoern A. Zeeb 			if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ))
10366b4cac81SBjoern A. Zeeb 				continue;
10376b4cac81SBjoern A. Zeeb 		} else if (tid >= hw->queues)
10386b4cac81SBjoern A. Zeeb 			continue;
10396b4cac81SBjoern A. Zeeb 
10406b4cac81SBjoern A. Zeeb 		if (sta->txq[tid] == NULL)
10416b4cac81SBjoern A. Zeeb 			continue;
10426b4cac81SBjoern A. Zeeb 
10436b4cac81SBjoern A. Zeeb 		ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
10446b4cac81SBjoern A. Zeeb 		if (dequeue_seen && !ltxq->seen_dequeue)
10456b4cac81SBjoern A. Zeeb 			continue;
10466b4cac81SBjoern A. Zeeb 
1047eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_LOCK(ltxq);
1048eac3646fSBjoern A. Zeeb 		ltxq_empty = skb_queue_empty(&ltxq->skbq);
1049eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_UNLOCK(ltxq);
1050eac3646fSBjoern A. Zeeb 		if (no_emptyq && ltxq_empty)
10516b4cac81SBjoern A. Zeeb 			continue;
10526b4cac81SBjoern A. Zeeb 
10536b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
10546b4cac81SBjoern A. Zeeb 	}
10556b4cac81SBjoern A. Zeeb }
10566b4cac81SBjoern A. Zeeb 
10576b4cac81SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
10586b4cac81SBjoern A. Zeeb 
10596b4cac81SBjoern A. Zeeb static int
10606b4cac81SBjoern A. Zeeb lkpi_sta_state_do_nada(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
10616b4cac81SBjoern A. Zeeb {
10626b4cac81SBjoern A. Zeeb 
10636b4cac81SBjoern A. Zeeb 	return (0);
10646b4cac81SBjoern A. Zeeb }
10656b4cac81SBjoern A. Zeeb 
10666b4cac81SBjoern A. Zeeb /* lkpi_iv_newstate() handles the stop scan case generally. */
10676b4cac81SBjoern A. Zeeb #define	lkpi_sta_scan_to_init(_v, _n, _a)	lkpi_sta_state_do_nada(_v, _n, _a)
10686b4cac81SBjoern A. Zeeb 
10696b4cac81SBjoern A. Zeeb static int
10706b4cac81SBjoern A. Zeeb lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
10716b4cac81SBjoern A. Zeeb {
10726b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
1073c5e25798SBjoern A. Zeeb 	struct lkpi_chanctx *lchanctx;
10746b4cac81SBjoern A. Zeeb 	struct ieee80211_chanctx_conf *conf;
10756b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
10766b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
10776b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
10786b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
10796b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
10806b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
10816b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
10826b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
10836b4cac81SBjoern A. Zeeb 	uint32_t changed;
10846b4cac81SBjoern A. Zeeb 	int error;
10856b4cac81SBjoern A. Zeeb 
10862ac8a218SBjoern A. Zeeb 	/*
10872ac8a218SBjoern A. Zeeb 	 * In here we use vap->iv_bss until lvif->lvif_bss is set.
10882ac8a218SBjoern A. Zeeb 	 * For all later (STATE >= AUTH) functions we need to use the lvif
10892ac8a218SBjoern A. Zeeb 	 * cache which will be tracked even through (*iv_update_bss)().
10902ac8a218SBjoern A. Zeeb 	 */
10912ac8a218SBjoern A. Zeeb 
10922ac8a218SBjoern A. Zeeb 	if (vap->iv_bss == NULL) {
10932ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: no iv_bss for vap %p\n", __func__, vap);
10942ac8a218SBjoern A. Zeeb 		return (EINVAL);
10952ac8a218SBjoern A. Zeeb 	}
10960936c648SBjoern A. Zeeb 	/*
10970936c648SBjoern A. Zeeb 	 * Keep the ni alive locally.  In theory (and practice) iv_bss can change
10980936c648SBjoern A. Zeeb 	 * once we unlock here.  This is due to net80211 allowing state changes
10990936c648SBjoern A. Zeeb 	 * and new join1() despite having an active node as well as due to
11000936c648SBjoern A. Zeeb 	 * the fact that the iv_bss can be swapped under the hood in (*iv_update_bss).
11010936c648SBjoern A. Zeeb 	 */
11022ac8a218SBjoern A. Zeeb 	ni = ieee80211_ref_node(vap->iv_bss);
11032ac8a218SBjoern A. Zeeb 	if (ni->ni_chan == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC) {
11042ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: no channel set for iv_bss ni %p "
11052ac8a218SBjoern A. Zeeb 		    "on vap %p\n", __func__, ni, vap);
11060936c648SBjoern A. Zeeb 		ieee80211_free_node(ni);	/* Error handling for the local ni. */
11072ac8a218SBjoern A. Zeeb 		return (EINVAL);
11086b4cac81SBjoern A. Zeeb 	}
11096b4cac81SBjoern A. Zeeb 
11106b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
11112ac8a218SBjoern A. Zeeb 	chan = lkpi_find_lkpi80211_chan(lhw, ni->ni_chan);
11122ac8a218SBjoern A. Zeeb 	if (chan == NULL) {
11132ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: failed to get LKPI channel from "
11142ac8a218SBjoern A. Zeeb 		    "iv_bss ni %p on vap %p\n", __func__, ni, vap);
11150936c648SBjoern A. Zeeb 		ieee80211_free_node(ni);	/* Error handling for the local ni. */
11162ac8a218SBjoern A. Zeeb 		return (ESRCH);
11172ac8a218SBjoern A. Zeeb 	}
11182ac8a218SBjoern A. Zeeb 
11196b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
11206b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
11216b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
11226b4cac81SBjoern A. Zeeb 
11230936c648SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
11240936c648SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
11250936c648SBjoern A. Zeeb 	if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL) {
11260936c648SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
11270936c648SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
11280936c648SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
11290936c648SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
11300936c648SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
11310936c648SBjoern A. Zeeb 		return (EBUSY);
11320936c648SBjoern A. Zeeb 	}
11330936c648SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
11340936c648SBjoern A. Zeeb 
11356b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
11368ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
11376b4cac81SBjoern A. Zeeb 
11386b4cac81SBjoern A. Zeeb 	/* Add chanctx (or if exists, change it). */
11396b4cac81SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
11406b4cac81SBjoern A. Zeeb 		conf = vif->chanctx_conf;
1141c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
11426b4cac81SBjoern A. Zeeb 		IMPROVE("diff changes for changed, working on live copy, rcu");
11436b4cac81SBjoern A. Zeeb 	} else {
11446b4cac81SBjoern A. Zeeb 		/* Keep separate alloc as in Linux this is rcu managed? */
1145c5e25798SBjoern A. Zeeb 		lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size,
11466b4cac81SBjoern A. Zeeb 		    M_LKPI80211, M_WAITOK | M_ZERO);
1147c5e25798SBjoern A. Zeeb 		conf = &lchanctx->conf;
11486b4cac81SBjoern A. Zeeb 	}
11496b4cac81SBjoern A. Zeeb 
11506b4cac81SBjoern A. Zeeb 	conf->rx_chains_dynamic = 1;
11516b4cac81SBjoern A. Zeeb 	conf->rx_chains_static = 1;
11526b4cac81SBjoern A. Zeeb 	conf->radar_enabled =
11536b4cac81SBjoern A. Zeeb 	    (chan->flags & IEEE80211_CHAN_RADAR) ? true : false;
11546b4cac81SBjoern A. Zeeb 	conf->def.chan = chan;
11556b4cac81SBjoern A. Zeeb 	conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
11566b4cac81SBjoern A. Zeeb 	conf->def.center_freq1 = chan->center_freq;
11576b4cac81SBjoern A. Zeeb 	conf->def.center_freq2 = 0;
11589fb91463SBjoern A. Zeeb 	IMPROVE("Check vht_cap from band not just chan?");
11592ac8a218SBjoern A. Zeeb 	KASSERT(ni->ni_chan != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC,
11602ac8a218SBjoern A. Zeeb 	   ("%s:%d: ni %p ni_chan %p\n", __func__, __LINE__, ni, ni->ni_chan));
11619fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
11629fb91463SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
11639fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
11649fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_40;
11659fb91463SBjoern A. Zeeb 		} else
11669fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_20;
11679fb91463SBjoern A. Zeeb 	}
11689fb91463SBjoern A. Zeeb #endif
11699fb91463SBjoern A. Zeeb #ifdef LKPI_80211_VHT
11709fb91463SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
11719fb91463SBjoern A. Zeeb #ifdef __notyet__
11729fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
11739fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_80P80;
11749fb91463SBjoern A. Zeeb 			conf->def.center_freq2 = 0;	/* XXX */
11759fb91463SBjoern A. Zeeb 		} else
11769fb91463SBjoern A. Zeeb #endif
11779fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
11789fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_160;
11799fb91463SBjoern A. Zeeb 		else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
11809fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_80;
11819fb91463SBjoern A. Zeeb 	}
11829fb91463SBjoern A. Zeeb #endif
11836b4cac81SBjoern A. Zeeb 	/* Responder ... */
11846b4cac81SBjoern A. Zeeb 	conf->min_def.chan = chan;
11856b4cac81SBjoern A. Zeeb 	conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT;
11866b4cac81SBjoern A. Zeeb 	conf->min_def.center_freq1 = chan->center_freq;
11876b4cac81SBjoern A. Zeeb 	conf->min_def.center_freq2 = 0;
11889fb91463SBjoern A. Zeeb 	IMPROVE("currently 20_NOHT min_def only");
11896b4cac81SBjoern A. Zeeb 
11906ffb7bd4SBjoern A. Zeeb 	/* Set bss info (bss_info_changed). */
11916ffb7bd4SBjoern A. Zeeb 	bss_changed = 0;
11926ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.bssid = ni->ni_bssid;
11936ffb7bd4SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_BSSID;
11946ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.txpower = ni->ni_txpower;
11956ffb7bd4SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_TXPOWER;
11966ffb7bd4SBjoern A. Zeeb 	vif->cfg.idle = false;
11976ffb7bd4SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_IDLE;
11986ffb7bd4SBjoern A. Zeeb 
11996ffb7bd4SBjoern A. Zeeb 	/* vif->bss_conf.basic_rates ? Where exactly? */
12006ffb7bd4SBjoern A. Zeeb 
12016ffb7bd4SBjoern A. Zeeb 	/* Should almost assert it is this. */
12026ffb7bd4SBjoern A. Zeeb 	vif->cfg.assoc = false;
12036ffb7bd4SBjoern A. Zeeb 	vif->cfg.aid = 0;
12046ffb7bd4SBjoern A. Zeeb 
12056ffb7bd4SBjoern A. Zeeb 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
12066ffb7bd4SBjoern A. Zeeb 
12076b4cac81SBjoern A. Zeeb 	error = 0;
12086b4cac81SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
12096b4cac81SBjoern A. Zeeb 		changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
12106b4cac81SBjoern A. Zeeb 		changed |= IEEE80211_CHANCTX_CHANGE_RADAR;
12116b4cac81SBjoern A. Zeeb 		changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS;
12126b4cac81SBjoern A. Zeeb 		changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
12136b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_change_chanctx(hw, conf, changed);
12146b4cac81SBjoern A. Zeeb 	} else {
12156b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_add_chanctx(hw, conf);
12166b4cac81SBjoern A. Zeeb 		if (error == 0 || error == EOPNOTSUPP) {
12176b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.chan = conf->def.chan;
12186b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.width = conf->def.width;
12196b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.center_freq1 =
12206b4cac81SBjoern A. Zeeb 			    conf->def.center_freq1;
12219fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
12229fb91463SBjoern A. Zeeb 			if (vif->bss_conf.chandef.width == NL80211_CHAN_WIDTH_40) {
12239fb91463SBjoern A. Zeeb 				/* Note: it is 10 not 20. */
12249fb91463SBjoern A. Zeeb 				if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
12259fb91463SBjoern A. Zeeb 					vif->bss_conf.chandef.center_freq1 += 10;
12269fb91463SBjoern A. Zeeb 				else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
12279fb91463SBjoern A. Zeeb 					vif->bss_conf.chandef.center_freq1 -= 10;
12289fb91463SBjoern A. Zeeb 			}
12299fb91463SBjoern A. Zeeb #endif
12306b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.center_freq2 =
12316b4cac81SBjoern A. Zeeb 			    conf->def.center_freq2;
12326b4cac81SBjoern A. Zeeb 		} else {
1233018d93ecSBjoern A. Zeeb 			ic_printf(vap->iv_ic, "%s:%d: mo_add_chanctx "
1234018d93ecSBjoern A. Zeeb 			    "failed: %d\n", __func__, __LINE__, error);
12356b4cac81SBjoern A. Zeeb 			goto out;
12366b4cac81SBjoern A. Zeeb 		}
12376ffb7bd4SBjoern A. Zeeb 
12386ffb7bd4SBjoern A. Zeeb 		vif->bss_conf.chanctx_conf = conf;
12396ffb7bd4SBjoern A. Zeeb 
12406b4cac81SBjoern A. Zeeb 		/* Assign vif chanctx. */
12416b4cac81SBjoern A. Zeeb 		if (error == 0)
124268541546SBjoern A. Zeeb 			error = lkpi_80211_mo_assign_vif_chanctx(hw, vif,
124368541546SBjoern A. Zeeb 			    &vif->bss_conf, conf);
12446b4cac81SBjoern A. Zeeb 		if (error == EOPNOTSUPP)
12456b4cac81SBjoern A. Zeeb 			error = 0;
12466b4cac81SBjoern A. Zeeb 		if (error != 0) {
1247018d93ecSBjoern A. Zeeb 			ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx "
1248018d93ecSBjoern A. Zeeb 			    "failed: %d\n", __func__, __LINE__, error);
12496b4cac81SBjoern A. Zeeb 			lkpi_80211_mo_remove_chanctx(hw, conf);
1250c5e25798SBjoern A. Zeeb 			lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
1251c5e25798SBjoern A. Zeeb 			free(lchanctx, M_LKPI80211);
12526b4cac81SBjoern A. Zeeb 			goto out;
12536b4cac81SBjoern A. Zeeb 		}
12546b4cac81SBjoern A. Zeeb 	}
12556b4cac81SBjoern A. Zeeb 	IMPROVE("update radiotap chan fields too");
12566b4cac81SBjoern A. Zeeb 
12576b4cac81SBjoern A. Zeeb 	/* RATES */
12586b4cac81SBjoern A. Zeeb 	IMPROVE("bss info: not all needs to come now and rates are missing");
12596b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
12606b4cac81SBjoern A. Zeeb 
1261d9f59799SBjoern A. Zeeb 	/*
12620936c648SBjoern A. Zeeb 	 * Given ni and lsta are 1:1 from alloc to free we can assert that
12630936c648SBjoern A. Zeeb 	 * ni always has lsta data attach despite net80211 node swapping
12640936c648SBjoern A. Zeeb 	 * under the hoods.
1265d9f59799SBjoern A. Zeeb 	 */
12660936c648SBjoern A. Zeeb 	KASSERT(ni->ni_drv_data != NULL, ("%s: ni %p ni_drv_data %p\n",
12670936c648SBjoern A. Zeeb 	    __func__, ni, ni->ni_drv_data));
12686b4cac81SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
1269e24e8103SBjoern A. Zeeb 
1270e24e8103SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
12710936c648SBjoern A. Zeeb 	/* Re-check given (*iv_update_bss) could have happened. */
12720936c648SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? or deal as error? */
12732ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL)
12742ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
12752ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d, ni %p lsta %p\n", __func__, __LINE__,
12762ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
12772ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
12782ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched, ni, lsta);
12792ac8a218SBjoern A. Zeeb 
12800936c648SBjoern A. Zeeb 	/*
12810936c648SBjoern A. Zeeb 	 * Reference the ni for this cache of lsta/ni on lvif->lvif_bss
12820936c648SBjoern A. Zeeb 	 * essentially out lsta version of the iv_bss.
12830936c648SBjoern A. Zeeb 	 * Do NOT use iv_bss here anymore as that may have diverged from our
12840936c648SBjoern A. Zeeb 	 * function local ni already and would lead to inconsistencies.
12850936c648SBjoern A. Zeeb 	 */
12860936c648SBjoern A. Zeeb 	ieee80211_ref_node(ni);
12872ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = lsta;
12882ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = true;
12892ac8a218SBjoern A. Zeeb 
12902ac8a218SBjoern A. Zeeb 	/* Insert the [l]sta into the list of known stations. */
1291e24e8103SBjoern A. Zeeb 	TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry);
1292e24e8103SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1293e24e8103SBjoern A. Zeeb 
1294d9f59799SBjoern A. Zeeb 	/* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */
12956b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
12966b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NOTEXIST, ("%s: lsta %p state not "
12976b4cac81SBjoern A. Zeeb 	    "NOTEXIST: %#x\n", __func__, lsta, lsta->state));
1298e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
12996b4cac81SBjoern A. Zeeb 	if (error != 0) {
13006b4cac81SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
1301018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
1302018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
13036b4cac81SBjoern A. Zeeb 		goto out;
13046b4cac81SBjoern A. Zeeb 	}
13056b4cac81SBjoern A. Zeeb #if 0
13066b4cac81SBjoern A. Zeeb 	lsta->added_to_drv = true;	/* mo manages. */
13076b4cac81SBjoern A. Zeeb #endif
13086b4cac81SBjoern A. Zeeb 
13099d9ba2b7SBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
13109d9ba2b7SBjoern A. Zeeb 
13116b4cac81SBjoern A. Zeeb 	/*
13126b4cac81SBjoern A. Zeeb 	 * Wakeup all queues now that sta is there so we have as much time to
13136b4cac81SBjoern A. Zeeb 	 * possibly prepare the queue in the driver to be ready for the 1st
13146b4cac81SBjoern A. Zeeb 	 * packet;  lkpi_80211_txq_tx_one() still has a workaround as there
13156b4cac81SBjoern A. Zeeb 	 * is no guarantee or way to check.
1316d9f59799SBjoern A. Zeeb 	 * XXX-BZ and by now we know that this does not work on all drivers
1317d9f59799SBjoern A. Zeeb 	 * for all queues.
13186b4cac81SBjoern A. Zeeb 	 */
1319e7fe0373SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, false);
13206b4cac81SBjoern A. Zeeb 
13216b4cac81SBjoern A. Zeeb 	/* Start mgd_prepare_tx. */
13226b4cac81SBjoern A. Zeeb 	memset(&prep_tx_info, 0, sizeof(prep_tx_info));
13236b4cac81SBjoern A. Zeeb 	prep_tx_info.duration = PREP_TX_INFO_DURATION;
13246b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
13256b4cac81SBjoern A. Zeeb 	lsta->in_mgd = true;
13266b4cac81SBjoern A. Zeeb 
13276b4cac81SBjoern A. Zeeb 	/*
13286b4cac81SBjoern A. Zeeb 	 * What is going to happen next:
13296b4cac81SBjoern A. Zeeb 	 * - <twiddle> .. we should end up in "auth_to_assoc"
13306b4cac81SBjoern A. Zeeb 	 * - event_callback
13316b4cac81SBjoern A. Zeeb 	 * - update sta_state (NONE to AUTH)
13326b4cac81SBjoern A. Zeeb 	 * - mgd_complete_tx
13336b4cac81SBjoern A. Zeeb 	 * (ideally we'd do that on a callback for something else ...)
13346b4cac81SBjoern A. Zeeb 	 */
13356b4cac81SBjoern A. Zeeb 
13366b4cac81SBjoern A. Zeeb out:
13378ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
13386b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
13390936c648SBjoern A. Zeeb 	/*
13400936c648SBjoern A. Zeeb 	 * Release the reference that keop the ni stable locally
13410936c648SBjoern A. Zeeb 	 * during the work of this function.
13420936c648SBjoern A. Zeeb 	 */
13436b4cac81SBjoern A. Zeeb 	if (ni != NULL)
13446b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
13456b4cac81SBjoern A. Zeeb 	return (error);
13466b4cac81SBjoern A. Zeeb }
13476b4cac81SBjoern A. Zeeb 
13486b4cac81SBjoern A. Zeeb static int
13496b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
13506b4cac81SBjoern A. Zeeb {
13516b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
13526b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
13536b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
13546b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
13556b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
13566b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
13576b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
13586b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
13596b4cac81SBjoern A. Zeeb 	int error;
13606b4cac81SBjoern A. Zeeb 
13616b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
13626b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
13636b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
13646b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
13656b4cac81SBjoern A. Zeeb 
13662ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
13672ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
13682ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
13692ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
13702ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
13712ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
13722ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
13732ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
13742ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
13752ac8a218SBjoern A. Zeeb #endif
13762ac8a218SBjoern A. Zeeb 
13772ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
13782ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
13792ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
13802ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
13812ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
13822ac8a218SBjoern A. Zeeb 	ni = lsta->ni;			/* Reference held for lvif_bss. */
13836b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
13846b4cac81SBjoern A. Zeeb 
138567674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
13866b4cac81SBjoern A. Zeeb 
13876b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
13888ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
13896b4cac81SBjoern A. Zeeb 
1390d9f59799SBjoern A. Zeeb 	/* flush, drop. */
1391d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
1392d9f59799SBjoern A. Zeeb 
13936b4cac81SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
13946b4cac81SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
13956b4cac81SBjoern A. Zeeb 
13966b4cac81SBjoern A. Zeeb 	/* flush, no drop */
13976b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
13986b4cac81SBjoern A. Zeeb 
13996b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
14006b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
14016b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
14026b4cac81SBjoern A. Zeeb 		prep_tx_info.success = false;
14036b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
14046b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
14056b4cac81SBjoern A. Zeeb 	}
14066b4cac81SBjoern A. Zeeb 
14076b4cac81SBjoern A. Zeeb 	/* sync_rx_queues */
14086b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
14096b4cac81SBjoern A. Zeeb 
14106b4cac81SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
14116b4cac81SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
1412d9f59799SBjoern A. Zeeb 
1413d9f59799SBjoern A. Zeeb 	/* Take the station down. */
14146b4cac81SBjoern A. Zeeb 
14156b4cac81SBjoern A. Zeeb 	/* Adjust sta and change state (from NONE) to NOTEXIST. */
14166b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
14176b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
1418d9f59799SBjoern A. Zeeb 	    "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
1419e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
14206b4cac81SBjoern A. Zeeb 	if (error != 0) {
14216b4cac81SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
1422018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
1423018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
14246b4cac81SBjoern A. Zeeb 		goto out;
14256b4cac81SBjoern A. Zeeb 	}
14266b4cac81SBjoern A. Zeeb #if 0
14276b4cac81SBjoern A. Zeeb 	lsta->added_to_drv = false;	/* mo manages. */
14286b4cac81SBjoern A. Zeeb #endif
14296b4cac81SBjoern A. Zeeb 
143067674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
14316b4cac81SBjoern A. Zeeb 
14322ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
14332ac8a218SBjoern A. Zeeb 	/* Remove ni reference for this cache of lsta. */
14342ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
14352ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
14362ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1437d9f59799SBjoern A. Zeeb 	lkpi_lsta_remove(lsta, lvif);
14380936c648SBjoern A. Zeeb 	/*
14390936c648SBjoern A. Zeeb 	 * The very last release the reference on the ni for the ni/lsta on
14400936c648SBjoern A. Zeeb 	 * lvif->lvif_bss.  Upon return from this both ni and lsta are invalid
14410936c648SBjoern A. Zeeb 	 * and potentially freed.
14420936c648SBjoern A. Zeeb 	 */
14430936c648SBjoern A. Zeeb 	ieee80211_free_node(ni);
1444d9f59799SBjoern A. Zeeb 
1445d9f59799SBjoern A. Zeeb 	/* conf_tx */
1446d9f59799SBjoern A. Zeeb 
1447d9f59799SBjoern A. Zeeb 	/* Take the chan ctx down. */
14486b4cac81SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
1449c5e25798SBjoern A. Zeeb 		struct lkpi_chanctx *lchanctx;
14506b4cac81SBjoern A. Zeeb 		struct ieee80211_chanctx_conf *conf;
14516b4cac81SBjoern A. Zeeb 
14526b4cac81SBjoern A. Zeeb 		conf = vif->chanctx_conf;
14536b4cac81SBjoern A. Zeeb 		/* Remove vif context. */
145468541546SBjoern A. Zeeb 		lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
14556b4cac81SBjoern A. Zeeb 		/* NB: vif->chanctx_conf is NULL now. */
14566b4cac81SBjoern A. Zeeb 
14576b4cac81SBjoern A. Zeeb 		/* Remove chan ctx. */
14586b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_remove_chanctx(hw, conf);
1459c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
1460c5e25798SBjoern A. Zeeb 		free(lchanctx, M_LKPI80211);
14616b4cac81SBjoern A. Zeeb 	}
14626b4cac81SBjoern A. Zeeb 
14636b4cac81SBjoern A. Zeeb out:
14648ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
14656b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
14666b4cac81SBjoern A. Zeeb 	return (error);
14676b4cac81SBjoern A. Zeeb }
14686b4cac81SBjoern A. Zeeb 
14696b4cac81SBjoern A. Zeeb static int
14706b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
14716b4cac81SBjoern A. Zeeb {
14726b4cac81SBjoern A. Zeeb 	int error;
14736b4cac81SBjoern A. Zeeb 
14746b4cac81SBjoern A. Zeeb 	error = lkpi_sta_auth_to_scan(vap, nstate, arg);
14756b4cac81SBjoern A. Zeeb 	if (error == 0)
14766b4cac81SBjoern A. Zeeb 		error = lkpi_sta_scan_to_init(vap, nstate, arg);
14776b4cac81SBjoern A. Zeeb 	return (error);
14786b4cac81SBjoern A. Zeeb }
14796b4cac81SBjoern A. Zeeb 
14806b4cac81SBjoern A. Zeeb static int
14816b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
14826b4cac81SBjoern A. Zeeb {
14836b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
14846b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
14856b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
14866b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
14876b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
14886b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
14896b4cac81SBjoern A. Zeeb 	int error;
14906b4cac81SBjoern A. Zeeb 
14916b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
14926b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
14936b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
14946b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
14956b4cac81SBjoern A. Zeeb 
14966b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
14978ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
14982ac8a218SBjoern A. Zeeb 
14992ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
15002ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
15012ac8a218SBjoern A. Zeeb 	if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
15022ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
15032ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
15042ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
15052ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
15062ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
15072ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
15082ac8a218SBjoern A. Zeeb #endif
15092ac8a218SBjoern A. Zeeb 		error = ENOTRECOVERABLE;
15102ac8a218SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
15112ac8a218SBjoern A. Zeeb 		goto out;
15122ac8a218SBjoern A. Zeeb 	}
15132ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
15142ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
15152ac8a218SBjoern A. Zeeb 
15162ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: lsta %p\n", __func__, lsta));
15176b4cac81SBjoern A. Zeeb 
15186b4cac81SBjoern A. Zeeb 	/* Finish auth. */
15196b4cac81SBjoern A. Zeeb 	IMPROVE("event callback");
15206b4cac81SBjoern A. Zeeb 
15216b4cac81SBjoern A. Zeeb 	/* Update sta_state (NONE to AUTH). */
15226b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
15236b4cac81SBjoern A. Zeeb 	    "NONE: %#x\n", __func__, lsta, lsta->state));
1524e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
1525018d93ecSBjoern A. Zeeb 	if (error != 0) {
1526018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
1527018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
15286b4cac81SBjoern A. Zeeb 		goto out;
1529018d93ecSBjoern A. Zeeb 	}
15306b4cac81SBjoern A. Zeeb 
15316b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
15326b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
15336b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
15346b4cac81SBjoern A. Zeeb 		prep_tx_info.success = true;
15356b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
15366b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
15376b4cac81SBjoern A. Zeeb 	}
15386b4cac81SBjoern A. Zeeb 
15396b4cac81SBjoern A. Zeeb 	/* Now start assoc. */
15406b4cac81SBjoern A. Zeeb 
15416b4cac81SBjoern A. Zeeb 	/* Start mgd_prepare_tx. */
15426b4cac81SBjoern A. Zeeb 	if (!lsta->in_mgd) {
15436b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
15446b4cac81SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
15456b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
15466b4cac81SBjoern A. Zeeb 		lsta->in_mgd = true;
15476b4cac81SBjoern A. Zeeb 	}
15486b4cac81SBjoern A. Zeeb 
15496b4cac81SBjoern A. Zeeb 	/* Wake tx queue to get packet out. */
1550e7fe0373SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), true, true);
15516b4cac81SBjoern A. Zeeb 
15526b4cac81SBjoern A. Zeeb 	/*
15536b4cac81SBjoern A. Zeeb 	 * <twiddle> .. we end up in "assoc_to_run"
15546b4cac81SBjoern A. Zeeb 	 * - update sta_state (AUTH to ASSOC)
15556b4cac81SBjoern A. Zeeb 	 * - conf_tx [all]
15566b4cac81SBjoern A. Zeeb 	 * - bss_info_changed (assoc, aid, ssid, ..)
15576b4cac81SBjoern A. Zeeb 	 * - change_chanctx (if needed)
15586b4cac81SBjoern A. Zeeb 	 * - event_callback
15596b4cac81SBjoern A. Zeeb 	 * - mgd_complete_tx
15606b4cac81SBjoern A. Zeeb 	 */
15616b4cac81SBjoern A. Zeeb 
15626b4cac81SBjoern A. Zeeb out:
15638ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
15646b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
15656b4cac81SBjoern A. Zeeb 	return (error);
15666b4cac81SBjoern A. Zeeb }
15676b4cac81SBjoern A. Zeeb 
15686b4cac81SBjoern A. Zeeb /* auth_to_auth, assoc_to_assoc. */
15696b4cac81SBjoern A. Zeeb static int
15706b4cac81SBjoern A. Zeeb lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
15716b4cac81SBjoern A. Zeeb {
15726b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
15736b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
15746b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
15756b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
15766b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
15776b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
15782ac8a218SBjoern A. Zeeb 	int error;
15796b4cac81SBjoern A. Zeeb 
15806b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
15816b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
15826b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
15836b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
15846b4cac81SBjoern A. Zeeb 
15856b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
15868ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
15872ac8a218SBjoern A. Zeeb 
15882ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
15892ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
15902ac8a218SBjoern A. Zeeb 	if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
15912ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
15922ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
15932ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
15942ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
15952ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
15962ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
15972ac8a218SBjoern A. Zeeb #endif
15982ac8a218SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
15992ac8a218SBjoern A. Zeeb 		error = ENOTRECOVERABLE;
16002ac8a218SBjoern A. Zeeb 		goto out;
16012ac8a218SBjoern A. Zeeb 	}
16022ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
16032ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
16042ac8a218SBjoern A. Zeeb 
16052ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
16062ac8a218SBjoern A. Zeeb 	    lsta, lvif, vap));
16076b4cac81SBjoern A. Zeeb 
16086b4cac81SBjoern A. Zeeb 	IMPROVE("event callback?");
16096b4cac81SBjoern A. Zeeb 
16106b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
16116b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
16126b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
16136b4cac81SBjoern A. Zeeb 		prep_tx_info.success = false;
16146b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
16156b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
16166b4cac81SBjoern A. Zeeb 	}
16176b4cac81SBjoern A. Zeeb 
16186b4cac81SBjoern A. Zeeb 	/* Now start assoc. */
16196b4cac81SBjoern A. Zeeb 
16206b4cac81SBjoern A. Zeeb 	/* Start mgd_prepare_tx. */
16216b4cac81SBjoern A. Zeeb 	if (!lsta->in_mgd) {
16226b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
16236b4cac81SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
16246b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
16256b4cac81SBjoern A. Zeeb 		lsta->in_mgd = true;
16266b4cac81SBjoern A. Zeeb 	}
16276b4cac81SBjoern A. Zeeb 
16282ac8a218SBjoern A. Zeeb 	error = 0;
16292ac8a218SBjoern A. Zeeb out:
16308ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
16316b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
16326b4cac81SBjoern A. Zeeb 
16332ac8a218SBjoern A. Zeeb 	return (error);
16346b4cac81SBjoern A. Zeeb }
16356b4cac81SBjoern A. Zeeb 
16366b4cac81SBjoern A. Zeeb static int
1637d9f59799SBjoern A. Zeeb _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
16386b4cac81SBjoern A. Zeeb {
16396b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
16406b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
16416b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
16426b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
16436b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
16446b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
16456b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
16466b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
1647d9f59799SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
16486b4cac81SBjoern A. Zeeb 	int error;
16496b4cac81SBjoern A. Zeeb 
16506b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
16516b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
16526b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
16536b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
16546b4cac81SBjoern A. Zeeb 
16552ac8a218SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
16562ac8a218SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
16572ac8a218SBjoern A. Zeeb 
16582ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
16592ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
16602ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
16612ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
16622ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
16632ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
16642ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
16652ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
16662ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
16672ac8a218SBjoern A. Zeeb #endif
16682ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
16692ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
16702ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
16712ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
16722ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
16732ac8a218SBjoern A. Zeeb 
16742ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
16756b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
16766b4cac81SBjoern A. Zeeb 
167767674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
1678d9f59799SBjoern A. Zeeb 
1679d9f59799SBjoern A. Zeeb 	/* flush, drop. */
1680d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
1681d9f59799SBjoern A. Zeeb 
1682d9f59799SBjoern A. Zeeb 	IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
1683d9f59799SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
1684d9f59799SBjoern A. Zeeb 	    !lsta->in_mgd) {
1685d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
1686d9f59799SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
1687d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
1688d9f59799SBjoern A. Zeeb 		lsta->in_mgd = true;
1689d9f59799SBjoern A. Zeeb 	}
1690d9f59799SBjoern A. Zeeb 
16918ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
1692d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
1693d9f59799SBjoern A. Zeeb 
1694d9f59799SBjoern A. Zeeb 	/* Call iv_newstate first so we get potential DISASSOC packet out. */
1695d9f59799SBjoern A. Zeeb 	error = lvif->iv_newstate(vap, nstate, arg);
1696018d93ecSBjoern A. Zeeb 	if (error != 0) {
1697018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
1698018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
1699d9f59799SBjoern A. Zeeb 		goto outni;
1700018d93ecSBjoern A. Zeeb 	}
1701d9f59799SBjoern A. Zeeb 
1702d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
17038ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
1704d9f59799SBjoern A. Zeeb 
170567674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
1706d9f59799SBjoern A. Zeeb 
1707d9f59799SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
1708d9f59799SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
1709d9f59799SBjoern A. Zeeb 
1710d9f59799SBjoern A. Zeeb 	/* flush, no drop */
1711d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
1712d9f59799SBjoern A. Zeeb 
17136b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
17146b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
17156b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
17166b4cac81SBjoern A. Zeeb 		prep_tx_info.success = false;
17176b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
17186b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
17196b4cac81SBjoern A. Zeeb 	}
17206b4cac81SBjoern A. Zeeb 
1721d9f59799SBjoern A. Zeeb 	/* sync_rx_queues */
1722d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
1723d9f59799SBjoern A. Zeeb 
1724d9f59799SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
1725d9f59799SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
1726d9f59799SBjoern A. Zeeb 
1727d9f59799SBjoern A. Zeeb 	/* Take the station down. */
1728d9f59799SBjoern A. Zeeb 
17296b4cac81SBjoern A. Zeeb 	/* Update sta and change state (from AUTH) to NONE. */
17306b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
17316b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
17326b4cac81SBjoern A. Zeeb 	    "AUTH: %#x\n", __func__, lsta, lsta->state));
1733e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
1734018d93ecSBjoern A. Zeeb 	if (error != 0) {
1735018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
1736018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
17376b4cac81SBjoern A. Zeeb 		goto out;
1738018d93ecSBjoern A. Zeeb 	}
17396b4cac81SBjoern A. Zeeb 
174067674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
17416b4cac81SBjoern A. Zeeb 
174216e688b2SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
174316e688b2SBjoern A. Zeeb 	/*
174416e688b2SBjoern A. Zeeb 	 * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST
174516e688b2SBjoern A. Zeeb 	 * as otherwise drivers (iwlwifi at least) will silently not remove
174616e688b2SBjoern A. Zeeb 	 * the sta from the firmware and when we will add a new one trigger
174716e688b2SBjoern A. Zeeb 	 * a fw assert.
174816e688b2SBjoern A. Zeeb 	 */
174916e688b2SBjoern A. Zeeb 	lkpi_disassoc(sta, vif, lhw);
175016e688b2SBjoern A. Zeeb 
1751d9f59799SBjoern A. Zeeb 	/* Adjust sta and change state (from NONE) to NOTEXIST. */
1752d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
1753d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
1754d9f59799SBjoern A. Zeeb 	    "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
1755e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
1756d9f59799SBjoern A. Zeeb 	if (error != 0) {
1757d9f59799SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
1758018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
1759018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
1760d9f59799SBjoern A. Zeeb 		goto out;
1761d9f59799SBjoern A. Zeeb 	}
1762d9f59799SBjoern A. Zeeb 
176316e688b2SBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);	/* sta no longer save to use. */
1764d9f59799SBjoern A. Zeeb 
1765d9f59799SBjoern A. Zeeb 	IMPROVE("Any bss_info changes to announce?");
1766d9f59799SBjoern A. Zeeb 	bss_changed = 0;
1767d9f59799SBjoern A. Zeeb 	vif->bss_conf.qos = 0;
1768d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_QOS;
1769616e1330SBjoern A. Zeeb 	vif->cfg.ssid_len = 0;
1770616e1330SBjoern A. Zeeb 	memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
1771d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_BSSID;
1772d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
1773d9f59799SBjoern A. Zeeb 
17742ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
17752ac8a218SBjoern A. Zeeb 	/* Remove ni reference for this cache of lsta. */
17762ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
17772ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
17782ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1779d9f59799SBjoern A. Zeeb 	lkpi_lsta_remove(lsta, lvif);
17800936c648SBjoern A. Zeeb 	/*
17810936c648SBjoern A. Zeeb 	 * The very last release the reference on the ni for the ni/lsta on
17820936c648SBjoern A. Zeeb 	 * lvif->lvif_bss.  Upon return from this both ni and lsta are invalid
17830936c648SBjoern A. Zeeb 	 * and potentially freed.
17840936c648SBjoern A. Zeeb 	 */
17850936c648SBjoern A. Zeeb 	ieee80211_free_node(ni);
1786d9f59799SBjoern A. Zeeb 
1787d9f59799SBjoern A. Zeeb 	/* conf_tx */
1788d9f59799SBjoern A. Zeeb 
1789d9f59799SBjoern A. Zeeb 	/* Take the chan ctx down. */
1790d9f59799SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
1791c5e25798SBjoern A. Zeeb 		struct lkpi_chanctx *lchanctx;
1792d9f59799SBjoern A. Zeeb 		struct ieee80211_chanctx_conf *conf;
1793d9f59799SBjoern A. Zeeb 
1794d9f59799SBjoern A. Zeeb 		conf = vif->chanctx_conf;
1795d9f59799SBjoern A. Zeeb 		/* Remove vif context. */
179668541546SBjoern A. Zeeb 		lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
1797d9f59799SBjoern A. Zeeb 		/* NB: vif->chanctx_conf is NULL now. */
1798d9f59799SBjoern A. Zeeb 
1799d9f59799SBjoern A. Zeeb 		/* Remove chan ctx. */
1800d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_remove_chanctx(hw, conf);
1801c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
1802c5e25798SBjoern A. Zeeb 		free(lchanctx, M_LKPI80211);
1803d9f59799SBjoern A. Zeeb 	}
1804d9f59799SBjoern A. Zeeb 
1805d9f59799SBjoern A. Zeeb 	error = EALREADY;
18066b4cac81SBjoern A. Zeeb out:
18078ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
18086b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
1809d9f59799SBjoern A. Zeeb outni:
18106b4cac81SBjoern A. Zeeb 	return (error);
18116b4cac81SBjoern A. Zeeb }
18126b4cac81SBjoern A. Zeeb 
18136b4cac81SBjoern A. Zeeb static int
1814d9f59799SBjoern A. Zeeb lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
1815d9f59799SBjoern A. Zeeb {
1816d9f59799SBjoern A. Zeeb 	int error;
1817d9f59799SBjoern A. Zeeb 
1818d9f59799SBjoern A. Zeeb 	error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
1819d9f59799SBjoern A. Zeeb 	if (error != 0 && error != EALREADY)
1820d9f59799SBjoern A. Zeeb 		return (error);
1821d9f59799SBjoern A. Zeeb 
1822d9f59799SBjoern A. Zeeb 	/* At this point iv_bss is long a new node! */
1823d9f59799SBjoern A. Zeeb 
1824d9f59799SBjoern A. Zeeb 	error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
1825d9f59799SBjoern A. Zeeb 	return (error);
1826d9f59799SBjoern A. Zeeb }
1827d9f59799SBjoern A. Zeeb 
1828d9f59799SBjoern A. Zeeb static int
18296b4cac81SBjoern A. Zeeb lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18306b4cac81SBjoern A. Zeeb {
18316b4cac81SBjoern A. Zeeb 	int error;
18326b4cac81SBjoern A. Zeeb 
1833d9f59799SBjoern A. Zeeb 	error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
18346b4cac81SBjoern A. Zeeb 	return (error);
18356b4cac81SBjoern A. Zeeb }
18366b4cac81SBjoern A. Zeeb 
18376b4cac81SBjoern A. Zeeb static int
18386b4cac81SBjoern A. Zeeb lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18396b4cac81SBjoern A. Zeeb {
18406b4cac81SBjoern A. Zeeb 	int error;
18416b4cac81SBjoern A. Zeeb 
1842d9f59799SBjoern A. Zeeb 	error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
18436b4cac81SBjoern A. Zeeb 	return (error);
18446b4cac81SBjoern A. Zeeb }
18456b4cac81SBjoern A. Zeeb 
18466b4cac81SBjoern A. Zeeb static int
18476b4cac81SBjoern A. Zeeb lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18486b4cac81SBjoern A. Zeeb {
18496b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
18506b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
18516b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
18526b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
18536b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
18546b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
18556b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
18566b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
18576b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
18586b4cac81SBjoern A. Zeeb 	int error;
18596b4cac81SBjoern A. Zeeb 
18606b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
18616b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
18626b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
18636b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
18646b4cac81SBjoern A. Zeeb 
18656b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
18668ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
18672ac8a218SBjoern A. Zeeb 
18682ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
18692ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
18702ac8a218SBjoern A. Zeeb 	if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
18712ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
18722ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
18732ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
18742ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
18752ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
18762ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
18772ac8a218SBjoern A. Zeeb #endif
18782ac8a218SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
18792ac8a218SBjoern A. Zeeb 		error = ENOTRECOVERABLE;
18802ac8a218SBjoern A. Zeeb 		goto out;
18812ac8a218SBjoern A. Zeeb 	}
18822ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
18832ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
18842ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
18852ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
18862ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
18872ac8a218SBjoern A. Zeeb 
18882ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
18896b4cac81SBjoern A. Zeeb 
18906b4cac81SBjoern A. Zeeb 	IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, "
18916b4cac81SBjoern A. Zeeb 	    "and to lesser extend ieee80211_notify_node_join");
18926b4cac81SBjoern A. Zeeb 
18939597f7cbSBjoern A. Zeeb 	/* Finish assoc. */
18949597f7cbSBjoern A. Zeeb 	/* Update sta_state (AUTH to ASSOC) and set aid. */
18959597f7cbSBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
18969597f7cbSBjoern A. Zeeb 	    "AUTH: %#x\n", __func__, lsta, lsta->state));
18976b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
18989597f7cbSBjoern A. Zeeb 	sta->aid = IEEE80211_NODE_AID(ni);
18994a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
19004a67f1dfSBjoern A. Zeeb 	if (vap->iv_flags & IEEE80211_F_WME)
19014a67f1dfSBjoern A. Zeeb 		sta->wme = true;
19024a67f1dfSBjoern A. Zeeb #endif
1903e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
1904018d93ecSBjoern A. Zeeb 	if (error != 0) {
1905018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
1906018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
19079597f7cbSBjoern A. Zeeb 		goto out;
1908018d93ecSBjoern A. Zeeb 	}
19096b4cac81SBjoern A. Zeeb 
19106b4cac81SBjoern A. Zeeb 	IMPROVE("wme / conf_tx [all]");
19116b4cac81SBjoern A. Zeeb 
19126b4cac81SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
19136b4cac81SBjoern A. Zeeb 	bss_changed = 0;
19144a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
19154a67f1dfSBjoern A. Zeeb 	bss_changed |= lkpi_wme_update(lhw, vap, true);
19164a67f1dfSBjoern A. Zeeb #endif
1917616e1330SBjoern A. Zeeb 	if (!vif->cfg.assoc || vif->cfg.aid != IEEE80211_NODE_AID(ni)) {
1918616e1330SBjoern A. Zeeb 		vif->cfg.assoc = true;
1919616e1330SBjoern A. Zeeb 		vif->cfg.aid = IEEE80211_NODE_AID(ni);
19206b4cac81SBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_ASSOC;
19216b4cac81SBjoern A. Zeeb 	}
19226b4cac81SBjoern A. Zeeb 	/* We set SSID but this is not BSSID! */
1923616e1330SBjoern A. Zeeb 	vif->cfg.ssid_len = ni->ni_esslen;
1924616e1330SBjoern A. Zeeb 	memcpy(vif->cfg.ssid, ni->ni_essid, ni->ni_esslen);
19256b4cac81SBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) !=
19266b4cac81SBjoern A. Zeeb 	    vif->bss_conf.use_short_preamble) {
19276b4cac81SBjoern A. Zeeb 		vif->bss_conf.use_short_preamble ^= 1;
19286b4cac81SBjoern A. Zeeb 		/* bss_changed |= BSS_CHANGED_??? */
19296b4cac81SBjoern A. Zeeb 	}
19306b4cac81SBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_SHSLOT) !=
19316b4cac81SBjoern A. Zeeb 	    vif->bss_conf.use_short_slot) {
19326b4cac81SBjoern A. Zeeb 		vif->bss_conf.use_short_slot ^= 1;
19336b4cac81SBjoern A. Zeeb 		/* bss_changed |= BSS_CHANGED_??? */
19346b4cac81SBjoern A. Zeeb 	}
19356b4cac81SBjoern A. Zeeb 	if ((ni->ni_flags & IEEE80211_NODE_QOS) !=
19366b4cac81SBjoern A. Zeeb 	    vif->bss_conf.qos) {
19376b4cac81SBjoern A. Zeeb 		vif->bss_conf.qos ^= 1;
19386b4cac81SBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_QOS;
19396b4cac81SBjoern A. Zeeb 	}
1940fa8f007dSBjoern A. Zeeb 
1941fa8f007dSBjoern A. Zeeb 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
1942fa8f007dSBjoern A. Zeeb 
19436b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
19446b4cac81SBjoern A. Zeeb 
19456b4cac81SBjoern A. Zeeb 	/* - change_chanctx (if needed)
19466b4cac81SBjoern A. Zeeb 	 * - event_callback
19476b4cac81SBjoern A. Zeeb 	 */
19486b4cac81SBjoern A. Zeeb 
19496b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
19506b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
19516b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
19526b4cac81SBjoern A. Zeeb 		prep_tx_info.success = true;
19536b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
19546b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
19556b4cac81SBjoern A. Zeeb 	}
19566b4cac81SBjoern A. Zeeb 
1957086be6a8SBjoern A. Zeeb 	lkpi_hw_conf_idle(hw, false);
1958086be6a8SBjoern A. Zeeb 
19596b4cac81SBjoern A. Zeeb 	/*
19606b4cac81SBjoern A. Zeeb 	 * And then:
19616b4cac81SBjoern A. Zeeb 	 * - (more packets)?
19626b4cac81SBjoern A. Zeeb 	 * - set_key
19636b4cac81SBjoern A. Zeeb 	 * - set_default_unicast_key
19646b4cac81SBjoern A. Zeeb 	 * - set_key (?)
19656b4cac81SBjoern A. Zeeb 	 * - ipv6_addr_change (?)
19666b4cac81SBjoern A. Zeeb 	 */
19676b4cac81SBjoern A. Zeeb 	/* Prepare_multicast && configure_filter. */
19686b4cac81SBjoern A. Zeeb 	lhw->update_mc = true;
19696b4cac81SBjoern A. Zeeb 	lkpi_update_mcast_filter(vap->iv_ic, true);
19706b4cac81SBjoern A. Zeeb 
19716b4cac81SBjoern A. Zeeb 	if (!ieee80211_node_is_authorized(ni)) {
19726b4cac81SBjoern A. Zeeb 		IMPROVE("net80211 does not consider node authorized");
19736b4cac81SBjoern A. Zeeb 	}
19746b4cac81SBjoern A. Zeeb 
19759fb91463SBjoern A. Zeeb #if defined(LKPI_80211_HT)
19769fb91463SBjoern A. Zeeb 	IMPROVE("Is this the right spot, has net80211 done all updates already?");
19779fb91463SBjoern A. Zeeb 	lkpi_sta_sync_ht_from_ni(sta, ni, NULL);
19789fb91463SBjoern A. Zeeb #endif
19799fb91463SBjoern A. Zeeb 
19806b4cac81SBjoern A. Zeeb 	/* Update sta_state (ASSOC to AUTHORIZED). */
19816b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
19826b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
19836b4cac81SBjoern A. Zeeb 	    "ASSOC: %#x\n", __func__, lsta, lsta->state));
1984e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTHORIZED);
19856b4cac81SBjoern A. Zeeb 	if (error != 0) {
19866b4cac81SBjoern A. Zeeb 		IMPROVE("undo some changes?");
1987018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTHORIZED) "
1988018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
19896b4cac81SBjoern A. Zeeb 		goto out;
19906b4cac81SBjoern A. Zeeb 	}
19916b4cac81SBjoern A. Zeeb 
19926b4cac81SBjoern A. Zeeb 	/* - drv_config (?)
19936b4cac81SBjoern A. Zeeb 	 * - bss_info_changed
19946b4cac81SBjoern A. Zeeb 	 * - set_rekey_data (?)
19956b4cac81SBjoern A. Zeeb 	 *
19966b4cac81SBjoern A. Zeeb 	 * And now we should be passing packets.
19976b4cac81SBjoern A. Zeeb 	 */
19986b4cac81SBjoern A. Zeeb 	IMPROVE("Need that bssid setting, and the keys");
19996b4cac81SBjoern A. Zeeb 
2000fa8f007dSBjoern A. Zeeb 	bss_changed = 0;
2001fa8f007dSBjoern A. Zeeb 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
2002fa8f007dSBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
2003fa8f007dSBjoern A. Zeeb 
20046b4cac81SBjoern A. Zeeb out:
20058ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
20066b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
20076b4cac81SBjoern A. Zeeb 	return (error);
20086b4cac81SBjoern A. Zeeb }
20096b4cac81SBjoern A. Zeeb 
20106b4cac81SBjoern A. Zeeb static int
20116b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
20126b4cac81SBjoern A. Zeeb {
20136b4cac81SBjoern A. Zeeb 	int error;
20146b4cac81SBjoern A. Zeeb 
20156b4cac81SBjoern A. Zeeb 	error = lkpi_sta_auth_to_assoc(vap, nstate, arg);
20166b4cac81SBjoern A. Zeeb 	if (error == 0)
20176b4cac81SBjoern A. Zeeb 		error = lkpi_sta_assoc_to_run(vap, nstate, arg);
20186b4cac81SBjoern A. Zeeb 	return (error);
20196b4cac81SBjoern A. Zeeb }
20206b4cac81SBjoern A. Zeeb 
20216b4cac81SBjoern A. Zeeb static int
20226b4cac81SBjoern A. Zeeb lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
20236b4cac81SBjoern A. Zeeb {
20246b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
20256b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
20266b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
20276b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
20286b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
20296b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
20306b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
2031d9f59799SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
2032d9f59799SBjoern A. Zeeb #if 0
2033d9f59799SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
2034d9f59799SBjoern A. Zeeb #endif
20356b4cac81SBjoern A. Zeeb 	int error;
20366b4cac81SBjoern A. Zeeb 
20376b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
20386b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
20396b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
20406b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
20416b4cac81SBjoern A. Zeeb 
20422ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
20432ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
20442ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
20452ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
20462ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
20472ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
20482ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
20492ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
20502ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
20512ac8a218SBjoern A. Zeeb #endif
20522ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
20532ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
20542ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
20552ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
20562ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
20572ac8a218SBjoern A. Zeeb 
20582ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
20596b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
20606b4cac81SBjoern A. Zeeb 
206167674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2062d9f59799SBjoern A. Zeeb 
2063d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
20648ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2065d9f59799SBjoern A. Zeeb 
2066d9f59799SBjoern A. Zeeb 	/* flush, drop. */
2067d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
2068d9f59799SBjoern A. Zeeb 
2069d9f59799SBjoern A. Zeeb 	IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
2070d9f59799SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
2071d9f59799SBjoern A. Zeeb 	    !lsta->in_mgd) {
2072d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2073d9f59799SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
2074d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
2075d9f59799SBjoern A. Zeeb 		lsta->in_mgd = true;
2076d9f59799SBjoern A. Zeeb 	}
2077d9f59799SBjoern A. Zeeb 
20788ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2079d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2080d9f59799SBjoern A. Zeeb 
2081d9f59799SBjoern A. Zeeb 	/* Call iv_newstate first so we get potential DISASSOC packet out. */
2082d9f59799SBjoern A. Zeeb 	error = lvif->iv_newstate(vap, nstate, arg);
2083018d93ecSBjoern A. Zeeb 	if (error != 0) {
2084018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
2085018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
2086d9f59799SBjoern A. Zeeb 		goto outni;
2087018d93ecSBjoern A. Zeeb 	}
2088d9f59799SBjoern A. Zeeb 
2089d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
20908ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2091d9f59799SBjoern A. Zeeb 
209267674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2093d9f59799SBjoern A. Zeeb 
2094d9f59799SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
2095d9f59799SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
2096d9f59799SBjoern A. Zeeb 
2097d9f59799SBjoern A. Zeeb 	/* flush, no drop */
2098d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
2099d9f59799SBjoern A. Zeeb 
2100d9f59799SBjoern A. Zeeb 	/* End mgd_complete_tx. */
2101d9f59799SBjoern A. Zeeb 	if (lsta->in_mgd) {
2102d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2103d9f59799SBjoern A. Zeeb 		prep_tx_info.success = false;
2104d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
2105d9f59799SBjoern A. Zeeb 		lsta->in_mgd = false;
2106d9f59799SBjoern A. Zeeb 	}
2107d9f59799SBjoern A. Zeeb 
2108d9f59799SBjoern A. Zeeb #if 0
2109d9f59799SBjoern A. Zeeb 	/* sync_rx_queues */
2110d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
2111d9f59799SBjoern A. Zeeb 
2112d9f59799SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
2113d9f59799SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
2114d9f59799SBjoern A. Zeeb #endif
2115d9f59799SBjoern A. Zeeb 
2116d9f59799SBjoern A. Zeeb 	/* Take the station down. */
2117d9f59799SBjoern A. Zeeb 
21186b4cac81SBjoern A. Zeeb 	/* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
21196b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
21206b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
21216b4cac81SBjoern A. Zeeb 	    "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
2122e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
2123018d93ecSBjoern A. Zeeb 	if (error != 0) {
2124018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
2125018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
21266b4cac81SBjoern A. Zeeb 		goto out;
2127018d93ecSBjoern A. Zeeb 	}
21286b4cac81SBjoern A. Zeeb 
212967674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
21306b4cac81SBjoern A. Zeeb 
21316b4cac81SBjoern A. Zeeb 	/* Update sta_state (ASSOC to AUTH). */
21326b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
21336b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
21346b4cac81SBjoern A. Zeeb 	    "ASSOC: %#x\n", __func__, lsta, lsta->state));
2135e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
2136018d93ecSBjoern A. Zeeb 	if (error != 0) {
2137018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
2138018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
21396b4cac81SBjoern A. Zeeb 		goto out;
2140018d93ecSBjoern A. Zeeb 	}
21416b4cac81SBjoern A. Zeeb 
214267674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
21436b4cac81SBjoern A. Zeeb 
2144d9f59799SBjoern A. Zeeb #if 0
2145d9f59799SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
2146d9f59799SBjoern A. Zeeb 	lkpi_disassoc(sta, vif, lhw);
2147d9f59799SBjoern A. Zeeb #endif
2148d9f59799SBjoern A. Zeeb 
2149d9f59799SBjoern A. Zeeb 	error = EALREADY;
21506b4cac81SBjoern A. Zeeb out:
21518ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
21526b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2153d9f59799SBjoern A. Zeeb outni:
21546b4cac81SBjoern A. Zeeb 	return (error);
21556b4cac81SBjoern A. Zeeb }
21566b4cac81SBjoern A. Zeeb 
21576b4cac81SBjoern A. Zeeb static int
2158d9f59799SBjoern A. Zeeb lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2159d9f59799SBjoern A. Zeeb {
2160d9f59799SBjoern A. Zeeb 	struct lkpi_hw *lhw;
2161d9f59799SBjoern A. Zeeb 	struct ieee80211_hw *hw;
2162d9f59799SBjoern A. Zeeb 	struct lkpi_vif *lvif;
2163d9f59799SBjoern A. Zeeb 	struct ieee80211_vif *vif;
2164d9f59799SBjoern A. Zeeb 	struct ieee80211_node *ni;
2165d9f59799SBjoern A. Zeeb 	struct lkpi_sta *lsta;
2166d9f59799SBjoern A. Zeeb 	struct ieee80211_sta *sta;
2167d9f59799SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
2168d9f59799SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
2169d9f59799SBjoern A. Zeeb 	int error;
2170d9f59799SBjoern A. Zeeb 
2171d9f59799SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
2172d9f59799SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
2173d9f59799SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
2174d9f59799SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
2175d9f59799SBjoern A. Zeeb 
21762ac8a218SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
21772ac8a218SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
21782ac8a218SBjoern A. Zeeb 
21792ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
21802ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
21812ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
21822ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
21832ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
21842ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
21852ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
21862ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
21872ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
21882ac8a218SBjoern A. Zeeb #endif
21892ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
21902ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
21912ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
21922ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
21932ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
21942ac8a218SBjoern A. Zeeb 
21952ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
2196d9f59799SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
2197d9f59799SBjoern A. Zeeb 
219867674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2199d9f59799SBjoern A. Zeeb 
2200d9f59799SBjoern A. Zeeb 	/* flush, drop. */
2201d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
2202d9f59799SBjoern A. Zeeb 
2203d9f59799SBjoern A. Zeeb 	IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
2204d9f59799SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
2205d9f59799SBjoern A. Zeeb 	    !lsta->in_mgd) {
2206d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2207d9f59799SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
2208d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
2209d9f59799SBjoern A. Zeeb 		lsta->in_mgd = true;
2210d9f59799SBjoern A. Zeeb 	}
2211d9f59799SBjoern A. Zeeb 
22128ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2213d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2214d9f59799SBjoern A. Zeeb 
2215d9f59799SBjoern A. Zeeb 	/* Call iv_newstate first so we get potential DISASSOC packet out. */
2216d9f59799SBjoern A. Zeeb 	error = lvif->iv_newstate(vap, nstate, arg);
2217018d93ecSBjoern A. Zeeb 	if (error != 0) {
2218018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
2219018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
2220d9f59799SBjoern A. Zeeb 		goto outni;
2221018d93ecSBjoern A. Zeeb 	}
2222d9f59799SBjoern A. Zeeb 
2223d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
22248ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2225d9f59799SBjoern A. Zeeb 
222667674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2227d9f59799SBjoern A. Zeeb 
2228d9f59799SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
2229d9f59799SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
2230d9f59799SBjoern A. Zeeb 
2231d9f59799SBjoern A. Zeeb 	/* flush, no drop */
2232d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
2233d9f59799SBjoern A. Zeeb 
2234d9f59799SBjoern A. Zeeb 	/* End mgd_complete_tx. */
2235d9f59799SBjoern A. Zeeb 	if (lsta->in_mgd) {
2236d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2237d9f59799SBjoern A. Zeeb 		prep_tx_info.success = false;
2238d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
2239d9f59799SBjoern A. Zeeb 		lsta->in_mgd = false;
2240d9f59799SBjoern A. Zeeb 	}
2241d9f59799SBjoern A. Zeeb 
2242d9f59799SBjoern A. Zeeb 	/* sync_rx_queues */
2243d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
2244d9f59799SBjoern A. Zeeb 
2245d9f59799SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
2246d9f59799SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
2247d9f59799SBjoern A. Zeeb 
2248d9f59799SBjoern A. Zeeb 	/* Take the station down. */
2249d9f59799SBjoern A. Zeeb 
2250d9f59799SBjoern A. Zeeb 	/* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
2251d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2252d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
2253d9f59799SBjoern A. Zeeb 	    "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
2254e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
2255018d93ecSBjoern A. Zeeb 	if (error != 0) {
2256018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
2257018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2258d9f59799SBjoern A. Zeeb 		goto out;
2259018d93ecSBjoern A. Zeeb 	}
2260d9f59799SBjoern A. Zeeb 
226167674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2262d9f59799SBjoern A. Zeeb 
2263d9f59799SBjoern A. Zeeb 	/* Update sta_state (ASSOC to AUTH). */
2264d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2265d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
2266d9f59799SBjoern A. Zeeb 	    "ASSOC: %#x\n", __func__, lsta, lsta->state));
2267e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
2268018d93ecSBjoern A. Zeeb 	if (error != 0) {
2269018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
2270018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2271d9f59799SBjoern A. Zeeb 		goto out;
2272018d93ecSBjoern A. Zeeb 	}
2273d9f59799SBjoern A. Zeeb 
227467674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2275d9f59799SBjoern A. Zeeb 
2276d9f59799SBjoern A. Zeeb 	/* Update sta and change state (from AUTH) to NONE. */
2277d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2278d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
2279d9f59799SBjoern A. Zeeb 	    "AUTH: %#x\n", __func__, lsta, lsta->state));
2280e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
2281018d93ecSBjoern A. Zeeb 	if (error != 0) {
2282018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
2283018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2284d9f59799SBjoern A. Zeeb 		goto out;
2285018d93ecSBjoern A. Zeeb 	}
2286d9f59799SBjoern A. Zeeb 
228767674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2288d9f59799SBjoern A. Zeeb 
228916e688b2SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
229016e688b2SBjoern A. Zeeb 	/*
229116e688b2SBjoern A. Zeeb 	 * One would expect this to happen when going off AUTHORIZED.
229216e688b2SBjoern A. Zeeb 	 * See comment there; removes the sta from fw.
229316e688b2SBjoern A. Zeeb 	 */
229416e688b2SBjoern A. Zeeb 	lkpi_disassoc(sta, vif, lhw);
229516e688b2SBjoern A. Zeeb 
2296d9f59799SBjoern A. Zeeb 	/* Adjust sta and change state (from NONE) to NOTEXIST. */
2297d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2298d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
2299d9f59799SBjoern A. Zeeb 	    "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
2300e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
2301d9f59799SBjoern A. Zeeb 	if (error != 0) {
2302d9f59799SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
2303018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
2304018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2305d9f59799SBjoern A. Zeeb 		goto out;
2306d9f59799SBjoern A. Zeeb 	}
2307d9f59799SBjoern A. Zeeb 
230816e688b2SBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);	/* sta no longer save to use. */
2309d9f59799SBjoern A. Zeeb 
2310d9f59799SBjoern A. Zeeb 	IMPROVE("Any bss_info changes to announce?");
2311d9f59799SBjoern A. Zeeb 	bss_changed = 0;
2312d9f59799SBjoern A. Zeeb 	vif->bss_conf.qos = 0;
2313d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_QOS;
2314616e1330SBjoern A. Zeeb 	vif->cfg.ssid_len = 0;
2315616e1330SBjoern A. Zeeb 	memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
2316d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_BSSID;
2317d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
2318d9f59799SBjoern A. Zeeb 
23192ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
23202ac8a218SBjoern A. Zeeb 	/* Remove ni reference for this cache of lsta. */
23212ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
23222ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
23232ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
2324d9f59799SBjoern A. Zeeb 	lkpi_lsta_remove(lsta, lvif);
23250936c648SBjoern A. Zeeb 	/*
23260936c648SBjoern A. Zeeb 	 * The very last release the reference on the ni for the ni/lsta on
23270936c648SBjoern A. Zeeb 	 * lvif->lvif_bss.  Upon return from this both ni and lsta are invalid
23280936c648SBjoern A. Zeeb 	 * and potentially freed.
23290936c648SBjoern A. Zeeb 	 */
23300936c648SBjoern A. Zeeb 	ieee80211_free_node(ni);
2331d9f59799SBjoern A. Zeeb 
2332d9f59799SBjoern A. Zeeb 	/* conf_tx */
2333d9f59799SBjoern A. Zeeb 
2334d9f59799SBjoern A. Zeeb 	/* Take the chan ctx down. */
2335d9f59799SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
2336c5e25798SBjoern A. Zeeb 		struct lkpi_chanctx *lchanctx;
2337d9f59799SBjoern A. Zeeb 		struct ieee80211_chanctx_conf *conf;
2338d9f59799SBjoern A. Zeeb 
2339d9f59799SBjoern A. Zeeb 		conf = vif->chanctx_conf;
2340d9f59799SBjoern A. Zeeb 		/* Remove vif context. */
234168541546SBjoern A. Zeeb 		lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
2342d9f59799SBjoern A. Zeeb 		/* NB: vif->chanctx_conf is NULL now. */
2343d9f59799SBjoern A. Zeeb 
2344d9f59799SBjoern A. Zeeb 		/* Remove chan ctx. */
2345d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_remove_chanctx(hw, conf);
2346c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
2347c5e25798SBjoern A. Zeeb 		free(lchanctx, M_LKPI80211);
2348d9f59799SBjoern A. Zeeb 	}
2349d9f59799SBjoern A. Zeeb 
2350d9f59799SBjoern A. Zeeb 	error = EALREADY;
2351d9f59799SBjoern A. Zeeb out:
23528ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2353d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2354d9f59799SBjoern A. Zeeb outni:
2355d9f59799SBjoern A. Zeeb 	return (error);
2356d9f59799SBjoern A. Zeeb }
2357d9f59799SBjoern A. Zeeb 
2358d9f59799SBjoern A. Zeeb static int
2359d9f59799SBjoern A. Zeeb lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2360d9f59799SBjoern A. Zeeb {
2361d9f59799SBjoern A. Zeeb 
2362d9f59799SBjoern A. Zeeb 	return (lkpi_sta_run_to_init(vap, nstate, arg));
2363d9f59799SBjoern A. Zeeb }
2364d9f59799SBjoern A. Zeeb 
2365d9f59799SBjoern A. Zeeb static int
23666b4cac81SBjoern A. Zeeb lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
23676b4cac81SBjoern A. Zeeb {
23686b4cac81SBjoern A. Zeeb 	int error;
23696b4cac81SBjoern A. Zeeb 
2370d9f59799SBjoern A. Zeeb 	error = lkpi_sta_run_to_init(vap, nstate, arg);
2371d9f59799SBjoern A. Zeeb 	if (error != 0 && error != EALREADY)
2372d9f59799SBjoern A. Zeeb 		return (error);
2373d9f59799SBjoern A. Zeeb 
2374d9f59799SBjoern A. Zeeb 	/* At this point iv_bss is long a new node! */
2375d9f59799SBjoern A. Zeeb 
2376d9f59799SBjoern A. Zeeb 	error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
23776b4cac81SBjoern A. Zeeb 	return (error);
23786b4cac81SBjoern A. Zeeb }
23796b4cac81SBjoern A. Zeeb 
2380d9f59799SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
23816b4cac81SBjoern A. Zeeb 
23826b4cac81SBjoern A. Zeeb /*
23836b4cac81SBjoern A. Zeeb  * The matches the documented state changes in net80211::sta_newstate().
23846b4cac81SBjoern A. Zeeb  * XXX (1) without CSA and SLEEP yet, * XXX (2) not all unhandled cases
23856b4cac81SBjoern A. Zeeb  * there are "invalid" (so there is a room for failure here).
23866b4cac81SBjoern A. Zeeb  */
23876b4cac81SBjoern A. Zeeb struct fsm_state {
23886b4cac81SBjoern A. Zeeb 	/* INIT, SCAN, AUTH, ASSOC, CAC, RUN, CSA, SLEEP */
23896b4cac81SBjoern A. Zeeb 	enum ieee80211_state ostate;
23906b4cac81SBjoern A. Zeeb 	enum ieee80211_state nstate;
23916b4cac81SBjoern A. Zeeb 	int (*handler)(struct ieee80211vap *, enum ieee80211_state, int);
23926b4cac81SBjoern A. Zeeb } sta_state_fsm[] = {
23936b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_INIT, lkpi_sta_state_do_nada },
23946b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_SCAN,	IEEE80211_S_INIT, lkpi_sta_state_do_nada },	/* scan_to_init */
23956b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_INIT, lkpi_sta_auth_to_init },	/* not explicitly in sta_newstate() */
2396d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_INIT, lkpi_sta_assoc_to_init },	/* Send DEAUTH. */
2397d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_INIT, lkpi_sta_run_to_init },	/* Send DISASSOC. */
23986b4cac81SBjoern A. Zeeb 
23996b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
24006b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_SCAN,	IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
24016b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_SCAN, lkpi_sta_auth_to_scan },
24026b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan },
2403d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_SCAN, lkpi_sta_run_to_scan },	/* Beacon miss. */
24046b4cac81SBjoern A. Zeeb 
2405d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },	/* Send AUTH. */
2406d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_SCAN,	IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },	/* Send AUTH. */
2407d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_AUTH, lkpi_sta_a_to_a },		/* Send ?AUTH. */
2408d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth },	/* Send ?AUTH. */
2409d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_AUTH, lkpi_sta_run_to_auth },	/* Send ?AUTH. */
24106b4cac81SBjoern A. Zeeb 
2411d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc },	/* Send ASSOCREQ. */
2412d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_ASSOC, lkpi_sta_a_to_a },		/* Send ASSOCREQ. */
2413d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc },	/* Send ASSOCREQ/REASSOCREQ. */
24146b4cac81SBjoern A. Zeeb 
24156b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_RUN, lkpi_sta_auth_to_run },
24166b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_RUN, lkpi_sta_assoc_to_run },
24176b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_RUN, lkpi_sta_state_do_nada },
24186b4cac81SBjoern A. Zeeb 
24196b4cac81SBjoern A. Zeeb 	/* Dummy at the end without handler. */
24206b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_INIT, NULL },
24216b4cac81SBjoern A. Zeeb };
24226b4cac81SBjoern A. Zeeb 
24236b4cac81SBjoern A. Zeeb static int
24246b4cac81SBjoern A. Zeeb lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
24256b4cac81SBjoern A. Zeeb {
24266b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
24276b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
24286b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
24296b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
24306b4cac81SBjoern A. Zeeb 	struct fsm_state *s;
24316b4cac81SBjoern A. Zeeb 	enum ieee80211_state ostate;
24326b4cac81SBjoern A. Zeeb 	int error;
24336b4cac81SBjoern A. Zeeb 
24346b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
24356b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK_ASSERT(ic);
24366b4cac81SBjoern A. Zeeb 	ostate = vap->iv_state;
24376b4cac81SBjoern A. Zeeb 
24389d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
24399d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
24406b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x\n",
24416b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, vap, nstate, arg);
24429d9ba2b7SBjoern A. Zeeb #endif
24436b4cac81SBjoern A. Zeeb 
24446b4cac81SBjoern A. Zeeb 	if (vap->iv_opmode == IEEE80211_M_STA) {
24456b4cac81SBjoern A. Zeeb 
24466b4cac81SBjoern A. Zeeb 		lhw = ic->ic_softc;
24476b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
24486b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
24496b4cac81SBjoern A. Zeeb 
24506b4cac81SBjoern A. Zeeb 		/* No need to replicate this in most state handlers. */
24516b4cac81SBjoern A. Zeeb 		if (ostate == IEEE80211_S_SCAN && nstate != IEEE80211_S_SCAN)
24526b4cac81SBjoern A. Zeeb 			lkpi_stop_hw_scan(lhw, vif);
24536b4cac81SBjoern A. Zeeb 
24546b4cac81SBjoern A. Zeeb 		s = sta_state_fsm;
24556b4cac81SBjoern A. Zeeb 
24566b4cac81SBjoern A. Zeeb 	} else {
24576b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: only station mode currently supported: "
24586b4cac81SBjoern A. Zeeb 		    "cap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode);
24596b4cac81SBjoern A. Zeeb 		return (ENOSYS);
24606b4cac81SBjoern A. Zeeb 	}
24616b4cac81SBjoern A. Zeeb 
24626b4cac81SBjoern A. Zeeb 	error = 0;
24636b4cac81SBjoern A. Zeeb 	for (; s->handler != NULL; s++) {
24646b4cac81SBjoern A. Zeeb 		if (ostate == s->ostate && nstate == s->nstate) {
24659d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
24669d9ba2b7SBjoern A. Zeeb 			if (linuxkpi_debug_80211 & D80211_TRACE)
2467d9f59799SBjoern A. Zeeb 				ic_printf(vap->iv_ic, "%s: new state %d (%s) ->"
2468d9f59799SBjoern A. Zeeb 				    " %d (%s): arg %d.\n", __func__,
2469d9f59799SBjoern A. Zeeb 				    ostate, ieee80211_state_name[ostate],
2470d9f59799SBjoern A. Zeeb 				    nstate, ieee80211_state_name[nstate], arg);
24719d9ba2b7SBjoern A. Zeeb #endif
24726b4cac81SBjoern A. Zeeb 			error = s->handler(vap, nstate, arg);
24736b4cac81SBjoern A. Zeeb 			break;
24746b4cac81SBjoern A. Zeeb 		}
24756b4cac81SBjoern A. Zeeb 	}
24766b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
24776b4cac81SBjoern A. Zeeb 
24786b4cac81SBjoern A. Zeeb 	if (s->handler == NULL) {
2479d9f59799SBjoern A. Zeeb 		IMPROVE("turn this into a KASSERT\n");
24806b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: unsupported state transition "
24816b4cac81SBjoern A. Zeeb 		    "%d (%s) -> %d (%s)\n", __func__,
24826b4cac81SBjoern A. Zeeb 		    ostate, ieee80211_state_name[ostate],
24836b4cac81SBjoern A. Zeeb 		    nstate, ieee80211_state_name[nstate]);
24846b4cac81SBjoern A. Zeeb 		return (ENOSYS);
24856b4cac81SBjoern A. Zeeb 	}
24866b4cac81SBjoern A. Zeeb 
24876b4cac81SBjoern A. Zeeb 	if (error == EALREADY) {
24889d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
24899d9ba2b7SBjoern A. Zeeb 		if (linuxkpi_debug_80211 & D80211_TRACE)
2490d9f59799SBjoern A. Zeeb 			ic_printf(vap->iv_ic, "%s: state transition %d (%s) -> "
2491d9f59799SBjoern A. Zeeb 			    "%d (%s): iv_newstate already handled: %d.\n",
2492d9f59799SBjoern A. Zeeb 			    __func__, ostate, ieee80211_state_name[ostate],
2493d9f59799SBjoern A. Zeeb 			    nstate, ieee80211_state_name[nstate], error);
24949d9ba2b7SBjoern A. Zeeb #endif
24956b4cac81SBjoern A. Zeeb 		return (0);
24966b4cac81SBjoern A. Zeeb 	}
24976b4cac81SBjoern A. Zeeb 
24986b4cac81SBjoern A. Zeeb 	if (error != 0) {
24996b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: error %d during state transition "
25006b4cac81SBjoern A. Zeeb 		    "%d (%s) -> %d (%s)\n", __func__, error,
25016b4cac81SBjoern A. Zeeb 		    ostate, ieee80211_state_name[ostate],
25026b4cac81SBjoern A. Zeeb 		    nstate, ieee80211_state_name[nstate]);
250345c27ad5SBjoern A. Zeeb 		return (error);
25046b4cac81SBjoern A. Zeeb 	}
25056b4cac81SBjoern A. Zeeb 
25069d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
25079d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
2508d9f59799SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x "
2509d9f59799SBjoern A. Zeeb 		    "calling net80211 parent\n",
25106b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, vap, nstate, arg);
25119d9ba2b7SBjoern A. Zeeb #endif
25126b4cac81SBjoern A. Zeeb 
25136b4cac81SBjoern A. Zeeb 	return (lvif->iv_newstate(vap, nstate, arg));
25146b4cac81SBjoern A. Zeeb }
25156b4cac81SBjoern A. Zeeb 
25166b4cac81SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
25176b4cac81SBjoern A. Zeeb 
2518d9f59799SBjoern A. Zeeb /*
2519d9f59799SBjoern A. Zeeb  * We overload (*iv_update_bss) as otherwise we have cases in, e.g.,
2520d9f59799SBjoern A. Zeeb  * net80211::ieee80211_sta_join1() where vap->iv_bss gets replaced by a
2521d9f59799SBjoern A. Zeeb  * new node without us knowing and thus our ni/lsta are out of sync.
2522d9f59799SBjoern A. Zeeb  */
2523d9f59799SBjoern A. Zeeb static struct ieee80211_node *
2524d9f59799SBjoern A. Zeeb lkpi_iv_update_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
2525d9f59799SBjoern A. Zeeb {
2526d9f59799SBjoern A. Zeeb 	struct lkpi_vif *lvif;
25272ac8a218SBjoern A. Zeeb 	struct ieee80211_node *rni;
2528d9f59799SBjoern A. Zeeb 
25292ac8a218SBjoern A. Zeeb 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
2530d9f59799SBjoern A. Zeeb 
2531ed3ef56bSBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
25322ac8a218SBjoern A. Zeeb 
25332ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
25342ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
25352ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
25362ac8a218SBjoern A. Zeeb 
25372ac8a218SBjoern A. Zeeb 	rni = lvif->iv_update_bss(vap, ni);
25382ac8a218SBjoern A. Zeeb 	return (rni);
2539d9f59799SBjoern A. Zeeb }
2540d9f59799SBjoern A. Zeeb 
25414a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
25426b4cac81SBjoern A. Zeeb static int
25434a67f1dfSBjoern A. Zeeb lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
25446b4cac81SBjoern A. Zeeb {
25454a67f1dfSBjoern A. Zeeb 	struct ieee80211com *ic;
25466b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
25476b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
25486b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
25496b4cac81SBjoern A. Zeeb 	struct chanAccParams chp;
25506b4cac81SBjoern A. Zeeb 	struct wmeParams wmeparr[WME_NUM_AC];
25516b4cac81SBjoern A. Zeeb 	struct ieee80211_tx_queue_params txqp;
25526b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed changed;
25536b4cac81SBjoern A. Zeeb 	int error;
25546b4cac81SBjoern A. Zeeb 	uint16_t ac;
25556b4cac81SBjoern A. Zeeb 
25566b4cac81SBjoern A. Zeeb 	IMPROVE();
25576b4cac81SBjoern A. Zeeb 	KASSERT(WME_NUM_AC == IEEE80211_NUM_ACS, ("%s: WME_NUM_AC %d != "
25586b4cac81SBjoern A. Zeeb 	    "IEEE80211_NUM_ACS %d\n", __func__, WME_NUM_AC, IEEE80211_NUM_ACS));
25596b4cac81SBjoern A. Zeeb 
25606b4cac81SBjoern A. Zeeb 	if (vap == NULL)
25616b4cac81SBjoern A. Zeeb 		return (0);
25626b4cac81SBjoern A. Zeeb 
25634a67f1dfSBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_WME) == 0)
25644a67f1dfSBjoern A. Zeeb 		return (0);
25654a67f1dfSBjoern A. Zeeb 
25666b4cac81SBjoern A. Zeeb 	if (lhw->ops->conf_tx == NULL)
25676b4cac81SBjoern A. Zeeb 		return (0);
25686b4cac81SBjoern A. Zeeb 
25694a67f1dfSBjoern A. Zeeb 	if (!planned && (vap->iv_state != IEEE80211_S_RUN)) {
25704a67f1dfSBjoern A. Zeeb 		lhw->update_wme = true;
25714a67f1dfSBjoern A. Zeeb 		return (0);
25724a67f1dfSBjoern A. Zeeb 	}
25734a67f1dfSBjoern A. Zeeb 	lhw->update_wme = false;
25746b4cac81SBjoern A. Zeeb 
25754a67f1dfSBjoern A. Zeeb 	ic = lhw->ic;
25766b4cac81SBjoern A. Zeeb 	ieee80211_wme_ic_getparams(ic, &chp);
25776b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(ic);
25786b4cac81SBjoern A. Zeeb 	for (ac = 0; ac < WME_NUM_AC; ac++)
25796b4cac81SBjoern A. Zeeb 		wmeparr[ac] = chp.cap_wmeParams[ac];
25806b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(ic);
25816b4cac81SBjoern A. Zeeb 
25824a67f1dfSBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
25834a67f1dfSBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
25844a67f1dfSBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
25854a67f1dfSBjoern A. Zeeb 
25866b4cac81SBjoern A. Zeeb 	/* Configure tx queues (conf_tx) & send BSS_CHANGED_QOS. */
25876b4cac81SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
25886b4cac81SBjoern A. Zeeb 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
25896b4cac81SBjoern A. Zeeb 		struct wmeParams *wmep;
25906b4cac81SBjoern A. Zeeb 
25916b4cac81SBjoern A. Zeeb 		wmep = &wmeparr[ac];
25926b4cac81SBjoern A. Zeeb 		bzero(&txqp, sizeof(txqp));
25936b4cac81SBjoern A. Zeeb 		txqp.cw_min = wmep->wmep_logcwmin;
25946b4cac81SBjoern A. Zeeb 		txqp.cw_max = wmep->wmep_logcwmax;
25956b4cac81SBjoern A. Zeeb 		txqp.txop = wmep->wmep_txopLimit;
25966b4cac81SBjoern A. Zeeb 		txqp.aifs = wmep->wmep_aifsn;
259768541546SBjoern A. Zeeb 		error = lkpi_80211_mo_conf_tx(hw, vif, /* link_id */0, ac, &txqp);
25986b4cac81SBjoern A. Zeeb 		if (error != 0)
25993f0083c4SBjoern A. Zeeb 			ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
26006b4cac81SBjoern A. Zeeb 			    __func__, ac, error);
26016b4cac81SBjoern A. Zeeb 	}
26026b4cac81SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
26036b4cac81SBjoern A. Zeeb 	changed = BSS_CHANGED_QOS;
26044a67f1dfSBjoern A. Zeeb 	if (!planned)
26056b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
26064a67f1dfSBjoern A. Zeeb 
26074a67f1dfSBjoern A. Zeeb 	return (changed);
26084a67f1dfSBjoern A. Zeeb }
26096b4cac81SBjoern A. Zeeb #endif
26106b4cac81SBjoern A. Zeeb 
26114a67f1dfSBjoern A. Zeeb static int
26124a67f1dfSBjoern A. Zeeb lkpi_ic_wme_update(struct ieee80211com *ic)
26134a67f1dfSBjoern A. Zeeb {
26144a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
26154a67f1dfSBjoern A. Zeeb 	struct ieee80211vap *vap;
26164a67f1dfSBjoern A. Zeeb 	struct lkpi_hw *lhw;
26174a67f1dfSBjoern A. Zeeb 
26184a67f1dfSBjoern A. Zeeb 	IMPROVE("Use the per-VAP callback in net80211.");
26194a67f1dfSBjoern A. Zeeb 	vap = TAILQ_FIRST(&ic->ic_vaps);
26204a67f1dfSBjoern A. Zeeb 	if (vap == NULL)
26216b4cac81SBjoern A. Zeeb 		return (0);
26224a67f1dfSBjoern A. Zeeb 
26234a67f1dfSBjoern A. Zeeb 	lhw = ic->ic_softc;
26244a67f1dfSBjoern A. Zeeb 
26254a67f1dfSBjoern A. Zeeb 	lkpi_wme_update(lhw, vap, false);
26264a67f1dfSBjoern A. Zeeb #endif
26274a67f1dfSBjoern A. Zeeb 	return (0);	/* unused */
26286b4cac81SBjoern A. Zeeb }
26296b4cac81SBjoern A. Zeeb 
26306b4cac81SBjoern A. Zeeb static struct ieee80211vap *
26316b4cac81SBjoern A. Zeeb lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
26326b4cac81SBjoern A. Zeeb     int unit, enum ieee80211_opmode opmode, int flags,
26336b4cac81SBjoern A. Zeeb     const uint8_t bssid[IEEE80211_ADDR_LEN],
26346b4cac81SBjoern A. Zeeb     const uint8_t mac[IEEE80211_ADDR_LEN])
26356b4cac81SBjoern A. Zeeb {
26366b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
26376b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
26386b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
26396b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
26406b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
2641a6042e17SBjoern A. Zeeb 	struct ieee80211_tx_queue_params txqp;
26426b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed changed;
26436b4cac81SBjoern A. Zeeb 	size_t len;
2644e3a0b120SBjoern A. Zeeb 	int error, i;
2645a6042e17SBjoern A. Zeeb 	uint16_t ac;
26466b4cac81SBjoern A. Zeeb 
26476b4cac81SBjoern A. Zeeb 	if (!TAILQ_EMPTY(&ic->ic_vaps))	/* 1 so far. Add <n> once this works. */
26486b4cac81SBjoern A. Zeeb 		return (NULL);
26496b4cac81SBjoern A. Zeeb 
26506b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
26516b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
26526b4cac81SBjoern A. Zeeb 
26536b4cac81SBjoern A. Zeeb 	len = sizeof(*lvif);
26546b4cac81SBjoern A. Zeeb 	len += hw->vif_data_size;	/* vif->drv_priv */
26556b4cac81SBjoern A. Zeeb 
26566b4cac81SBjoern A. Zeeb 	lvif = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO);
26576b4cac81SBjoern A. Zeeb 	mtx_init(&lvif->mtx, "lvif", NULL, MTX_DEF);
26586b4cac81SBjoern A. Zeeb 	TAILQ_INIT(&lvif->lsta_head);
26592ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
26602ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
26616b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
26626b4cac81SBjoern A. Zeeb 
26636b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
26646b4cac81SBjoern A. Zeeb 	memcpy(vif->addr, mac, IEEE80211_ADDR_LEN);
26656b4cac81SBjoern A. Zeeb 	vif->p2p = false;
26666b4cac81SBjoern A. Zeeb 	vif->probe_req_reg = false;
26676b4cac81SBjoern A. Zeeb 	vif->type = lkpi_opmode_to_vif_type(opmode);
26686b4cac81SBjoern A. Zeeb 	lvif->wdev.iftype = vif->type;
26696b4cac81SBjoern A. Zeeb 	/* Need to fill in other fields as well. */
26706b4cac81SBjoern A. Zeeb 	IMPROVE();
26716b4cac81SBjoern A. Zeeb 
26726b4cac81SBjoern A. Zeeb 	/* XXX-BZ hardcoded for now! */
26736b4cac81SBjoern A. Zeeb #if 1
26746b4cac81SBjoern A. Zeeb 	vif->chanctx_conf = NULL;
2675adff403fSBjoern A. Zeeb 	vif->bss_conf.vif = vif;
26766ffb7bd4SBjoern A. Zeeb 	/* vap->iv_myaddr is not set until net80211::vap_setup or vap_attach. */
26776ffb7bd4SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(vif->bss_conf.addr, mac);
26786ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.link_id = 0;	/* Non-MLO operation. */
26796b4cac81SBjoern A. Zeeb 	vif->bss_conf.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
26806b4cac81SBjoern A. Zeeb 	vif->bss_conf.use_short_preamble = false;	/* vap->iv_flags IEEE80211_F_SHPREAMBLE */
26816b4cac81SBjoern A. Zeeb 	vif->bss_conf.use_short_slot = false;		/* vap->iv_flags IEEE80211_F_SHSLOT */
26826b4cac81SBjoern A. Zeeb 	vif->bss_conf.qos = false;
26836b4cac81SBjoern A. Zeeb 	vif->bss_conf.use_cts_prot = false;		/* vap->iv_protmode */
26846b4cac81SBjoern A. Zeeb 	vif->bss_conf.ht_operation_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
2685616e1330SBjoern A. Zeeb 	vif->cfg.aid = 0;
2686616e1330SBjoern A. Zeeb 	vif->cfg.assoc = false;
2687616e1330SBjoern A. Zeeb 	vif->cfg.idle = true;
2688616e1330SBjoern A. Zeeb 	vif->cfg.ps = false;
26896ffb7bd4SBjoern A. Zeeb 	IMPROVE("Check other fields and then figure out whats is left elsewhere of them");
2690caaa79c3SBjoern A. Zeeb 	/*
2691caaa79c3SBjoern A. Zeeb 	 * We need to initialize it to something as the bss_info_changed call
2692caaa79c3SBjoern A. Zeeb 	 * will try to copy from it in iwlwifi and NULL is a panic.
2693caaa79c3SBjoern A. Zeeb 	 * We will set the proper one in scan_to_auth() before being assoc.
2694caaa79c3SBjoern A. Zeeb 	 */
26956ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.bssid = ieee80211broadcastaddr;
26966b4cac81SBjoern A. Zeeb #endif
26976b4cac81SBjoern A. Zeeb #if 0
26986b4cac81SBjoern A. Zeeb 	vif->bss_conf.dtim_period = 0; /* IEEE80211_DTIM_DEFAULT ; must stay 0. */
26996b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(vif->bss_conf.bssid, bssid);
27006b4cac81SBjoern A. Zeeb 	vif->bss_conf.beacon_int = ic->ic_bintval;
27016b4cac81SBjoern A. Zeeb 	/* iwlwifi bug. */
27026b4cac81SBjoern A. Zeeb 	if (vif->bss_conf.beacon_int < 16)
27036b4cac81SBjoern A. Zeeb 		vif->bss_conf.beacon_int = 16;
27046b4cac81SBjoern A. Zeeb #endif
2705e3a0b120SBjoern A. Zeeb 
27066ffb7bd4SBjoern A. Zeeb 	/* Link Config */
27076ffb7bd4SBjoern A. Zeeb 	vif->link_conf[0] = &vif->bss_conf;
27086ffb7bd4SBjoern A. Zeeb 	for (i = 0; i < nitems(vif->link_conf); i++) {
27096ffb7bd4SBjoern A. Zeeb 		IMPROVE("more than 1 link one day");
27106ffb7bd4SBjoern A. Zeeb 	}
27116ffb7bd4SBjoern A. Zeeb 
2712e3a0b120SBjoern A. Zeeb 	/* Setup queue defaults; driver may override in (*add_interface). */
2713e3a0b120SBjoern A. Zeeb 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
2714e3a0b120SBjoern A. Zeeb 		if (ieee80211_hw_check(hw, QUEUE_CONTROL))
2715e3a0b120SBjoern A. Zeeb 			vif->hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
2716e3a0b120SBjoern A. Zeeb 		else if (hw->queues >= IEEE80211_NUM_ACS)
2717e3a0b120SBjoern A. Zeeb 			vif->hw_queue[i] = i;
2718e3a0b120SBjoern A. Zeeb 		else
2719e3a0b120SBjoern A. Zeeb 			vif->hw_queue[i] = 0;
27200cbcfa19SBjoern A. Zeeb 
27210cbcfa19SBjoern A. Zeeb 		/* Initialize the queue to running. Stopped? */
27220cbcfa19SBjoern A. Zeeb 		lvif->hw_queue_stopped[i] = false;
2723e3a0b120SBjoern A. Zeeb 	}
2724e3a0b120SBjoern A. Zeeb 	vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
2725e3a0b120SBjoern A. Zeeb 
27266b4cac81SBjoern A. Zeeb 	IMPROVE();
27276b4cac81SBjoern A. Zeeb 
27286b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_start(hw);
27296b4cac81SBjoern A. Zeeb 	if (error != 0) {
27303f0083c4SBjoern A. Zeeb 		ic_printf(ic, "%s: failed to start hw: %d\n", __func__, error);
27316b4cac81SBjoern A. Zeeb 		mtx_destroy(&lvif->mtx);
27326b4cac81SBjoern A. Zeeb 		free(lvif, M_80211_VAP);
27336b4cac81SBjoern A. Zeeb 		return (NULL);
27346b4cac81SBjoern A. Zeeb 	}
27356b4cac81SBjoern A. Zeeb 
27366b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_add_interface(hw, vif);
27376b4cac81SBjoern A. Zeeb 	if (error != 0) {
27386b4cac81SBjoern A. Zeeb 		IMPROVE();	/* XXX-BZ mo_stop()? */
27393f0083c4SBjoern A. Zeeb 		ic_printf(ic, "%s: failed to add interface: %d\n", __func__, error);
27406b4cac81SBjoern A. Zeeb 		mtx_destroy(&lvif->mtx);
27416b4cac81SBjoern A. Zeeb 		free(lvif, M_80211_VAP);
27426b4cac81SBjoern A. Zeeb 		return (NULL);
27436b4cac81SBjoern A. Zeeb 	}
27446b4cac81SBjoern A. Zeeb 
27458891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
27466b4cac81SBjoern A. Zeeb 	TAILQ_INSERT_TAIL(&lhw->lvif_head, lvif, lvif_entry);
27478891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
27486b4cac81SBjoern A. Zeeb 
27496b4cac81SBjoern A. Zeeb 	/* Set bss_info. */
27506b4cac81SBjoern A. Zeeb 	changed = 0;
27516b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
27526b4cac81SBjoern A. Zeeb 
2753a6042e17SBjoern A. Zeeb 	/* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */
2754a6042e17SBjoern A. Zeeb 	IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element");
2755a6042e17SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2756a6042e17SBjoern A. Zeeb 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
2757a6042e17SBjoern A. Zeeb 
2758a6042e17SBjoern A. Zeeb 		bzero(&txqp, sizeof(txqp));
2759a6042e17SBjoern A. Zeeb 		txqp.cw_min = 15;
2760a6042e17SBjoern A. Zeeb 		txqp.cw_max = 1023;
2761a6042e17SBjoern A. Zeeb 		txqp.txop = 0;
2762a6042e17SBjoern A. Zeeb 		txqp.aifs = 2;
2763a6042e17SBjoern A. Zeeb 		error = lkpi_80211_mo_conf_tx(hw, vif, /* link_id */0, ac, &txqp);
2764a6042e17SBjoern A. Zeeb 		if (error != 0)
2765a6042e17SBjoern A. Zeeb 			ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
2766a6042e17SBjoern A. Zeeb 			    __func__, ac, error);
2767a6042e17SBjoern A. Zeeb 	}
2768a6042e17SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2769a6042e17SBjoern A. Zeeb 	changed = BSS_CHANGED_QOS;
2770a6042e17SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
27716b4cac81SBjoern A. Zeeb 
27726b4cac81SBjoern A. Zeeb 	/* Force MC init. */
27736b4cac81SBjoern A. Zeeb 	lkpi_update_mcast_filter(ic, true);
27746b4cac81SBjoern A. Zeeb 
27756b4cac81SBjoern A. Zeeb 	IMPROVE();
27766b4cac81SBjoern A. Zeeb 
27776b4cac81SBjoern A. Zeeb 	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
27786b4cac81SBjoern A. Zeeb 
27796b4cac81SBjoern A. Zeeb 	/* Override with LinuxKPI method so we can drive mac80211/cfg80211. */
27806b4cac81SBjoern A. Zeeb 	lvif->iv_newstate = vap->iv_newstate;
27816b4cac81SBjoern A. Zeeb 	vap->iv_newstate = lkpi_iv_newstate;
2782d9f59799SBjoern A. Zeeb 	lvif->iv_update_bss = vap->iv_update_bss;
2783d9f59799SBjoern A. Zeeb 	vap->iv_update_bss = lkpi_iv_update_bss;
27846b4cac81SBjoern A. Zeeb 
27856b4cac81SBjoern A. Zeeb 	/* Key management. */
27866b4cac81SBjoern A. Zeeb 	if (lhw->ops->set_key != NULL) {
2787b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
27886b4cac81SBjoern A. Zeeb 		vap->iv_key_set = lkpi_iv_key_set;
27896b4cac81SBjoern A. Zeeb 		vap->iv_key_delete = lkpi_iv_key_delete;
27906b4cac81SBjoern A. Zeeb #endif
27916b4cac81SBjoern A. Zeeb 	}
27926b4cac81SBjoern A. Zeeb 
27939fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
27949fb91463SBjoern A. Zeeb 	/* Stay with the iv_ampdu_rxmax,limit / iv_ampdu_density defaults until later. */
27959fb91463SBjoern A. Zeeb #endif
27969fb91463SBjoern A. Zeeb 
27976b4cac81SBjoern A. Zeeb 	ieee80211_ratectl_init(vap);
27986b4cac81SBjoern A. Zeeb 
27996b4cac81SBjoern A. Zeeb 	/* Complete setup. */
28006b4cac81SBjoern A. Zeeb 	ieee80211_vap_attach(vap, ieee80211_media_change,
28016b4cac81SBjoern A. Zeeb 	    ieee80211_media_status, mac);
28026b4cac81SBjoern A. Zeeb 
28036b4cac81SBjoern A. Zeeb 	if (hw->max_listen_interval == 0)
28046b4cac81SBjoern A. Zeeb 		hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval);
28056b4cac81SBjoern A. Zeeb 	hw->conf.listen_interval = hw->max_listen_interval;
28066b4cac81SBjoern A. Zeeb 	ic->ic_set_channel(ic);
28076b4cac81SBjoern A. Zeeb 
28086b4cac81SBjoern A. Zeeb 	/* XXX-BZ do we need to be able to update these? */
28096b4cac81SBjoern A. Zeeb 	hw->wiphy->frag_threshold = vap->iv_fragthreshold;
28106b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_set_frag_threshold(hw, vap->iv_fragthreshold);
28116b4cac81SBjoern A. Zeeb 	hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
28126b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold);
28136b4cac81SBjoern A. Zeeb 	/* any others? */
28146b4cac81SBjoern A. Zeeb 	IMPROVE();
28156b4cac81SBjoern A. Zeeb 
28166b4cac81SBjoern A. Zeeb 	return (vap);
28176b4cac81SBjoern A. Zeeb }
28186b4cac81SBjoern A. Zeeb 
28194b0af114SBjoern A. Zeeb void
28204b0af114SBjoern A. Zeeb linuxkpi_ieee80211_unregister_hw(struct ieee80211_hw *hw)
28214b0af114SBjoern A. Zeeb {
28224b0af114SBjoern A. Zeeb 
28234b0af114SBjoern A. Zeeb 	wiphy_unregister(hw->wiphy);
28244b0af114SBjoern A. Zeeb 	linuxkpi_ieee80211_ifdetach(hw);
28254b0af114SBjoern A. Zeeb 
28264b0af114SBjoern A. Zeeb 	IMPROVE();
28274b0af114SBjoern A. Zeeb }
28284b0af114SBjoern A. Zeeb 
28294b0af114SBjoern A. Zeeb void
28304b0af114SBjoern A. Zeeb linuxkpi_ieee80211_restart_hw(struct ieee80211_hw *hw)
28314b0af114SBjoern A. Zeeb {
28324b0af114SBjoern A. Zeeb 
28334b0af114SBjoern A. Zeeb 	TODO();
28344b0af114SBjoern A. Zeeb }
28354b0af114SBjoern A. Zeeb 
28366b4cac81SBjoern A. Zeeb static void
28376b4cac81SBjoern A. Zeeb lkpi_ic_vap_delete(struct ieee80211vap *vap)
28386b4cac81SBjoern A. Zeeb {
28396b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
28406b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
28416b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
28426b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
28436b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
28446b4cac81SBjoern A. Zeeb 
28456b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
28466b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
28476b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
28486b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
28496b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
28506b4cac81SBjoern A. Zeeb 
28518891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
28526b4cac81SBjoern A. Zeeb 	TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry);
28538891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
28546b4cac81SBjoern A. Zeeb 
28556b4cac81SBjoern A. Zeeb 	ieee80211_ratectl_deinit(vap);
28566b4cac81SBjoern A. Zeeb 	ieee80211_vap_detach(vap);
2857dbf76919SBjoern A. Zeeb 
2858dbf76919SBjoern A. Zeeb 	IMPROVE("clear up other bits in this state");
2859dbf76919SBjoern A. Zeeb 
2860dbf76919SBjoern A. Zeeb 	lkpi_80211_mo_remove_interface(hw, vif);
2861dbf76919SBjoern A. Zeeb 
28626c38c6b1SBjoern A. Zeeb 	/* Single VAP, so we can do this here. */
28636c38c6b1SBjoern A. Zeeb 	lkpi_80211_mo_stop(hw);
28646c38c6b1SBjoern A. Zeeb 
28656b4cac81SBjoern A. Zeeb 	mtx_destroy(&lvif->mtx);
28666b4cac81SBjoern A. Zeeb 	free(lvif, M_80211_VAP);
28676b4cac81SBjoern A. Zeeb }
28686b4cac81SBjoern A. Zeeb 
28696b4cac81SBjoern A. Zeeb static void
28706b4cac81SBjoern A. Zeeb lkpi_ic_update_mcast(struct ieee80211com *ic)
28716b4cac81SBjoern A. Zeeb {
28726b4cac81SBjoern A. Zeeb 
28736b4cac81SBjoern A. Zeeb 	lkpi_update_mcast_filter(ic, false);
28746b4cac81SBjoern A. Zeeb 	TRACEOK();
28756b4cac81SBjoern A. Zeeb }
28766b4cac81SBjoern A. Zeeb 
28776b4cac81SBjoern A. Zeeb static void
28786b4cac81SBjoern A. Zeeb lkpi_ic_update_promisc(struct ieee80211com *ic)
28796b4cac81SBjoern A. Zeeb {
28806b4cac81SBjoern A. Zeeb 
28816b4cac81SBjoern A. Zeeb 	UNIMPLEMENTED;
28826b4cac81SBjoern A. Zeeb }
28836b4cac81SBjoern A. Zeeb 
28846b4cac81SBjoern A. Zeeb static void
28856b4cac81SBjoern A. Zeeb lkpi_ic_update_chw(struct ieee80211com *ic)
28866b4cac81SBjoern A. Zeeb {
28876b4cac81SBjoern A. Zeeb 
28886b4cac81SBjoern A. Zeeb 	UNIMPLEMENTED;
28896b4cac81SBjoern A. Zeeb }
28906b4cac81SBjoern A. Zeeb 
28916b4cac81SBjoern A. Zeeb /* Start / stop device. */
28926b4cac81SBjoern A. Zeeb static void
28936b4cac81SBjoern A. Zeeb lkpi_ic_parent(struct ieee80211com *ic)
28946b4cac81SBjoern A. Zeeb {
28956b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
28968d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
28976b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
28986b4cac81SBjoern A. Zeeb 	int error;
28998d58a057SBjoern A. Zeeb #endif
29006b4cac81SBjoern A. Zeeb 	bool start_all;
29016b4cac81SBjoern A. Zeeb 
29026b4cac81SBjoern A. Zeeb 	IMPROVE();
29036b4cac81SBjoern A. Zeeb 
29046b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
29058d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
29066b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
29078d58a057SBjoern A. Zeeb #endif
29086b4cac81SBjoern A. Zeeb 	start_all = false;
29096b4cac81SBjoern A. Zeeb 
29108ac540d3SBjoern A. Zeeb 	/* IEEE80211_UNLOCK(ic); */
29118ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
29126b4cac81SBjoern A. Zeeb 	if (ic->ic_nrunning > 0) {
29138d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
29146b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_start(hw);
29156b4cac81SBjoern A. Zeeb 		if (error == 0)
29168d58a057SBjoern A. Zeeb #endif
29176b4cac81SBjoern A. Zeeb 			start_all = true;
29186b4cac81SBjoern A. Zeeb 	} else {
29198d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
29206b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_stop(hw);
29218d58a057SBjoern A. Zeeb #endif
29226b4cac81SBjoern A. Zeeb 	}
29238ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
29248ac540d3SBjoern A. Zeeb 	/* IEEE80211_LOCK(ic); */
29256b4cac81SBjoern A. Zeeb 
29266b4cac81SBjoern A. Zeeb 	if (start_all)
29276b4cac81SBjoern A. Zeeb 		ieee80211_start_all(ic);
29286b4cac81SBjoern A. Zeeb }
29296b4cac81SBjoern A. Zeeb 
29306b4cac81SBjoern A. Zeeb bool
29316b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_is_ie_id_in_ie_buf(const u8 ie, const u8 *ie_ids,
29326b4cac81SBjoern A. Zeeb     size_t ie_ids_len)
29336b4cac81SBjoern A. Zeeb {
29346b4cac81SBjoern A. Zeeb 	int i;
29356b4cac81SBjoern A. Zeeb 
29366b4cac81SBjoern A. Zeeb 	for (i = 0; i < ie_ids_len; i++) {
29376b4cac81SBjoern A. Zeeb 		if (ie == *ie_ids)
29386b4cac81SBjoern A. Zeeb 			return (true);
29396b4cac81SBjoern A. Zeeb 	}
29406b4cac81SBjoern A. Zeeb 
29416b4cac81SBjoern A. Zeeb 	return (false);
29426b4cac81SBjoern A. Zeeb }
29436b4cac81SBjoern A. Zeeb 
29446b4cac81SBjoern A. Zeeb /* Return true if skipped; false if error. */
29456b4cac81SBjoern A. Zeeb bool
29466b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_ie_advance(size_t *xp, const u8 *ies, size_t ies_len)
29476b4cac81SBjoern A. Zeeb {
29486b4cac81SBjoern A. Zeeb 	size_t x;
29496b4cac81SBjoern A. Zeeb 	uint8_t l;
29506b4cac81SBjoern A. Zeeb 
29516b4cac81SBjoern A. Zeeb 	x = *xp;
29526b4cac81SBjoern A. Zeeb 
29536b4cac81SBjoern A. Zeeb 	KASSERT(x < ies_len, ("%s: x %zu ies_len %zu ies %p\n",
29546b4cac81SBjoern A. Zeeb 	    __func__, x, ies_len, ies));
29556b4cac81SBjoern A. Zeeb 	l = ies[x + 1];
29566b4cac81SBjoern A. Zeeb 	x += 2 + l;
29576b4cac81SBjoern A. Zeeb 
29586b4cac81SBjoern A. Zeeb 	if (x > ies_len)
29596b4cac81SBjoern A. Zeeb 		return (false);
29606b4cac81SBjoern A. Zeeb 
29616b4cac81SBjoern A. Zeeb 	*xp = x;
29626b4cac81SBjoern A. Zeeb 	return (true);
29636b4cac81SBjoern A. Zeeb }
29646b4cac81SBjoern A. Zeeb 
2965d9945d78SBjoern A. Zeeb static uint8_t *
2966d9945d78SBjoern A. Zeeb lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
2967d9945d78SBjoern A. Zeeb     uint32_t band_mask, struct ieee80211vap *vap, struct ieee80211_hw *hw)
29686b4cac81SBjoern A. Zeeb {
2969d9945d78SBjoern A. Zeeb 	struct ieee80211_supported_band *supband;
2970d9945d78SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *channels;
29713dd98026SBjoern A. Zeeb 	struct ieee80211com *ic;
2972d9945d78SBjoern A. Zeeb 	const struct ieee80211_channel *chan;
2973d9945d78SBjoern A. Zeeb 	const struct ieee80211_rateset *rs;
2974d9945d78SBjoern A. Zeeb 	uint8_t *pb;
2975d9945d78SBjoern A. Zeeb 	int band, i;
29766b4cac81SBjoern A. Zeeb 
29773dd98026SBjoern A. Zeeb 	ic = vap->iv_ic;
2978d9945d78SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
2979d9945d78SBjoern A. Zeeb 		if ((band_mask & (1 << band)) == 0)
2980d9945d78SBjoern A. Zeeb 			continue;
2981d9945d78SBjoern A. Zeeb 
2982d9945d78SBjoern A. Zeeb 		supband = hw->wiphy->bands[band];
2983d9945d78SBjoern A. Zeeb 		/*
2984d9945d78SBjoern A. Zeeb 		 * This should not happen;
2985d9945d78SBjoern A. Zeeb 		 * band_mask is a bitmask of valid bands to scan on.
2986d9945d78SBjoern A. Zeeb 		 */
2987d9945d78SBjoern A. Zeeb 		if (supband == NULL || supband->n_channels == 0)
2988d9945d78SBjoern A. Zeeb 			continue;
2989d9945d78SBjoern A. Zeeb 
2990d9945d78SBjoern A. Zeeb 		/* Find a first channel to get the mode and rates from. */
2991d9945d78SBjoern A. Zeeb 		channels = supband->channels;
2992d9945d78SBjoern A. Zeeb 		chan = NULL;
2993d9945d78SBjoern A. Zeeb 		for (i = 0; i < supband->n_channels; i++) {
2994d9945d78SBjoern A. Zeeb 
2995d9945d78SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED)
2996d9945d78SBjoern A. Zeeb 				continue;
2997d9945d78SBjoern A. Zeeb 
29983dd98026SBjoern A. Zeeb 			chan = ieee80211_find_channel(ic,
2999d9945d78SBjoern A. Zeeb 			    channels[i].center_freq, 0);
3000d9945d78SBjoern A. Zeeb 			if (chan != NULL)
3001d9945d78SBjoern A. Zeeb 				break;
3002d9945d78SBjoern A. Zeeb 		}
3003d9945d78SBjoern A. Zeeb 
3004d9945d78SBjoern A. Zeeb 		/* This really should not happen. */
3005d9945d78SBjoern A. Zeeb 		if (chan == NULL)
3006d9945d78SBjoern A. Zeeb 			continue;
3007d9945d78SBjoern A. Zeeb 
3008d9945d78SBjoern A. Zeeb 		pb = p;
30093dd98026SBjoern A. Zeeb 		rs = ieee80211_get_suprates(ic, chan);	/* calls chan2mode */
3010d9945d78SBjoern A. Zeeb 		p = ieee80211_add_rates(p, rs);
3011d9945d78SBjoern A. Zeeb 		p = ieee80211_add_xrates(p, rs);
3012d9945d78SBjoern A. Zeeb 
30133dd98026SBjoern A. Zeeb #if defined(LKPI_80211_HT)
30143dd98026SBjoern A. Zeeb 		if ((vap->iv_flags_ht & IEEE80211_FHT_HT) != 0) {
30153dd98026SBjoern A. Zeeb 			struct ieee80211_channel *c;
30163dd98026SBjoern A. Zeeb 
30173dd98026SBjoern A. Zeeb 			c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
30183dd98026SBjoern A. Zeeb 			    vap->iv_flags_ht);
30193dd98026SBjoern A. Zeeb 			p = ieee80211_add_htcap_ch(p, vap, c);
30203dd98026SBjoern A. Zeeb 		}
30213dd98026SBjoern A. Zeeb #endif
30223dd98026SBjoern A. Zeeb #if defined(LKPI_80211_VHT)
30233dd98026SBjoern A. Zeeb 		if ((vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
30243dd98026SBjoern A. Zeeb 			struct ieee80211_channel *c;
30253dd98026SBjoern A. Zeeb 
30263dd98026SBjoern A. Zeeb 			c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
30273dd98026SBjoern A. Zeeb 			    vap->iv_flags_ht);
30283dd98026SBjoern A. Zeeb 			c = ieee80211_vht_adjust_channel(ic, c,
30293dd98026SBjoern A. Zeeb 			    vap->iv_vht_flags);
30303dd98026SBjoern A. Zeeb 			p = ieee80211_add_vhtcap_ch(p, vap, c);
30313dd98026SBjoern A. Zeeb 		}
30323dd98026SBjoern A. Zeeb #endif
30333dd98026SBjoern A. Zeeb 
3034d9945d78SBjoern A. Zeeb 		scan_ies->ies[band] = pb;
3035d9945d78SBjoern A. Zeeb 		scan_ies->len[band] = p - pb;
3036d9945d78SBjoern A. Zeeb 	}
3037d9945d78SBjoern A. Zeeb 
3038d9945d78SBjoern A. Zeeb 	/* Add common_ies */
3039d9945d78SBjoern A. Zeeb 	pb = p;
3040d9945d78SBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_WPA1) != 0 &&
3041d9945d78SBjoern A. Zeeb 	    vap->iv_wpa_ie != NULL) {
3042d9945d78SBjoern A. Zeeb 		memcpy(p, vap->iv_wpa_ie, 2 + vap->iv_wpa_ie[1]);
3043d9945d78SBjoern A. Zeeb 		p += 2 + vap->iv_wpa_ie[1];
3044d9945d78SBjoern A. Zeeb 	}
3045d9945d78SBjoern A. Zeeb 	if (vap->iv_appie_probereq != NULL) {
3046d9945d78SBjoern A. Zeeb 		memcpy(p, vap->iv_appie_probereq->ie_data,
3047d9945d78SBjoern A. Zeeb 		    vap->iv_appie_probereq->ie_len);
3048d9945d78SBjoern A. Zeeb 		p += vap->iv_appie_probereq->ie_len;
3049d9945d78SBjoern A. Zeeb 	}
3050d9945d78SBjoern A. Zeeb 	scan_ies->common_ies = pb;
3051d9945d78SBjoern A. Zeeb 	scan_ies->common_ie_len = p - pb;
3052d9945d78SBjoern A. Zeeb 
3053d9945d78SBjoern A. Zeeb 	return (p);
30546b4cac81SBjoern A. Zeeb }
30556b4cac81SBjoern A. Zeeb 
30566b4cac81SBjoern A. Zeeb static void
30576b4cac81SBjoern A. Zeeb lkpi_ic_scan_start(struct ieee80211com *ic)
30586b4cac81SBjoern A. Zeeb {
30596b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
30606b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
30616b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
30626b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
30636b4cac81SBjoern A. Zeeb 	struct ieee80211_scan_state *ss;
30646b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
30656b4cac81SBjoern A. Zeeb 	int error;
30668ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
30676b4cac81SBjoern A. Zeeb 
30686b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
30698ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
3070a486fbbdSBjoern A. Zeeb 	if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
30716b4cac81SBjoern A. Zeeb 		/* A scan is still running. */
30728ac540d3SBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_UNLOCK(lhw);
30736b4cac81SBjoern A. Zeeb 		return;
30746b4cac81SBjoern A. Zeeb 	}
30758ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
30768ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
30776b4cac81SBjoern A. Zeeb 
30786b4cac81SBjoern A. Zeeb 	ss = ic->ic_scan;
30796b4cac81SBjoern A. Zeeb 	vap = ss->ss_vap;
30806b4cac81SBjoern A. Zeeb 	if (vap->iv_state != IEEE80211_S_SCAN) {
3081d3ef7fb4SBjoern A. Zeeb 		IMPROVE("We need to be able to scan if not in S_SCAN");
30826b4cac81SBjoern A. Zeeb 		return;
30836b4cac81SBjoern A. Zeeb 	}
30846b4cac81SBjoern A. Zeeb 
30856b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
30868ac540d3SBjoern A. Zeeb 	if (!is_hw_scan) {
3087a486fbbdSBjoern A. Zeeb 		/* If hw_scan is cleared clear FEXT_SCAN_OFFLOAD too. */
3088a486fbbdSBjoern A. Zeeb 		vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
3089d3ef7fb4SBjoern A. Zeeb sw_scan:
30906b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
30916b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
3092086be6a8SBjoern A. Zeeb 
3093086be6a8SBjoern A. Zeeb 		if (vap->iv_state == IEEE80211_S_SCAN)
3094086be6a8SBjoern A. Zeeb 			lkpi_hw_conf_idle(hw, false);
3095086be6a8SBjoern A. Zeeb 
30966b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_sw_scan_start(hw, vif, vif->addr);
30976b4cac81SBjoern A. Zeeb 		/* net80211::scan_start() handled PS for us. */
30986b4cac81SBjoern A. Zeeb 		IMPROVE();
30996b4cac81SBjoern A. Zeeb 		/* XXX Also means it is too late to flush queues?
31006b4cac81SBjoern A. Zeeb 		 * need to check iv_sta_ps or overload? */
31016b4cac81SBjoern A. Zeeb 		/* XXX want to adjust ss end time/ maxdwell? */
31026b4cac81SBjoern A. Zeeb 
31036b4cac81SBjoern A. Zeeb 	} else {
31046b4cac81SBjoern A. Zeeb 		struct ieee80211_channel *c;
31056b4cac81SBjoern A. Zeeb 		struct ieee80211_scan_request *hw_req;
31066b4cac81SBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *lc, **cpp;
31076b4cac81SBjoern A. Zeeb 		struct cfg80211_ssid *ssids;
31086b4cac81SBjoern A. Zeeb 		struct cfg80211_scan_6ghz_params *s6gp;
31096b4cac81SBjoern A. Zeeb 		size_t chan_len, nchan, ssids_len, s6ghzlen;
3110d9945d78SBjoern A. Zeeb 		int band, i, ssid_count, common_ie_len;
3111d9945d78SBjoern A. Zeeb 		uint32_t band_mask;
3112d9945d78SBjoern A. Zeeb 		uint8_t *ie, *ieend;
31133206587aSBjoern A. Zeeb 		bool running;
3114d9945d78SBjoern A. Zeeb 
3115d9945d78SBjoern A. Zeeb 		ssid_count = min(ss->ss_nssid, hw->wiphy->max_scan_ssids);
3116d9945d78SBjoern A. Zeeb 		ssids_len = ssid_count * sizeof(*ssids);
31176b4cac81SBjoern A. Zeeb 		s6ghzlen = 0 * (sizeof(*s6gp));			/* XXX-BZ */
31186b4cac81SBjoern A. Zeeb 
3119d9945d78SBjoern A. Zeeb 		band_mask = 0;
31206b4cac81SBjoern A. Zeeb 		nchan = 0;
3121d9945d78SBjoern A. Zeeb 		for (i = ss->ss_next; i < ss->ss_last; i++) {
31226b4cac81SBjoern A. Zeeb 			nchan++;
3123d9945d78SBjoern A. Zeeb 			band = lkpi_net80211_chan_to_nl80211_band(
3124d9945d78SBjoern A. Zeeb 			    ss->ss_chans[ss->ss_next + i]);
3125d9945d78SBjoern A. Zeeb 			band_mask |= (1 << band);
3126d9945d78SBjoern A. Zeeb 		}
31273206587aSBjoern A. Zeeb 
31283206587aSBjoern A. Zeeb 		if (!ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
31293206587aSBjoern A. Zeeb 			IMPROVE("individual band scans not yet supported, only scanning first band");
31303206587aSBjoern A. Zeeb 			/* In theory net80211 should drive this. */
31313206587aSBjoern A. Zeeb 			/* Probably we need to add local logic for now;
31323206587aSBjoern A. Zeeb 			 * need to deal with scan_complete
31333206587aSBjoern A. Zeeb 			 * and cancel_scan and keep local state.
31343206587aSBjoern A. Zeeb 			 * Also cut the nchan down above.
31353206587aSBjoern A. Zeeb 			 */
31363206587aSBjoern A. Zeeb 			/* XXX-BZ ath10k does not set this but still does it? &$%^ */
31373206587aSBjoern A. Zeeb 		}
31383206587aSBjoern A. Zeeb 
31396b4cac81SBjoern A. Zeeb 		chan_len = nchan * (sizeof(lc) + sizeof(*lc));
31406b4cac81SBjoern A. Zeeb 
3141d9945d78SBjoern A. Zeeb 		common_ie_len = 0;
3142d9945d78SBjoern A. Zeeb 		if ((vap->iv_flags & IEEE80211_F_WPA1) != 0 &&
3143d9945d78SBjoern A. Zeeb 		    vap->iv_wpa_ie != NULL)
3144d9945d78SBjoern A. Zeeb 			common_ie_len += vap->iv_wpa_ie[1];
3145d9945d78SBjoern A. Zeeb 		if (vap->iv_appie_probereq != NULL)
3146d9945d78SBjoern A. Zeeb 			common_ie_len += vap->iv_appie_probereq->ie_len;
3147d9945d78SBjoern A. Zeeb 
3148d9945d78SBjoern A. Zeeb 		/* We would love to check this at an earlier stage... */
3149d9945d78SBjoern A. Zeeb 		if (common_ie_len >  hw->wiphy->max_scan_ie_len) {
3150d9945d78SBjoern A. Zeeb 			ic_printf(ic, "WARNING: %s: common_ie_len %d > "
3151d9945d78SBjoern A. Zeeb 			    "wiphy->max_scan_ie_len %d\n", __func__,
3152d9945d78SBjoern A. Zeeb 			    common_ie_len, hw->wiphy->max_scan_ie_len);
3153d9945d78SBjoern A. Zeeb 		}
3154d9945d78SBjoern A. Zeeb 
31553206587aSBjoern A. Zeeb 		hw_req = malloc(sizeof(*hw_req) + ssids_len +
3156d9945d78SBjoern A. Zeeb 		    s6ghzlen + chan_len + lhw->supbands * lhw->scan_ie_len +
3157d9945d78SBjoern A. Zeeb 		    common_ie_len, M_LKPI80211, M_WAITOK | M_ZERO);
31586b4cac81SBjoern A. Zeeb 
31596b4cac81SBjoern A. Zeeb 		hw_req->req.flags = 0;			/* XXX ??? */
31606b4cac81SBjoern A. Zeeb 		/* hw_req->req.wdev */
31616b4cac81SBjoern A. Zeeb 		hw_req->req.wiphy = hw->wiphy;
31626b4cac81SBjoern A. Zeeb 		hw_req->req.no_cck = false;		/* XXX */
31636b4cac81SBjoern A. Zeeb #if 0
31646b4cac81SBjoern A. Zeeb 		/* This seems to pessimise default scanning behaviour. */
31656b4cac81SBjoern A. Zeeb 		hw_req->req.duration_mandatory = TICKS_2_USEC(ss->ss_mindwell);
31666b4cac81SBjoern A. Zeeb 		hw_req->req.duration = TICKS_2_USEC(ss->ss_maxdwell);
31676b4cac81SBjoern A. Zeeb #endif
31686b4cac81SBjoern A. Zeeb #ifdef __notyet__
31696b4cac81SBjoern A. Zeeb 		hw_req->req.flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
31706b4cac81SBjoern A. Zeeb 		memcpy(hw_req->req.mac_addr, xxx, IEEE80211_ADDR_LEN);
31716b4cac81SBjoern A. Zeeb 		memset(hw_req->req.mac_addr_mask, 0xxx, IEEE80211_ADDR_LEN);
31726b4cac81SBjoern A. Zeeb #endif
3173e1e90be0SBjoern A. Zeeb 		eth_broadcast_addr(hw_req->req.bssid);
31746b4cac81SBjoern A. Zeeb 
31756b4cac81SBjoern A. Zeeb 		hw_req->req.n_channels = nchan;
31766b4cac81SBjoern A. Zeeb 		cpp = (struct linuxkpi_ieee80211_channel **)(hw_req + 1);
31776b4cac81SBjoern A. Zeeb 		lc = (struct linuxkpi_ieee80211_channel *)(cpp + nchan);
31786b4cac81SBjoern A. Zeeb 		for (i = 0; i < nchan; i++) {
31796b4cac81SBjoern A. Zeeb 			*(cpp + i) =
31806b4cac81SBjoern A. Zeeb 			    (struct linuxkpi_ieee80211_channel *)(lc + i);
31816b4cac81SBjoern A. Zeeb 		}
31826b4cac81SBjoern A. Zeeb 		for (i = 0; i < nchan; i++) {
31836b4cac81SBjoern A. Zeeb 			c = ss->ss_chans[ss->ss_next + i];
31846b4cac81SBjoern A. Zeeb 
31856b4cac81SBjoern A. Zeeb 			lc->hw_value = c->ic_ieee;
31863206587aSBjoern A. Zeeb 			lc->center_freq = c->ic_freq;	/* XXX */
31876b4cac81SBjoern A. Zeeb 			/* lc->flags */
31886b4cac81SBjoern A. Zeeb 			lc->band = lkpi_net80211_chan_to_nl80211_band(c);
31896b4cac81SBjoern A. Zeeb 			lc->max_power = c->ic_maxpower;
31906b4cac81SBjoern A. Zeeb 			/* lc-> ... */
31916b4cac81SBjoern A. Zeeb 			lc++;
31926b4cac81SBjoern A. Zeeb 		}
31936b4cac81SBjoern A. Zeeb 
3194d9945d78SBjoern A. Zeeb 		hw_req->req.n_ssids = ssid_count;
31956b4cac81SBjoern A. Zeeb 		if (hw_req->req.n_ssids > 0) {
31966b4cac81SBjoern A. Zeeb 			ssids = (struct cfg80211_ssid *)lc;
31976b4cac81SBjoern A. Zeeb 			hw_req->req.ssids = ssids;
3198d9945d78SBjoern A. Zeeb 			for (i = 0; i < ssid_count; i++) {
31996b4cac81SBjoern A. Zeeb 				ssids->ssid_len = ss->ss_ssid[i].len;
32006b4cac81SBjoern A. Zeeb 				memcpy(ssids->ssid, ss->ss_ssid[i].ssid,
32016b4cac81SBjoern A. Zeeb 				    ss->ss_ssid[i].len);
32026b4cac81SBjoern A. Zeeb 				ssids++;
32036b4cac81SBjoern A. Zeeb 			}
32046b4cac81SBjoern A. Zeeb 			s6gp = (struct cfg80211_scan_6ghz_params *)ssids;
32056b4cac81SBjoern A. Zeeb 		} else {
32066b4cac81SBjoern A. Zeeb 			s6gp = (struct cfg80211_scan_6ghz_params *)lc;
32076b4cac81SBjoern A. Zeeb 		}
32086b4cac81SBjoern A. Zeeb 
32096b4cac81SBjoern A. Zeeb 		/* 6GHz one day. */
32106b4cac81SBjoern A. Zeeb 		hw_req->req.n_6ghz_params = 0;
32116b4cac81SBjoern A. Zeeb 		hw_req->req.scan_6ghz_params = NULL;
32126b4cac81SBjoern A. Zeeb 		hw_req->req.scan_6ghz = false;	/* Weird boolean; not what you think. */
32136b4cac81SBjoern A. Zeeb 		/* s6gp->... */
32146b4cac81SBjoern A. Zeeb 
3215d9945d78SBjoern A. Zeeb 		ie = ieend = (uint8_t *)s6gp;
3216d9945d78SBjoern A. Zeeb 		/* Copy per-band IEs, copy common IEs */
3217d9945d78SBjoern A. Zeeb 		ieend = lkpi_scan_ies_add(ie, &hw_req->ies, band_mask, vap, hw);
3218d9945d78SBjoern A. Zeeb 		hw_req->req.ie = ie;
3219d9945d78SBjoern A. Zeeb 		hw_req->req.ie_len = ieend - ie;
3220d9945d78SBjoern A. Zeeb 
32216b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
32226b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
32233206587aSBjoern A. Zeeb 
32243206587aSBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_LOCK(lhw);
32253206587aSBjoern A. Zeeb 		/* Re-check under lock. */
32263206587aSBjoern A. Zeeb 		running = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
32273206587aSBjoern A. Zeeb 		if (!running) {
32283206587aSBjoern A. Zeeb 			KASSERT(lhw->hw_req == NULL, ("%s: ic %p lhw %p hw_req %p "
32293206587aSBjoern A. Zeeb 			    "!= NULL\n", __func__, ic, lhw, lhw->hw_req));
32303206587aSBjoern A. Zeeb 
32313206587aSBjoern A. Zeeb 			lhw->scan_flags |= LKPI_LHW_SCAN_RUNNING;
32323206587aSBjoern A. Zeeb 			lhw->hw_req = hw_req;
32333206587aSBjoern A. Zeeb 		}
32343206587aSBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_UNLOCK(lhw);
32353206587aSBjoern A. Zeeb 		if (running) {
32363206587aSBjoern A. Zeeb 			free(hw_req, M_LKPI80211);
32373206587aSBjoern A. Zeeb 			return;
32383206587aSBjoern A. Zeeb 		}
32393206587aSBjoern A. Zeeb 
32406b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_hw_scan(hw, vif, hw_req);
32416b4cac81SBjoern A. Zeeb 		if (error != 0) {
3242d9945d78SBjoern A. Zeeb 			ieee80211_cancel_scan(vap);
3243d9945d78SBjoern A. Zeeb 
32443206587aSBjoern A. Zeeb 			/*
32453206587aSBjoern A. Zeeb 			 * ieee80211_scan_completed must be called in either
32463206587aSBjoern A. Zeeb 			 * case of error or none.  So let the free happen there
32473206587aSBjoern A. Zeeb 			 * and only there.
32483206587aSBjoern A. Zeeb 			 * That would be fine in theory but in practice drivers
32493206587aSBjoern A. Zeeb 			 * behave differently:
32503206587aSBjoern A. Zeeb 			 * ath10k does not return hw_scan until after scan_complete
32513206587aSBjoern A. Zeeb 			 *        and can then still return an error.
32523206587aSBjoern A. Zeeb 			 * rtw88 can return 1 or -EBUSY without scan_complete
32533206587aSBjoern A. Zeeb 			 * iwlwifi can return various errors before scan starts
32543206587aSBjoern A. Zeeb 			 * ...
32553206587aSBjoern A. Zeeb 			 * So we cannot rely on that behaviour and have to check
32563206587aSBjoern A. Zeeb 			 * and balance between both code paths.
32573206587aSBjoern A. Zeeb 			 */
32583206587aSBjoern A. Zeeb 			LKPI_80211_LHW_SCAN_LOCK(lhw);
32593206587aSBjoern A. Zeeb 			if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
32603206587aSBjoern A. Zeeb 				free(lhw->hw_req, M_LKPI80211);
32616b4cac81SBjoern A. Zeeb 				lhw->hw_req = NULL;
32623206587aSBjoern A. Zeeb 				lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
32633206587aSBjoern A. Zeeb 			}
32643206587aSBjoern A. Zeeb 			LKPI_80211_LHW_SCAN_UNLOCK(lhw);
3265d3ef7fb4SBjoern A. Zeeb 
3266d3ef7fb4SBjoern A. Zeeb 			/*
3267d3ef7fb4SBjoern A. Zeeb 			 * XXX-SIGH magic number.
3268d3ef7fb4SBjoern A. Zeeb 			 * rtw88 has a magic "return 1" if offloading scan is
3269d3ef7fb4SBjoern A. Zeeb 			 * not possible.  Fall back to sw scan in that case.
3270d3ef7fb4SBjoern A. Zeeb 			 */
3271196cfd0bSBjoern A. Zeeb 			if (error == 1) {
32728ac540d3SBjoern A. Zeeb 				LKPI_80211_LHW_SCAN_LOCK(lhw);
3273a486fbbdSBjoern A. Zeeb 				lhw->scan_flags &= ~LKPI_LHW_SCAN_HW;
32748ac540d3SBjoern A. Zeeb 				LKPI_80211_LHW_SCAN_UNLOCK(lhw);
32753206587aSBjoern A. Zeeb 				/*
32763206587aSBjoern A. Zeeb 				 * XXX If we clear this now and later a driver
32773206587aSBjoern A. Zeeb 				 * thinks it * can do a hw_scan again, we will
32783206587aSBjoern A. Zeeb 				 * currently not re-enable it?
32793206587aSBjoern A. Zeeb 				 */
32803206587aSBjoern A. Zeeb 				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
3281196cfd0bSBjoern A. Zeeb 				ieee80211_start_scan(vap,
3282196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_ACTIVE |
3283196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_NOPICK |
3284196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_ONCE,
3285196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_FOREVER,
3286196cfd0bSBjoern A. Zeeb 				    ss->ss_mindwell ? ss->ss_mindwell : msecs_to_ticks(20),
3287196cfd0bSBjoern A. Zeeb 				    ss->ss_maxdwell ? ss->ss_maxdwell : msecs_to_ticks(200),
3288196cfd0bSBjoern A. Zeeb 				    vap->iv_des_nssid, vap->iv_des_ssid);
3289d3ef7fb4SBjoern A. Zeeb 				goto sw_scan;
3290196cfd0bSBjoern A. Zeeb 			}
3291d3ef7fb4SBjoern A. Zeeb 
3292d3ef7fb4SBjoern A. Zeeb 			ic_printf(ic, "ERROR: %s: hw_scan returned %d\n",
3293d3ef7fb4SBjoern A. Zeeb 			    __func__, error);
32946b4cac81SBjoern A. Zeeb 		}
32956b4cac81SBjoern A. Zeeb 	}
32966b4cac81SBjoern A. Zeeb }
32976b4cac81SBjoern A. Zeeb 
32986b4cac81SBjoern A. Zeeb static void
32996b4cac81SBjoern A. Zeeb lkpi_ic_scan_end(struct ieee80211com *ic)
33006b4cac81SBjoern A. Zeeb {
33016b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
33028ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
33036b4cac81SBjoern A. Zeeb 
33046b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
33058ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
3306a486fbbdSBjoern A. Zeeb 	if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) == 0) {
33078ac540d3SBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33086b4cac81SBjoern A. Zeeb 		return;
33096b4cac81SBjoern A. Zeeb 	}
33108ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
33118ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33126b4cac81SBjoern A. Zeeb 
33138ac540d3SBjoern A. Zeeb 	if (!is_hw_scan) {
3314a486fbbdSBjoern A. Zeeb 		struct ieee80211_scan_state *ss;
3315a486fbbdSBjoern A. Zeeb 		struct ieee80211vap *vap;
33166b4cac81SBjoern A. Zeeb 		struct ieee80211_hw *hw;
33176b4cac81SBjoern A. Zeeb 		struct lkpi_vif *lvif;
33186b4cac81SBjoern A. Zeeb 		struct ieee80211_vif *vif;
33196b4cac81SBjoern A. Zeeb 
3320a486fbbdSBjoern A. Zeeb 		ss = ic->ic_scan;
3321a486fbbdSBjoern A. Zeeb 		vap = ss->ss_vap;
33226b4cac81SBjoern A. Zeeb 		hw = LHW_TO_HW(lhw);
33236b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
33246b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
3325a486fbbdSBjoern A. Zeeb 
33266b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_sw_scan_complete(hw, vif);
33276b4cac81SBjoern A. Zeeb 
33286b4cac81SBjoern A. Zeeb 		/* Send PS to stop buffering if n80211 does not for us? */
3329086be6a8SBjoern A. Zeeb 
3330086be6a8SBjoern A. Zeeb 		if (vap->iv_state == IEEE80211_S_SCAN)
3331086be6a8SBjoern A. Zeeb 			lkpi_hw_conf_idle(hw, true);
33326b4cac81SBjoern A. Zeeb 	}
33336b4cac81SBjoern A. Zeeb }
33346b4cac81SBjoern A. Zeeb 
33356b4cac81SBjoern A. Zeeb static void
3336a486fbbdSBjoern A. Zeeb lkpi_ic_scan_curchan(struct ieee80211_scan_state *ss,
3337a486fbbdSBjoern A. Zeeb     unsigned long maxdwell)
3338a486fbbdSBjoern A. Zeeb {
3339a486fbbdSBjoern A. Zeeb 	struct lkpi_hw *lhw;
33408ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
3341a486fbbdSBjoern A. Zeeb 
3342a486fbbdSBjoern A. Zeeb 	lhw = ss->ss_ic->ic_softc;
33438ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
33448ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
33458ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33468ac540d3SBjoern A. Zeeb 	if (!is_hw_scan)
3347a486fbbdSBjoern A. Zeeb 		lhw->ic_scan_curchan(ss, maxdwell);
3348a486fbbdSBjoern A. Zeeb }
3349a486fbbdSBjoern A. Zeeb 
3350a486fbbdSBjoern A. Zeeb static void
3351a486fbbdSBjoern A. Zeeb lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss)
3352a486fbbdSBjoern A. Zeeb {
3353a486fbbdSBjoern A. Zeeb 	struct lkpi_hw *lhw;
33548ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
3355a486fbbdSBjoern A. Zeeb 
3356a486fbbdSBjoern A. Zeeb 	lhw = ss->ss_ic->ic_softc;
33578ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
33588ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
33598ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33608ac540d3SBjoern A. Zeeb 	if (!is_hw_scan)
3361a486fbbdSBjoern A. Zeeb 		lhw->ic_scan_mindwell(ss);
3362a486fbbdSBjoern A. Zeeb }
3363a486fbbdSBjoern A. Zeeb 
3364a486fbbdSBjoern A. Zeeb static void
33656b4cac81SBjoern A. Zeeb lkpi_ic_set_channel(struct ieee80211com *ic)
33666b4cac81SBjoern A. Zeeb {
33676b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
33686b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
3369b2cf3c21SBjoern A. Zeeb 	struct ieee80211_channel *c;
3370b2cf3c21SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
33716b4cac81SBjoern A. Zeeb 	int error;
33728ac540d3SBjoern A. Zeeb 	bool hw_scan_running;
33736b4cac81SBjoern A. Zeeb 
33746b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
3375b2cf3c21SBjoern A. Zeeb 
3376b2cf3c21SBjoern A. Zeeb 	/* If we do not support (*config)() save us the work. */
3377b2cf3c21SBjoern A. Zeeb 	if (lhw->ops->config == NULL)
33786b4cac81SBjoern A. Zeeb 		return;
33796b4cac81SBjoern A. Zeeb 
3380a486fbbdSBjoern A. Zeeb 	/* If we have a hw_scan running do not switch channels. */
33818ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
33828ac540d3SBjoern A. Zeeb 	hw_scan_running =
33838ac540d3SBjoern A. Zeeb 	    (lhw->scan_flags & (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW)) ==
33848ac540d3SBjoern A. Zeeb 		(LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW);
33858ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33868ac540d3SBjoern A. Zeeb 	if (hw_scan_running)
3387a486fbbdSBjoern A. Zeeb 		return;
3388a486fbbdSBjoern A. Zeeb 
3389b2cf3c21SBjoern A. Zeeb 	c = ic->ic_curchan;
3390b2cf3c21SBjoern A. Zeeb 	if (c == NULL || c == IEEE80211_CHAN_ANYC) {
33916b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: c %p ops->config %p\n", __func__,
33926b4cac81SBjoern A. Zeeb 		    c, lhw->ops->config);
33936b4cac81SBjoern A. Zeeb 		return;
33946b4cac81SBjoern A. Zeeb 	}
33956b4cac81SBjoern A. Zeeb 
33966b4cac81SBjoern A. Zeeb 	chan = lkpi_find_lkpi80211_chan(lhw, c);
33976b4cac81SBjoern A. Zeeb 	if (chan == NULL) {
33986b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: c %p chan %p\n", __func__,
33996b4cac81SBjoern A. Zeeb 		    c, chan);
34006b4cac81SBjoern A. Zeeb 		return;
34016b4cac81SBjoern A. Zeeb 	}
34026b4cac81SBjoern A. Zeeb 
34036b4cac81SBjoern A. Zeeb 	/* XXX max power for scanning? */
34046b4cac81SBjoern A. Zeeb 	IMPROVE();
34056b4cac81SBjoern A. Zeeb 
34066b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
34074a07abdeSBjoern A. Zeeb 	cfg80211_chandef_create(&hw->conf.chandef, chan,
34089fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
34099fb91463SBjoern A. Zeeb 	    (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
34109fb91463SBjoern A. Zeeb #endif
34114a07abdeSBjoern A. Zeeb 	    NL80211_CHAN_NO_HT);
34126b4cac81SBjoern A. Zeeb 
34136b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_CHANNEL);
34146b4cac81SBjoern A. Zeeb 	if (error != 0 && error != EOPNOTSUPP) {
34156b4cac81SBjoern A. Zeeb 		ic_printf(ic, "ERROR: %s: config %#0x returned %d\n",
34166b4cac81SBjoern A. Zeeb 		    __func__, IEEE80211_CONF_CHANGE_CHANNEL, error);
34176b4cac81SBjoern A. Zeeb 		/* XXX should we unroll to the previous chandef? */
34186b4cac81SBjoern A. Zeeb 		IMPROVE();
34196b4cac81SBjoern A. Zeeb 	} else {
34206b4cac81SBjoern A. Zeeb 		/* Update radiotap channels as well. */
34216b4cac81SBjoern A. Zeeb 		lhw->rtap_tx.wt_chan_freq = htole16(c->ic_freq);
34226b4cac81SBjoern A. Zeeb 		lhw->rtap_tx.wt_chan_flags = htole16(c->ic_flags);
34236b4cac81SBjoern A. Zeeb 		lhw->rtap_rx.wr_chan_freq = htole16(c->ic_freq);
34246b4cac81SBjoern A. Zeeb 		lhw->rtap_rx.wr_chan_flags = htole16(c->ic_flags);
34256b4cac81SBjoern A. Zeeb 	}
34266b4cac81SBjoern A. Zeeb 
34276b4cac81SBjoern A. Zeeb 	/* Currently PS is hard coded off! Not sure it belongs here. */
34286b4cac81SBjoern A. Zeeb 	IMPROVE();
34296b4cac81SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, SUPPORTS_PS) &&
34306b4cac81SBjoern A. Zeeb 	    (hw->conf.flags & IEEE80211_CONF_PS) != 0) {
34316b4cac81SBjoern A. Zeeb 		hw->conf.flags &= ~IEEE80211_CONF_PS;
34326b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_PS);
34336b4cac81SBjoern A. Zeeb 		if (error != 0 && error != EOPNOTSUPP)
34346b4cac81SBjoern A. Zeeb 			ic_printf(ic, "ERROR: %s: config %#0x returned "
34356b4cac81SBjoern A. Zeeb 			    "%d\n", __func__, IEEE80211_CONF_CHANGE_PS,
34366b4cac81SBjoern A. Zeeb 			    error);
34376b4cac81SBjoern A. Zeeb 	}
34386b4cac81SBjoern A. Zeeb }
34396b4cac81SBjoern A. Zeeb 
34406b4cac81SBjoern A. Zeeb static struct ieee80211_node *
34416b4cac81SBjoern A. Zeeb lkpi_ic_node_alloc(struct ieee80211vap *vap,
34426b4cac81SBjoern A. Zeeb     const uint8_t mac[IEEE80211_ADDR_LEN])
34436b4cac81SBjoern A. Zeeb {
34446b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
34456b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
34464f61ef8bSBjoern A. Zeeb 	struct ieee80211_node *ni;
34476b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
34486b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
34496b4cac81SBjoern A. Zeeb 
34506b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
34516b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
34526b4cac81SBjoern A. Zeeb 
34536b4cac81SBjoern A. Zeeb 	/* We keep allocations de-coupled so we can deal with the two worlds. */
34544f61ef8bSBjoern A. Zeeb 	if (lhw->ic_node_alloc == NULL)
34554f61ef8bSBjoern A. Zeeb 		return (NULL);
34564f61ef8bSBjoern A. Zeeb 
34576b4cac81SBjoern A. Zeeb 	ni = lhw->ic_node_alloc(vap, mac);
34586b4cac81SBjoern A. Zeeb 	if (ni == NULL)
34596b4cac81SBjoern A. Zeeb 		return (NULL);
34606b4cac81SBjoern A. Zeeb 
34616b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
34624f61ef8bSBjoern A. Zeeb 	lsta = lkpi_lsta_alloc(vap, mac, hw, ni);
34636b4cac81SBjoern A. Zeeb 	if (lsta == NULL) {
34646b4cac81SBjoern A. Zeeb 		if (lhw->ic_node_free != NULL)
34656b4cac81SBjoern A. Zeeb 			lhw->ic_node_free(ni);
34666b4cac81SBjoern A. Zeeb 		return (NULL);
34676b4cac81SBjoern A. Zeeb 	}
34686b4cac81SBjoern A. Zeeb 
34696b4cac81SBjoern A. Zeeb 	return (ni);
34706b4cac81SBjoern A. Zeeb }
34716b4cac81SBjoern A. Zeeb 
34726b4cac81SBjoern A. Zeeb static int
34736b4cac81SBjoern A. Zeeb lkpi_ic_node_init(struct ieee80211_node *ni)
34746b4cac81SBjoern A. Zeeb {
34756b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
34766b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
34776b4cac81SBjoern A. Zeeb 	int error;
34786b4cac81SBjoern A. Zeeb 
34796b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
34806b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
34816b4cac81SBjoern A. Zeeb 
34826b4cac81SBjoern A. Zeeb 	if (lhw->ic_node_init != NULL) {
34836b4cac81SBjoern A. Zeeb 		error = lhw->ic_node_init(ni);
34846b4cac81SBjoern A. Zeeb 		if (error != 0)
34856b4cac81SBjoern A. Zeeb 			return (error);
34866b4cac81SBjoern A. Zeeb 	}
34876b4cac81SBjoern A. Zeeb 
34886b4cac81SBjoern A. Zeeb 	/* XXX-BZ Sync other state over. */
34896b4cac81SBjoern A. Zeeb 	IMPROVE();
34906b4cac81SBjoern A. Zeeb 
34916b4cac81SBjoern A. Zeeb 	return (0);
34926b4cac81SBjoern A. Zeeb }
34936b4cac81SBjoern A. Zeeb 
34946b4cac81SBjoern A. Zeeb static void
34956b4cac81SBjoern A. Zeeb lkpi_ic_node_cleanup(struct ieee80211_node *ni)
34966b4cac81SBjoern A. Zeeb {
34976b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
34986b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
34996b4cac81SBjoern A. Zeeb 
35006b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
35016b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
35026b4cac81SBjoern A. Zeeb 
35036b4cac81SBjoern A. Zeeb 	/* XXX-BZ remove from driver, ... */
35046b4cac81SBjoern A. Zeeb 	IMPROVE();
35056b4cac81SBjoern A. Zeeb 
35066b4cac81SBjoern A. Zeeb 	if (lhw->ic_node_cleanup != NULL)
35076b4cac81SBjoern A. Zeeb 		lhw->ic_node_cleanup(ni);
35086b4cac81SBjoern A. Zeeb }
35096b4cac81SBjoern A. Zeeb 
35106b4cac81SBjoern A. Zeeb static void
35116b4cac81SBjoern A. Zeeb lkpi_ic_node_free(struct ieee80211_node *ni)
35126b4cac81SBjoern A. Zeeb {
35136b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
35146b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
35156b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
35166b4cac81SBjoern A. Zeeb 
35176b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
35186b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
35196b4cac81SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
35206b4cac81SBjoern A. Zeeb 
35210936c648SBjoern A. Zeeb 	/* KASSERT lsta is not NULL here. Print ni/ni__refcnt. */
35226b4cac81SBjoern A. Zeeb 
35230936c648SBjoern A. Zeeb 	/*
35240936c648SBjoern A. Zeeb 	 * Pass in the original ni just in case of error we could check that
35250936c648SBjoern A. Zeeb 	 * it is the same as lsta->ni.
35260936c648SBjoern A. Zeeb 	 */
35270936c648SBjoern A. Zeeb 	lkpi_lsta_free(lsta, ni);
35286b4cac81SBjoern A. Zeeb 
35296b4cac81SBjoern A. Zeeb 	if (lhw->ic_node_free != NULL)
35306b4cac81SBjoern A. Zeeb 		lhw->ic_node_free(ni);
35316b4cac81SBjoern A. Zeeb }
35326b4cac81SBjoern A. Zeeb 
35336b4cac81SBjoern A. Zeeb static int
35346b4cac81SBjoern A. Zeeb lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
35356b4cac81SBjoern A. Zeeb         const struct ieee80211_bpf_params *params __unused)
35366b4cac81SBjoern A. Zeeb {
35376b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
35386b4cac81SBjoern A. Zeeb 
35396b4cac81SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
3540*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_LOCK(lsta);
35410936c648SBjoern A. Zeeb 	if (!lsta->txq_ready) {
3542*fa4e4257SBjoern A. Zeeb 		LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
3543*fa4e4257SBjoern A. Zeeb 		/*
3544*fa4e4257SBjoern A. Zeeb 		 * Free the mbuf (do NOT release ni ref for the m_pkthdr.rcvif!
3545*fa4e4257SBjoern A. Zeeb 		 * ieee80211_raw_output() does that in case of error).
3546*fa4e4257SBjoern A. Zeeb 		 */
35470936c648SBjoern A. Zeeb 		m_free(m);
35480936c648SBjoern A. Zeeb 		return (ENETDOWN);
35490936c648SBjoern A. Zeeb 	}
35506b4cac81SBjoern A. Zeeb 
35516b4cac81SBjoern A. Zeeb 	/* Queue the packet and enqueue the task to handle it. */
35526b4cac81SBjoern A. Zeeb 	mbufq_enqueue(&lsta->txq, m);
3553*fa4e4257SBjoern A. Zeeb 	taskqueue_enqueue(taskqueue_thread, &lsta->txq_task);
3554*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
35556b4cac81SBjoern A. Zeeb 
35569d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
35579d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
35586b4cac81SBjoern A. Zeeb 		printf("%s:%d lsta %p ni %p %6D mbuf_qlen %d\n",
35596b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, lsta, ni, ni->ni_macaddr, ":",
35606b4cac81SBjoern A. Zeeb 		    mbufq_len(&lsta->txq));
35619d9ba2b7SBjoern A. Zeeb #endif
35626b4cac81SBjoern A. Zeeb 
35636b4cac81SBjoern A. Zeeb 	return (0);
35646b4cac81SBjoern A. Zeeb }
35656b4cac81SBjoern A. Zeeb 
35666b4cac81SBjoern A. Zeeb static void
35676b4cac81SBjoern A. Zeeb lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
35686b4cac81SBjoern A. Zeeb {
35696b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
3570b35f6cd0SBjoern A. Zeeb #ifndef LKPI_80211_HW_CRYPTO
35716b4cac81SBjoern A. Zeeb 	struct ieee80211_frame *wh;
3572b35f6cd0SBjoern A. Zeeb #endif
35736b4cac81SBjoern A. Zeeb 	struct ieee80211_key *k;
35746b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
35756b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
35766b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
35776b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
35786b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
35796b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
35806b4cac81SBjoern A. Zeeb 	struct ieee80211_channel *c;
35816b4cac81SBjoern A. Zeeb 	struct ieee80211_tx_control control;
35826b4cac81SBjoern A. Zeeb 	struct ieee80211_tx_info *info;
35836b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
3584e3a0b120SBjoern A. Zeeb 	struct ieee80211_hdr *hdr;
35856b4cac81SBjoern A. Zeeb 	void *buf;
3586e3a0b120SBjoern A. Zeeb 	uint8_t ac, tid;
35876b4cac81SBjoern A. Zeeb 
35886b4cac81SBjoern A. Zeeb 	M_ASSERTPKTHDR(m);
35896b4cac81SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
35909d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX_DUMP)
35916b4cac81SBjoern A. Zeeb 		hexdump(mtod(m, const void *), m->m_len, "RAW TX (plain) ", 0);
35926b4cac81SBjoern A. Zeeb #endif
35936b4cac81SBjoern A. Zeeb 
35946b4cac81SBjoern A. Zeeb 	ni = lsta->ni;
3595b35f6cd0SBjoern A. Zeeb 	k = NULL;
3596b35f6cd0SBjoern A. Zeeb #ifndef LKPI_80211_HW_CRYPTO
35976b4cac81SBjoern A. Zeeb 	/* Encrypt the frame if need be; XXX-BZ info->control.hw_key. */
35986b4cac81SBjoern A. Zeeb 	wh = mtod(m, struct ieee80211_frame *);
35996b4cac81SBjoern A. Zeeb 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
36006b4cac81SBjoern A. Zeeb 		/* Retrieve key for TX && do software encryption. */
36016b4cac81SBjoern A. Zeeb 		k = ieee80211_crypto_encap(ni, m);
36026b4cac81SBjoern A. Zeeb 		if (k == NULL) {
36036b4cac81SBjoern A. Zeeb 			ieee80211_free_node(ni);
36046b4cac81SBjoern A. Zeeb 			m_freem(m);
36056b4cac81SBjoern A. Zeeb 			return;
36066b4cac81SBjoern A. Zeeb 		}
36076b4cac81SBjoern A. Zeeb 	}
36086b4cac81SBjoern A. Zeeb #endif
36096b4cac81SBjoern A. Zeeb 
36106b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
36116b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
36126b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
36136b4cac81SBjoern A. Zeeb 	c = ni->ni_chan;
36146b4cac81SBjoern A. Zeeb 
36156b4cac81SBjoern A. Zeeb 	if (ieee80211_radiotap_active_vap(ni->ni_vap)) {
36166b4cac81SBjoern A. Zeeb 		struct lkpi_radiotap_tx_hdr *rtap;
36176b4cac81SBjoern A. Zeeb 
36186b4cac81SBjoern A. Zeeb 		rtap = &lhw->rtap_tx;
36196b4cac81SBjoern A. Zeeb 		rtap->wt_flags = 0;
36206b4cac81SBjoern A. Zeeb 		if (k != NULL)
36216b4cac81SBjoern A. Zeeb 			rtap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
36226b4cac81SBjoern A. Zeeb 		if (m->m_flags & M_FRAG)
36236b4cac81SBjoern A. Zeeb 			rtap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
36246b4cac81SBjoern A. Zeeb 		IMPROVE();
36256b4cac81SBjoern A. Zeeb 		rtap->wt_rate = 0;
36266b4cac81SBjoern A. Zeeb 		if (c != NULL && c != IEEE80211_CHAN_ANYC) {
36276b4cac81SBjoern A. Zeeb 			rtap->wt_chan_freq = htole16(c->ic_freq);
36286b4cac81SBjoern A. Zeeb 			rtap->wt_chan_flags = htole16(c->ic_flags);
36296b4cac81SBjoern A. Zeeb 		}
36306b4cac81SBjoern A. Zeeb 
36316b4cac81SBjoern A. Zeeb 		ieee80211_radiotap_tx(ni->ni_vap, m);
36326b4cac81SBjoern A. Zeeb 	}
36336b4cac81SBjoern A. Zeeb 
36346b4cac81SBjoern A. Zeeb 	/*
36356b4cac81SBjoern A. Zeeb 	 * net80211 should handle hw->extra_tx_headroom.
36366b4cac81SBjoern A. Zeeb 	 * Though for as long as we are copying we don't mind.
36373d09d310SBjoern A. Zeeb 	 * XXX-BZ rtw88 asks for too much headroom for ipv6+tcp:
36383d09d310SBjoern A. Zeeb 	 * https://lists.freebsd.org/archives/freebsd-transport/2022-February/000012.html
36396b4cac81SBjoern A. Zeeb 	 */
36406b4cac81SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + m->m_pkthdr.len);
36416b4cac81SBjoern A. Zeeb 	if (skb == NULL) {
36423f0083c4SBjoern A. Zeeb 		ic_printf(ic, "ERROR %s: skb alloc failed\n", __func__);
36436b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
36446b4cac81SBjoern A. Zeeb 		m_freem(m);
36456b4cac81SBjoern A. Zeeb 		return;
36466b4cac81SBjoern A. Zeeb 	}
36476b4cac81SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
36486b4cac81SBjoern A. Zeeb 
36496b4cac81SBjoern A. Zeeb 	/* XXX-BZ we need a SKB version understanding mbuf. */
36506b4cac81SBjoern A. Zeeb 	/* Save the mbuf for ieee80211_tx_complete(). */
36516b4cac81SBjoern A. Zeeb 	skb->m_free_func = lkpi_ieee80211_free_skb_mbuf;
36526b4cac81SBjoern A. Zeeb 	skb->m = m;
36536b4cac81SBjoern A. Zeeb #if 0
36546b4cac81SBjoern A. Zeeb 	skb_put_data(skb, m->m_data, m->m_pkthdr.len);
36556b4cac81SBjoern A. Zeeb #else
36566b4cac81SBjoern A. Zeeb 	buf = skb_put(skb, m->m_pkthdr.len);
36576b4cac81SBjoern A. Zeeb 	m_copydata(m, 0, m->m_pkthdr.len, buf);
36586b4cac81SBjoern A. Zeeb #endif
36596b4cac81SBjoern A. Zeeb 	/* Save the ni. */
36606b4cac81SBjoern A. Zeeb 	m->m_pkthdr.PH_loc.ptr = ni;
36616b4cac81SBjoern A. Zeeb 
36626b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(ni->ni_vap);
36636b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
36646b4cac81SBjoern A. Zeeb 
3665e3a0b120SBjoern A. Zeeb 	hdr = (void *)skb->data;
3666e3a0b120SBjoern A. Zeeb 	tid = linuxkpi_ieee80211_get_tid(hdr, true);
3667e3a0b120SBjoern A. Zeeb 	if (tid == IEEE80211_NONQOS_TID) { /* == IEEE80211_NUM_TIDS */
3668e3a0b120SBjoern A. Zeeb 		skb->priority = 0;
3669e3a0b120SBjoern A. Zeeb 		ac = IEEE80211_AC_BE;
3670e3a0b120SBjoern A. Zeeb 	} else {
3671e3a0b120SBjoern A. Zeeb 		skb->priority = tid & IEEE80211_QOS_CTL_TID_MASK;
3672fb3c249eSBjoern A. Zeeb 		ac = ieee80211e_up_to_ac[tid & 7];
3673e3a0b120SBjoern A. Zeeb 	}
36746b4cac81SBjoern A. Zeeb 	skb_set_queue_mapping(skb, ac);
36756b4cac81SBjoern A. Zeeb 
36766b4cac81SBjoern A. Zeeb 	info = IEEE80211_SKB_CB(skb);
36776b4cac81SBjoern A. Zeeb 	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
36786b4cac81SBjoern A. Zeeb 	/* Slight delay; probably only happens on scanning so fine? */
36796b4cac81SBjoern A. Zeeb 	if (c == NULL || c == IEEE80211_CHAN_ANYC)
36806b4cac81SBjoern A. Zeeb 		c = ic->ic_curchan;
36816b4cac81SBjoern A. Zeeb 	info->band = lkpi_net80211_chan_to_nl80211_band(c);
3682e3a0b120SBjoern A. Zeeb 	info->hw_queue = vif->hw_queue[ac];
36836b4cac81SBjoern A. Zeeb 	if (m->m_flags & M_EAPOL)
36846b4cac81SBjoern A. Zeeb 		info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
36856b4cac81SBjoern A. Zeeb 	info->control.vif = vif;
36866b4cac81SBjoern A. Zeeb 	/* XXX-BZ info->control.rates */
36879fb91463SBjoern A. Zeeb #ifdef __notyet__
36889fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
36899fb91463SBjoern A. Zeeb 	info->control.rts_cts_rate_idx=
36909fb91463SBjoern A. Zeeb 	info->control.use_rts= /* RTS */
36919fb91463SBjoern A. Zeeb 	info->control.use_cts_prot= /* RTS/CTS*/
36929fb91463SBjoern A. Zeeb #endif
36939fb91463SBjoern A. Zeeb #endif
36946b4cac81SBjoern A. Zeeb 
36956b4cac81SBjoern A. Zeeb 	lsta = lkpi_find_lsta_by_ni(lvif, ni);
36966b4cac81SBjoern A. Zeeb 	if (lsta != NULL) {
36976b4cac81SBjoern A. Zeeb 		sta = LSTA_TO_STA(lsta);
3698b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
36996b4cac81SBjoern A. Zeeb 		info->control.hw_key = lsta->kc;
37006b4cac81SBjoern A. Zeeb #endif
37016b4cac81SBjoern A. Zeeb 	} else {
37026b4cac81SBjoern A. Zeeb 		sta = NULL;
37036b4cac81SBjoern A. Zeeb 	}
37046b4cac81SBjoern A. Zeeb 
37056b4cac81SBjoern A. Zeeb 	IMPROVE();
37066b4cac81SBjoern A. Zeeb 
37076b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
37086b4cac81SBjoern A. Zeeb 		struct lkpi_txq *ltxq;
37096b4cac81SBjoern A. Zeeb 
3710e3a0b120SBjoern A. Zeeb 		ltxq = NULL;
3711e3a0b120SBjoern A. Zeeb 		if (!ieee80211_is_data_present(hdr->frame_control)) {
3712e3a0b120SBjoern A. Zeeb 			if (vif->type == NL80211_IFTYPE_STATION &&
3713e3a0b120SBjoern A. Zeeb 			    lsta->added_to_drv &&
3714e3a0b120SBjoern A. Zeeb 			    sta->txq[IEEE80211_NUM_TIDS] != NULL)
3715d0d29110SBjoern A. Zeeb 				ltxq = TXQ_TO_LTXQ(sta->txq[IEEE80211_NUM_TIDS]);
3716e3a0b120SBjoern A. Zeeb 		} else if (lsta->added_to_drv &&
3717e3a0b120SBjoern A. Zeeb 		    sta->txq[skb->priority] != NULL) {
3718e3a0b120SBjoern A. Zeeb 			ltxq = TXQ_TO_LTXQ(sta->txq[skb->priority]);
3719e3a0b120SBjoern A. Zeeb 		}
3720e3a0b120SBjoern A. Zeeb 		if (ltxq == NULL)
3721d0d29110SBjoern A. Zeeb 			goto ops_tx;
3722e3a0b120SBjoern A. Zeeb 
3723d0d29110SBjoern A. Zeeb 		KASSERT(ltxq != NULL, ("%s: lsta %p sta %p m %p skb %p "
3724d0d29110SBjoern A. Zeeb 		    "ltxq %p != NULL\n", __func__, lsta, sta, m, skb, ltxq));
3725d0d29110SBjoern A. Zeeb 
3726eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_LOCK(ltxq);
37276b4cac81SBjoern A. Zeeb 		skb_queue_tail(&ltxq->skbq, skb);
37289d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
37299d9ba2b7SBjoern A. Zeeb 		if (linuxkpi_debug_80211 & D80211_TRACE_TX)
3730d0d29110SBjoern A. Zeeb 			printf("%s:%d mo_wake_tx_queue :: %d %u lsta %p sta %p "
3731d0d29110SBjoern A. Zeeb 			    "ni %p %6D skb %p lxtq %p { qlen %u, ac %d tid %u } "
3732d0d29110SBjoern A. Zeeb 			    "WAKE_TX_Q ac %d prio %u qmap %u\n",
3733fb6eaf74SBjoern A. Zeeb 			    __func__, __LINE__,
3734d0d29110SBjoern A. Zeeb 			    curthread->td_tid, (unsigned int)ticks,
3735d0d29110SBjoern A. Zeeb 			    lsta, sta, ni, ni->ni_macaddr, ":", skb, ltxq,
3736d0d29110SBjoern A. Zeeb 			    skb_queue_len(&ltxq->skbq), ltxq->txq.ac,
3737d0d29110SBjoern A. Zeeb 			    ltxq->txq.tid, ac, skb->priority, skb->qmap);
37389d9ba2b7SBjoern A. Zeeb #endif
3739eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_UNLOCK(ltxq);
3740d0d29110SBjoern A. Zeeb 		lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
37416b4cac81SBjoern A. Zeeb 		return;
37426b4cac81SBjoern A. Zeeb 	}
37436b4cac81SBjoern A. Zeeb 
37446b4cac81SBjoern A. Zeeb ops_tx:
37459d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
37469d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
3747d0d29110SBjoern A. Zeeb 		printf("%s:%d mo_tx :: lsta %p sta %p ni %p %6D skb %p "
3748d0d29110SBjoern A. Zeeb 		    "TX ac %d prio %u qmap %u\n",
37496b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, lsta, sta, ni, ni->ni_macaddr, ":",
37506b4cac81SBjoern A. Zeeb 		    skb, ac, skb->priority, skb->qmap);
37519d9ba2b7SBjoern A. Zeeb #endif
37526b4cac81SBjoern A. Zeeb 	memset(&control, 0, sizeof(control));
37536b4cac81SBjoern A. Zeeb 	control.sta = sta;
37546b4cac81SBjoern A. Zeeb 
37556b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_tx(hw, &control, skb);
37566b4cac81SBjoern A. Zeeb 	return;
37576b4cac81SBjoern A. Zeeb }
37586b4cac81SBjoern A. Zeeb 
37596b4cac81SBjoern A. Zeeb static void
37606b4cac81SBjoern A. Zeeb lkpi_80211_txq_task(void *ctx, int pending)
37616b4cac81SBjoern A. Zeeb {
37626b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
37636b4cac81SBjoern A. Zeeb 	struct mbufq mq;
37646b4cac81SBjoern A. Zeeb 	struct mbuf *m;
37656b4cac81SBjoern A. Zeeb 
37666b4cac81SBjoern A. Zeeb 	lsta = ctx;
37676b4cac81SBjoern A. Zeeb 
37689d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
37699d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
37706b4cac81SBjoern A. Zeeb 		printf("%s:%d lsta %p ni %p %6D pending %d mbuf_qlen %d\n",
37719d9ba2b7SBjoern A. Zeeb 		    __func__, __LINE__, lsta, lsta->ni, lsta->ni->ni_macaddr, ":",
37726b4cac81SBjoern A. Zeeb 		    pending, mbufq_len(&lsta->txq));
37739d9ba2b7SBjoern A. Zeeb #endif
37746b4cac81SBjoern A. Zeeb 
37756b4cac81SBjoern A. Zeeb 	mbufq_init(&mq, IFQ_MAXLEN);
37766b4cac81SBjoern A. Zeeb 
3777*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_LOCK(lsta);
3778*fa4e4257SBjoern A. Zeeb 	/*
3779*fa4e4257SBjoern A. Zeeb 	 * Do not re-check lsta->txq_ready here; we may have a pending
3780*fa4e4257SBjoern A. Zeeb 	 * disassoc frame still.
3781*fa4e4257SBjoern A. Zeeb 	 */
37826b4cac81SBjoern A. Zeeb 	mbufq_concat(&mq, &lsta->txq);
3783*fa4e4257SBjoern A. Zeeb 	LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
37846b4cac81SBjoern A. Zeeb 
37856b4cac81SBjoern A. Zeeb 	m = mbufq_dequeue(&mq);
37866b4cac81SBjoern A. Zeeb 	while (m != NULL) {
37876b4cac81SBjoern A. Zeeb 		lkpi_80211_txq_tx_one(lsta, m);
37886b4cac81SBjoern A. Zeeb 		m = mbufq_dequeue(&mq);
37896b4cac81SBjoern A. Zeeb 	}
37906b4cac81SBjoern A. Zeeb }
37916b4cac81SBjoern A. Zeeb 
37926b4cac81SBjoern A. Zeeb static int
37936b4cac81SBjoern A. Zeeb lkpi_ic_transmit(struct ieee80211com *ic, struct mbuf *m)
37946b4cac81SBjoern A. Zeeb {
37956b4cac81SBjoern A. Zeeb 
37966b4cac81SBjoern A. Zeeb 	/* XXX TODO */
37976b4cac81SBjoern A. Zeeb 	IMPROVE();
37986b4cac81SBjoern A. Zeeb 
37996b4cac81SBjoern A. Zeeb 	/* Quick and dirty cheating hack. */
38006b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
38016b4cac81SBjoern A. Zeeb 
38026b4cac81SBjoern A. Zeeb 	ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
38036b4cac81SBjoern A. Zeeb 	return (lkpi_ic_raw_xmit(ni, m, NULL));
38046b4cac81SBjoern A. Zeeb }
38056b4cac81SBjoern A. Zeeb 
38069fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
38079fb91463SBjoern A. Zeeb static int
38089fb91463SBjoern A. Zeeb lkpi_ic_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
38099fb91463SBjoern A. Zeeb     const uint8_t *frm, const uint8_t *efrm)
38109fb91463SBjoern A. Zeeb {
38119fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38129fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38139fb91463SBjoern A. Zeeb 
38149fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38159fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38169fb91463SBjoern A. Zeeb 
38179fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38189fb91463SBjoern A. Zeeb 
38199fb91463SBjoern A. Zeeb 	return (lhw->ic_recv_action(ni, wh, frm, efrm));
38209fb91463SBjoern A. Zeeb }
38219fb91463SBjoern A. Zeeb 
38229fb91463SBjoern A. Zeeb static int
38239fb91463SBjoern A. Zeeb lkpi_ic_send_action(struct ieee80211_node *ni, int category, int action, void *sa)
38249fb91463SBjoern A. Zeeb {
38259fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38269fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38279fb91463SBjoern A. Zeeb 
38289fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38299fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38309fb91463SBjoern A. Zeeb 
38319fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38329fb91463SBjoern A. Zeeb 
38339fb91463SBjoern A. Zeeb 	return (lhw->ic_send_action(ni, category, action, sa));
38349fb91463SBjoern A. Zeeb }
38359fb91463SBjoern A. Zeeb 
38369fb91463SBjoern A. Zeeb 
38379fb91463SBjoern A. Zeeb static int
38389fb91463SBjoern A. Zeeb lkpi_ic_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
38399fb91463SBjoern A. Zeeb {
38409fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38419fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38429fb91463SBjoern A. Zeeb 
38439fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38449fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38459fb91463SBjoern A. Zeeb 
38469fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38479fb91463SBjoern A. Zeeb 
38489fb91463SBjoern A. Zeeb 	return (lhw->ic_ampdu_enable(ni, tap));
38499fb91463SBjoern A. Zeeb }
38509fb91463SBjoern A. Zeeb 
38519fb91463SBjoern A. Zeeb static int
38529fb91463SBjoern A. Zeeb lkpi_ic_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
38539fb91463SBjoern A. Zeeb     int dialogtoken, int baparamset, int batimeout)
38549fb91463SBjoern A. Zeeb {
38559fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38569fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38579fb91463SBjoern A. Zeeb 
38589fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38599fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38609fb91463SBjoern A. Zeeb 
38619fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38629fb91463SBjoern A. Zeeb 
38639fb91463SBjoern A. Zeeb 	return (lhw->ic_addba_request(ni, tap, dialogtoken, baparamset, batimeout));
38649fb91463SBjoern A. Zeeb }
38659fb91463SBjoern A. Zeeb 
38669fb91463SBjoern A. Zeeb static int
38679fb91463SBjoern A. Zeeb lkpi_ic_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
38689fb91463SBjoern A. Zeeb     int status, int baparamset, int batimeout)
38699fb91463SBjoern A. Zeeb {
38709fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38719fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38729fb91463SBjoern A. Zeeb 
38739fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38749fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38759fb91463SBjoern A. Zeeb 
38769fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38779fb91463SBjoern A. Zeeb 
38789fb91463SBjoern A. Zeeb 	return (lhw->ic_addba_response(ni, tap, status, baparamset, batimeout));
38799fb91463SBjoern A. Zeeb }
38809fb91463SBjoern A. Zeeb 
38819fb91463SBjoern A. Zeeb static void
38829fb91463SBjoern A. Zeeb lkpi_ic_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
38839fb91463SBjoern A. Zeeb {
38849fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38859fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38869fb91463SBjoern A. Zeeb 
38879fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38889fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38899fb91463SBjoern A. Zeeb 
38909fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38919fb91463SBjoern A. Zeeb 
38929fb91463SBjoern A. Zeeb 	lhw->ic_addba_stop(ni, tap);
38939fb91463SBjoern A. Zeeb }
38949fb91463SBjoern A. Zeeb 
38959fb91463SBjoern A. Zeeb static void
38969fb91463SBjoern A. Zeeb lkpi_ic_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
38979fb91463SBjoern A. Zeeb {
38989fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38999fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39009fb91463SBjoern A. Zeeb 
39019fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39029fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39039fb91463SBjoern A. Zeeb 
39049fb91463SBjoern A. Zeeb 	IMPROVE_HT();
39059fb91463SBjoern A. Zeeb 
39069fb91463SBjoern A. Zeeb 	lhw->ic_addba_response_timeout(ni, tap);
39079fb91463SBjoern A. Zeeb }
39089fb91463SBjoern A. Zeeb 
39099fb91463SBjoern A. Zeeb static void
39109fb91463SBjoern A. Zeeb lkpi_ic_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
39119fb91463SBjoern A. Zeeb     int status)
39129fb91463SBjoern A. Zeeb {
39139fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
39149fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39159fb91463SBjoern A. Zeeb 
39169fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39179fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39189fb91463SBjoern A. Zeeb 
39199fb91463SBjoern A. Zeeb 	IMPROVE_HT();
39209fb91463SBjoern A. Zeeb 
39219fb91463SBjoern A. Zeeb 	lhw->ic_bar_response(ni, tap, status);
39229fb91463SBjoern A. Zeeb }
39239fb91463SBjoern A. Zeeb 
39249fb91463SBjoern A. Zeeb static int
39259fb91463SBjoern A. Zeeb lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
39269fb91463SBjoern A. Zeeb     int baparamset, int batimeout, int baseqctl)
39279fb91463SBjoern A. Zeeb {
39289fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
39299fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39309fb91463SBjoern A. Zeeb 	struct ieee80211_hw *hw;
39319fb91463SBjoern A. Zeeb 	struct ieee80211vap *vap;
39329fb91463SBjoern A. Zeeb 	struct lkpi_vif *lvif;
39339fb91463SBjoern A. Zeeb 	struct ieee80211_vif *vif;
39349fb91463SBjoern A. Zeeb 	struct lkpi_sta *lsta;
39359fb91463SBjoern A. Zeeb         struct ieee80211_sta *sta;
39369fb91463SBjoern A. Zeeb 	struct ieee80211_ampdu_params params;
39379fb91463SBjoern A. Zeeb 	int error;
39389fb91463SBjoern A. Zeeb 
39399fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39409fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39419fb91463SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
39429fb91463SBjoern A. Zeeb 	vap = ni->ni_vap;
39439fb91463SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
39449fb91463SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
39459fb91463SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
39469fb91463SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
39479fb91463SBjoern A. Zeeb 
39489fb91463SBjoern A. Zeeb 	params.sta = sta;
39499fb91463SBjoern A. Zeeb 	params.action = IEEE80211_AMPDU_RX_START;
39509fb91463SBjoern A. Zeeb 	params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
39519fb91463SBjoern A. Zeeb 	if (params.buf_size == 0)
39529fb91463SBjoern A. Zeeb 		params.buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
39539fb91463SBjoern A. Zeeb 	else
39549fb91463SBjoern A. Zeeb 		params.buf_size = min(params.buf_size, IEEE80211_MAX_AMPDU_BUF_HT);
39559fb91463SBjoern A. Zeeb 	if (params.buf_size > hw->max_rx_aggregation_subframes)
39569fb91463SBjoern A. Zeeb 		params.buf_size = hw->max_rx_aggregation_subframes;
39579fb91463SBjoern A. Zeeb 	params.timeout = le16toh(batimeout);
39589fb91463SBjoern A. Zeeb 	params.ssn = _IEEE80211_MASKSHIFT(le16toh(baseqctl), IEEE80211_BASEQ_START);
39599fb91463SBjoern A. Zeeb 	params.tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
39609fb91463SBjoern A. Zeeb 	params.amsdu = false;
39619fb91463SBjoern A. Zeeb 
39629fb91463SBjoern A. Zeeb 	IMPROVE_HT("Do we need to distinguish based on SUPPORTS_REORDERING_BUFFER?");
39639fb91463SBjoern A. Zeeb 
39649fb91463SBjoern A. Zeeb 	/* This may call kalloc.  Make sure we can sleep. */
39659fb91463SBjoern A. Zeeb 	error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
39669fb91463SBjoern A. Zeeb 	if (error != 0) {
39679fb91463SBjoern A. Zeeb 		ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
39689fb91463SBjoern A. Zeeb 		    __func__, error, ni, rap);
39699fb91463SBjoern A. Zeeb 		return (error);
39709fb91463SBjoern A. Zeeb 	}
39719fb91463SBjoern A. Zeeb 	IMPROVE_HT("net80211 is missing the error check on return and assumes success");
39729fb91463SBjoern A. Zeeb 
39739fb91463SBjoern A. Zeeb 	error = lhw->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
39749fb91463SBjoern A. Zeeb 	return (error);
39759fb91463SBjoern A. Zeeb }
39769fb91463SBjoern A. Zeeb 
39779fb91463SBjoern A. Zeeb static void
39789fb91463SBjoern A. Zeeb lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
39799fb91463SBjoern A. Zeeb {
39809fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
39819fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39829fb91463SBjoern A. Zeeb 	struct ieee80211_hw *hw;
39839fb91463SBjoern A. Zeeb 	struct ieee80211vap *vap;
39849fb91463SBjoern A. Zeeb 	struct lkpi_vif *lvif;
39859fb91463SBjoern A. Zeeb 	struct ieee80211_vif *vif;
39869fb91463SBjoern A. Zeeb 	struct lkpi_sta *lsta;
39879fb91463SBjoern A. Zeeb         struct ieee80211_sta *sta;
39889fb91463SBjoern A. Zeeb 	struct ieee80211_ampdu_params params;
39899fb91463SBjoern A. Zeeb 	int error;
39909fb91463SBjoern A. Zeeb 	uint8_t tid;
39919fb91463SBjoern A. Zeeb 
39929fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39939fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39949fb91463SBjoern A. Zeeb 
39959fb91463SBjoern A. Zeeb 	/*
39969fb91463SBjoern A. Zeeb 	 * We should not (cannot) call into mac80211 ops with AMPDU_RX_STOP if
39979fb91463SBjoern A. Zeeb 	 * we did not START.  Some drivers pass it down to firmware which will
39989fb91463SBjoern A. Zeeb 	 * simply barf and net80211 calls ieee80211_ht_node_cleanup() from
39999fb91463SBjoern A. Zeeb 	 * ieee80211_ht_node_init() amongst others which will iterate over all
40009fb91463SBjoern A. Zeeb 	 * tid and call ic_ampdu_rx_stop() unconditionally.
40019fb91463SBjoern A. Zeeb 	 * XXX net80211 should probably be more "gentle" in these cases and
40029fb91463SBjoern A. Zeeb 	 * track some state itself.
40039fb91463SBjoern A. Zeeb 	 */
40049fb91463SBjoern A. Zeeb 	if ((rap->rxa_flags & IEEE80211_AGGR_RUNNING) == 0)
40059fb91463SBjoern A. Zeeb 		goto net80211_only;
40069fb91463SBjoern A. Zeeb 
40079fb91463SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
40089fb91463SBjoern A. Zeeb 	vap = ni->ni_vap;
40099fb91463SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
40109fb91463SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
40119fb91463SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
40129fb91463SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
40139fb91463SBjoern A. Zeeb 
40149fb91463SBjoern A. Zeeb 	IMPROVE_HT("This really should be passed from ht_recv_action_ba_delba.");
40159fb91463SBjoern A. Zeeb 	for (tid = 0; tid < WME_NUM_TID; tid++) {
40169fb91463SBjoern A. Zeeb 		if (&ni->ni_rx_ampdu[tid] == rap)
40179fb91463SBjoern A. Zeeb 			break;
40189fb91463SBjoern A. Zeeb 	}
40199fb91463SBjoern A. Zeeb 
40209fb91463SBjoern A. Zeeb 	params.sta = sta;
40219fb91463SBjoern A. Zeeb 	params.action = IEEE80211_AMPDU_RX_STOP;
40229fb91463SBjoern A. Zeeb 	params.buf_size = 0;
40239fb91463SBjoern A. Zeeb 	params.timeout = 0;
40249fb91463SBjoern A. Zeeb 	params.ssn = 0;
40259fb91463SBjoern A. Zeeb 	params.tid = tid;
40269fb91463SBjoern A. Zeeb 	params.amsdu = false;
40279fb91463SBjoern A. Zeeb 
40289fb91463SBjoern A. Zeeb 	error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
40299fb91463SBjoern A. Zeeb 	if (error != 0)
40309fb91463SBjoern A. Zeeb 		ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
40319fb91463SBjoern A. Zeeb 		    __func__, error, ni, rap);
40329fb91463SBjoern A. Zeeb 
40339fb91463SBjoern A. Zeeb net80211_only:
40349fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_rx_stop(ni, rap);
40359fb91463SBjoern A. Zeeb }
40369fb91463SBjoern A. Zeeb #endif
40379fb91463SBjoern A. Zeeb 
40389fb91463SBjoern A. Zeeb static void
40399fb91463SBjoern A. Zeeb lkpi_ic_getradiocaps_ht(struct ieee80211com *ic, struct ieee80211_hw *hw,
40409fb91463SBjoern A. Zeeb     uint8_t *bands, int *chan_flags, enum nl80211_band band)
40419fb91463SBjoern A. Zeeb {
40429fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
40439fb91463SBjoern A. Zeeb 	struct ieee80211_sta_ht_cap *ht_cap;
40449fb91463SBjoern A. Zeeb 
40459fb91463SBjoern A. Zeeb 	ht_cap = &hw->wiphy->bands[band]->ht_cap;
40469fb91463SBjoern A. Zeeb 	if (!ht_cap->ht_supported)
40479fb91463SBjoern A. Zeeb 		return;
40489fb91463SBjoern A. Zeeb 
40499fb91463SBjoern A. Zeeb 	switch (band) {
40509fb91463SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
40519fb91463SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11NG);
40529fb91463SBjoern A. Zeeb 		break;
40539fb91463SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
40549fb91463SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11NA);
40559fb91463SBjoern A. Zeeb 		break;
40569fb91463SBjoern A. Zeeb 	default:
40579fb91463SBjoern A. Zeeb 		IMPROVE("Unsupported band %d", band);
40589fb91463SBjoern A. Zeeb 		return;
40599fb91463SBjoern A. Zeeb 	}
40609fb91463SBjoern A. Zeeb 
40619fb91463SBjoern A. Zeeb 	ic->ic_htcaps = IEEE80211_HTC_HT;	/* HT operation */
40629fb91463SBjoern A. Zeeb 
40639fb91463SBjoern A. Zeeb 	/*
40649fb91463SBjoern A. Zeeb 	 * Rather than manually checking each flag and
40659fb91463SBjoern A. Zeeb 	 * translating IEEE80211_HT_CAP_ to IEEE80211_HTCAP_,
40669fb91463SBjoern A. Zeeb 	 * simply copy the 16bits.
40679fb91463SBjoern A. Zeeb 	 */
40689fb91463SBjoern A. Zeeb 	ic->ic_htcaps |= ht_cap->cap;
40699fb91463SBjoern A. Zeeb 
40709fb91463SBjoern A. Zeeb 	/* Then deal with the other flags. */
40719fb91463SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, AMPDU_AGGREGATION))
40729fb91463SBjoern A. Zeeb 		ic->ic_htcaps |= IEEE80211_HTC_AMPDU;
40739fb91463SBjoern A. Zeeb #ifdef __notyet__
40749fb91463SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, TX_AMSDU))
40759fb91463SBjoern A. Zeeb 		ic->ic_htcaps |= IEEE80211_HTC_AMSDU;
40769fb91463SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU))
40779fb91463SBjoern A. Zeeb 		ic->ic_htcaps |= (IEEE80211_HTC_RX_AMSDU_AMPDU |
40789fb91463SBjoern A. Zeeb 		    IEEE80211_HTC_TX_AMSDU_AMPDU);
40799fb91463SBjoern A. Zeeb #endif
40809fb91463SBjoern A. Zeeb 
40819fb91463SBjoern A. Zeeb 	IMPROVE("PS, ampdu_*, ht_cap.mcs.tx_params, ...");
40829fb91463SBjoern A. Zeeb 	ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_OFF;
40839fb91463SBjoern A. Zeeb 
40849fb91463SBjoern A. Zeeb 	/* Only add HT40 channels if supported. */
40859fb91463SBjoern A. Zeeb 	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0 &&
40869fb91463SBjoern A. Zeeb 	    chan_flags != NULL)
40879fb91463SBjoern A. Zeeb 		*chan_flags |= NET80211_CBW_FLAG_HT40;
40889fb91463SBjoern A. Zeeb #endif
40899fb91463SBjoern A. Zeeb }
40909fb91463SBjoern A. Zeeb 
40916b4cac81SBjoern A. Zeeb static void
40926b4cac81SBjoern A. Zeeb lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
40936b4cac81SBjoern A. Zeeb     int *n, struct ieee80211_channel *c)
40946b4cac81SBjoern A. Zeeb {
40956b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
40966b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
40976b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *channels;
40986b4cac81SBjoern A. Zeeb 	uint8_t bands[IEEE80211_MODE_BYTES];
40996b4cac81SBjoern A. Zeeb 	int chan_flags, error, i, nchans;
41006b4cac81SBjoern A. Zeeb 
41016b4cac81SBjoern A. Zeeb 	/* Channels */
41026b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
41036b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
41046b4cac81SBjoern A. Zeeb 
41056b4cac81SBjoern A. Zeeb 	/* NL80211_BAND_2GHZ */
41066b4cac81SBjoern A. Zeeb 	nchans = 0;
41076b4cac81SBjoern A. Zeeb 	if (hw->wiphy->bands[NL80211_BAND_2GHZ] != NULL)
41086b4cac81SBjoern A. Zeeb 		nchans = hw->wiphy->bands[NL80211_BAND_2GHZ]->n_channels;
41096b4cac81SBjoern A. Zeeb 	if (nchans > 0) {
41106b4cac81SBjoern A. Zeeb 		memset(bands, 0, sizeof(bands));
41116b4cac81SBjoern A. Zeeb 		chan_flags = 0;
41126b4cac81SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11B);
41136b4cac81SBjoern A. Zeeb 		/* XXX-BZ unclear how to check for 11g. */
41149fb91463SBjoern A. Zeeb 
41159fb91463SBjoern A. Zeeb 		IMPROVE("the bitrates may have flags?");
41166b4cac81SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11G);
41179fb91463SBjoern A. Zeeb 
41189fb91463SBjoern A. Zeeb 		lkpi_ic_getradiocaps_ht(ic, hw, bands, &chan_flags,
41199fb91463SBjoern A. Zeeb 		    NL80211_BAND_2GHZ);
41206b4cac81SBjoern A. Zeeb 
41216b4cac81SBjoern A. Zeeb 		channels = hw->wiphy->bands[NL80211_BAND_2GHZ]->channels;
4122cee56e77SBjoern A. Zeeb 		for (i = 0; i < nchans && *n < maxchan; i++) {
41236b4cac81SBjoern A. Zeeb 			uint32_t nflags = 0;
41246b4cac81SBjoern A. Zeeb 			int cflags = chan_flags;
41256b4cac81SBjoern A. Zeeb 
41266b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED) {
41273f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Skipping disabled chan "
41283f0083c4SBjoern A. Zeeb 				    "[%u/%u/%#x]\n", __func__,
41296b4cac81SBjoern A. Zeeb 				    channels[i].hw_value,
41306b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags);
41316b4cac81SBjoern A. Zeeb 				continue;
41326b4cac81SBjoern A. Zeeb 			}
41336b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_IR)
41346b4cac81SBjoern A. Zeeb 				nflags |= (IEEE80211_CHAN_NOADHOC|IEEE80211_CHAN_PASSIVE);
41356b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_RADAR)
41366b4cac81SBjoern A. Zeeb 				nflags |= IEEE80211_CHAN_DFS;
41376b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_160MHZ)
41386b4cac81SBjoern A. Zeeb 				cflags &= ~(NET80211_CBW_FLAG_VHT160|NET80211_CBW_FLAG_VHT80P80);
41396b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_80MHZ)
41406b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_VHT80;
41416b4cac81SBjoern A. Zeeb 			/* XXX how to map the remaining enum ieee80211_channel_flags? */
41426b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_HT40)
41436b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_HT40;
41446b4cac81SBjoern A. Zeeb 
41456b4cac81SBjoern A. Zeeb 			error = ieee80211_add_channel_cbw(c, maxchan, n,
41466b4cac81SBjoern A. Zeeb 			    channels[i].hw_value, channels[i].center_freq,
41476b4cac81SBjoern A. Zeeb 			    channels[i].max_power,
41485856761fSBjoern A. Zeeb 			    nflags, bands, cflags);
4149cee56e77SBjoern A. Zeeb 			/* net80211::ENOBUFS: *n >= maxchans */
4150cee56e77SBjoern A. Zeeb 			if (error != 0 && error != ENOBUFS)
41513f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
41523f0083c4SBjoern A. Zeeb 				    "returned error %d\n",
41536b4cac81SBjoern A. Zeeb 				    __func__, channels[i].hw_value,
41546b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags,
41556b4cac81SBjoern A. Zeeb 				    nflags, chan_flags, cflags, error);
4156cee56e77SBjoern A. Zeeb 			if (error != 0)
41576b4cac81SBjoern A. Zeeb 				break;
41586b4cac81SBjoern A. Zeeb 		}
41596b4cac81SBjoern A. Zeeb 	}
41606b4cac81SBjoern A. Zeeb 
41616b4cac81SBjoern A. Zeeb 	/* NL80211_BAND_5GHZ */
41626b4cac81SBjoern A. Zeeb 	nchans = 0;
41636b4cac81SBjoern A. Zeeb 	if (hw->wiphy->bands[NL80211_BAND_5GHZ] != NULL)
41646b4cac81SBjoern A. Zeeb 		nchans = hw->wiphy->bands[NL80211_BAND_5GHZ]->n_channels;
41656b4cac81SBjoern A. Zeeb 	if (nchans > 0) {
41666b4cac81SBjoern A. Zeeb 		memset(bands, 0, sizeof(bands));
41676b4cac81SBjoern A. Zeeb 		chan_flags = 0;
41686b4cac81SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11A);
41699fb91463SBjoern A. Zeeb 
41709fb91463SBjoern A. Zeeb 		lkpi_ic_getradiocaps_ht(ic, hw, bands, &chan_flags,
41719fb91463SBjoern A. Zeeb 		    NL80211_BAND_5GHZ);
41729fb91463SBjoern A. Zeeb 
41739fb91463SBjoern A. Zeeb #ifdef LKPI_80211_VHT
41746b4cac81SBjoern A. Zeeb 		if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported){
41756b4cac81SBjoern A. Zeeb 
41766b4cac81SBjoern A. Zeeb 			ic->ic_flags_ext |= IEEE80211_FEXT_VHT;
4177562adbe1SBjoern A. Zeeb 			ic->ic_vht_cap.vht_cap_info =
41786b4cac81SBjoern A. Zeeb 			    hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap;
41796b4cac81SBjoern A. Zeeb 
41806b4cac81SBjoern A. Zeeb 			setbit(bands, IEEE80211_MODE_VHT_5GHZ);
41816b4cac81SBjoern A. Zeeb 			chan_flags |= NET80211_CBW_FLAG_VHT80;
41826b4cac81SBjoern A. Zeeb 			if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(
4183562adbe1SBjoern A. Zeeb 			    ic->ic_vht_cap.vht_cap_info))
41846b4cac81SBjoern A. Zeeb 				chan_flags |= NET80211_CBW_FLAG_VHT160;
41856b4cac81SBjoern A. Zeeb 			if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(
4186562adbe1SBjoern A. Zeeb 			    ic->ic_vht_cap.vht_cap_info))
41876b4cac81SBjoern A. Zeeb 				chan_flags |= NET80211_CBW_FLAG_VHT80P80;
41886b4cac81SBjoern A. Zeeb 		}
41896b4cac81SBjoern A. Zeeb #endif
41906b4cac81SBjoern A. Zeeb 
41916b4cac81SBjoern A. Zeeb 		channels = hw->wiphy->bands[NL80211_BAND_5GHZ]->channels;
4192cee56e77SBjoern A. Zeeb 		for (i = 0; i < nchans && *n < maxchan; i++) {
41936b4cac81SBjoern A. Zeeb 			uint32_t nflags = 0;
41946b4cac81SBjoern A. Zeeb 			int cflags = chan_flags;
41956b4cac81SBjoern A. Zeeb 
41966b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED) {
41973f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Skipping disabled chan "
41983f0083c4SBjoern A. Zeeb 				    "[%u/%u/%#x]\n", __func__,
41996b4cac81SBjoern A. Zeeb 				    channels[i].hw_value,
42006b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags);
42016b4cac81SBjoern A. Zeeb 				continue;
42026b4cac81SBjoern A. Zeeb 			}
42036b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_IR)
42046b4cac81SBjoern A. Zeeb 				nflags |= (IEEE80211_CHAN_NOADHOC|IEEE80211_CHAN_PASSIVE);
42056b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_RADAR)
42066b4cac81SBjoern A. Zeeb 				nflags |= IEEE80211_CHAN_DFS;
42076b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_160MHZ)
42086b4cac81SBjoern A. Zeeb 				cflags &= ~(NET80211_CBW_FLAG_VHT160|NET80211_CBW_FLAG_VHT80P80);
42096b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_80MHZ)
42106b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_VHT80;
42116b4cac81SBjoern A. Zeeb 			/* XXX hwo to map the remaining enum ieee80211_channel_flags? */
42126b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_HT40)
42136b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_HT40;
42146b4cac81SBjoern A. Zeeb 
42156b4cac81SBjoern A. Zeeb 			error = ieee80211_add_channel_cbw(c, maxchan, n,
42166b4cac81SBjoern A. Zeeb 			    channels[i].hw_value, channels[i].center_freq,
42176b4cac81SBjoern A. Zeeb 			    channels[i].max_power,
42185856761fSBjoern A. Zeeb 			    nflags, bands, cflags);
4219cee56e77SBjoern A. Zeeb 			/* net80211::ENOBUFS: *n >= maxchans */
4220cee56e77SBjoern A. Zeeb 			if (error != 0 && error != ENOBUFS)
42213f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
42223f0083c4SBjoern A. Zeeb 				    "returned error %d\n",
42236b4cac81SBjoern A. Zeeb 				    __func__, channels[i].hw_value,
42246b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags,
42256b4cac81SBjoern A. Zeeb 				    nflags, chan_flags, cflags, error);
4226cee56e77SBjoern A. Zeeb 			if (error != 0)
42276b4cac81SBjoern A. Zeeb 				break;
42286b4cac81SBjoern A. Zeeb 		}
42296b4cac81SBjoern A. Zeeb 	}
42306b4cac81SBjoern A. Zeeb }
42316b4cac81SBjoern A. Zeeb 
42326b4cac81SBjoern A. Zeeb static void *
42336b4cac81SBjoern A. Zeeb lkpi_ieee80211_ifalloc(void)
42346b4cac81SBjoern A. Zeeb {
42356b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
42366b4cac81SBjoern A. Zeeb 
42376b4cac81SBjoern A. Zeeb 	ic = malloc(sizeof(*ic), M_LKPI80211, M_WAITOK | M_ZERO);
42386b4cac81SBjoern A. Zeeb 	if (ic == NULL)
42396b4cac81SBjoern A. Zeeb 		return (NULL);
42406b4cac81SBjoern A. Zeeb 
42416b4cac81SBjoern A. Zeeb 	/* Setting these happens later when we have device information. */
42426b4cac81SBjoern A. Zeeb 	ic->ic_softc = NULL;
42436b4cac81SBjoern A. Zeeb 	ic->ic_name = "linuxkpi";
42446b4cac81SBjoern A. Zeeb 
42456b4cac81SBjoern A. Zeeb 	return (ic);
42466b4cac81SBjoern A. Zeeb }
42476b4cac81SBjoern A. Zeeb 
42486b4cac81SBjoern A. Zeeb struct ieee80211_hw *
42496b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
42506b4cac81SBjoern A. Zeeb {
42516b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
42526b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
42536b4cac81SBjoern A. Zeeb 	struct wiphy *wiphy;
4254a5ae63edSBjoern A. Zeeb 	int ac;
42556b4cac81SBjoern A. Zeeb 
42566b4cac81SBjoern A. Zeeb 	/* Get us and the driver data also allocated. */
42576b4cac81SBjoern A. Zeeb 	wiphy = wiphy_new(&linuxkpi_mac80211cfgops, sizeof(*lhw) + priv_len);
42586b4cac81SBjoern A. Zeeb 	if (wiphy == NULL)
42596b4cac81SBjoern A. Zeeb 		return (NULL);
42606b4cac81SBjoern A. Zeeb 
42616b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
42626b4cac81SBjoern A. Zeeb 	lhw->ops = ops;
4263652e22d3SBjoern A. Zeeb 
42648ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK_INIT(lhw);
42658ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK_INIT(lhw);
4266eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_LOCK_INIT(lhw);
42678891c455SBjoern A. Zeeb 	sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
42686b4cac81SBjoern A. Zeeb 	TAILQ_INIT(&lhw->lvif_head);
4269a5ae63edSBjoern A. Zeeb 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
4270a5ae63edSBjoern A. Zeeb 		lhw->txq_generation[ac] = 1;
4271a5ae63edSBjoern A. Zeeb 		TAILQ_INIT(&lhw->scheduled_txqs[ac]);
4272a5ae63edSBjoern A. Zeeb 	}
42736b4cac81SBjoern A. Zeeb 
42746b4cac81SBjoern A. Zeeb 	/*
42756b4cac81SBjoern A. Zeeb 	 * XXX-BZ TODO make sure there is a "_null" function to all ops
42766b4cac81SBjoern A. Zeeb 	 * not initialized.
42776b4cac81SBjoern A. Zeeb 	 */
42786b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
42796b4cac81SBjoern A. Zeeb 	hw->wiphy = wiphy;
4280086be6a8SBjoern A. Zeeb 	hw->conf.flags |= IEEE80211_CONF_IDLE;
42816b4cac81SBjoern A. Zeeb 	hw->priv = (void *)(lhw + 1);
42826b4cac81SBjoern A. Zeeb 
42836b4cac81SBjoern A. Zeeb 	/* BSD Specific. */
42846b4cac81SBjoern A. Zeeb 	lhw->ic = lkpi_ieee80211_ifalloc();
42856b4cac81SBjoern A. Zeeb 	if (lhw->ic == NULL) {
42866b4cac81SBjoern A. Zeeb 		ieee80211_free_hw(hw);
42876b4cac81SBjoern A. Zeeb 		return (NULL);
42886b4cac81SBjoern A. Zeeb 	}
42896b4cac81SBjoern A. Zeeb 
42906b4cac81SBjoern A. Zeeb 	IMPROVE();
42916b4cac81SBjoern A. Zeeb 
42926b4cac81SBjoern A. Zeeb 	return (hw);
42936b4cac81SBjoern A. Zeeb }
42946b4cac81SBjoern A. Zeeb 
42956b4cac81SBjoern A. Zeeb void
42966b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
42976b4cac81SBjoern A. Zeeb {
42986b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
42996b4cac81SBjoern A. Zeeb 
43006b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
43016b4cac81SBjoern A. Zeeb 	free(lhw->ic, M_LKPI80211);
43026b4cac81SBjoern A. Zeeb 	lhw->ic = NULL;
43036b4cac81SBjoern A. Zeeb 
43046b4cac81SBjoern A. Zeeb 	/* Cleanup more of lhw here or in wiphy_free()? */
4305eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
43068ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw);
4307eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_LOCK_DESTROY(lhw);
4308eac3646fSBjoern A. Zeeb 	sx_destroy(&lhw->lvif_sx);
43096b4cac81SBjoern A. Zeeb 	IMPROVE();
43106b4cac81SBjoern A. Zeeb }
43116b4cac81SBjoern A. Zeeb 
43126b4cac81SBjoern A. Zeeb void
43136b4cac81SBjoern A. Zeeb linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw, char *name)
43146b4cac81SBjoern A. Zeeb {
43156b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
43166b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
43176b4cac81SBjoern A. Zeeb 
43186b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
43196b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
43206b4cac81SBjoern A. Zeeb 
43216b4cac81SBjoern A. Zeeb 	/* Now set a proper name before ieee80211_ifattach(). */
43226b4cac81SBjoern A. Zeeb 	ic->ic_softc = lhw;
43236b4cac81SBjoern A. Zeeb 	ic->ic_name = name;
43246b4cac81SBjoern A. Zeeb 
43256b4cac81SBjoern A. Zeeb 	/* XXX-BZ do we also need to set wiphy name? */
43266b4cac81SBjoern A. Zeeb }
43276b4cac81SBjoern A. Zeeb 
43286b4cac81SBjoern A. Zeeb struct ieee80211_hw *
43296b4cac81SBjoern A. Zeeb linuxkpi_wiphy_to_ieee80211_hw(struct wiphy *wiphy)
43306b4cac81SBjoern A. Zeeb {
43316b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
43326b4cac81SBjoern A. Zeeb 
43336b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
43346b4cac81SBjoern A. Zeeb 	return (LHW_TO_HW(lhw));
43356b4cac81SBjoern A. Zeeb }
43366b4cac81SBjoern A. Zeeb 
43376b4cac81SBjoern A. Zeeb static void
43386b4cac81SBjoern A. Zeeb lkpi_radiotap_attach(struct lkpi_hw *lhw)
43396b4cac81SBjoern A. Zeeb {
43406b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
43416b4cac81SBjoern A. Zeeb 
43426b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
43436b4cac81SBjoern A. Zeeb 	ieee80211_radiotap_attach(ic,
43446b4cac81SBjoern A. Zeeb 	    &lhw->rtap_tx.wt_ihdr, sizeof(lhw->rtap_tx),
43456b4cac81SBjoern A. Zeeb 	    LKPI_RTAP_TX_FLAGS_PRESENT,
43466b4cac81SBjoern A. Zeeb 	    &lhw->rtap_rx.wr_ihdr, sizeof(lhw->rtap_rx),
43476b4cac81SBjoern A. Zeeb 	    LKPI_RTAP_RX_FLAGS_PRESENT);
43486b4cac81SBjoern A. Zeeb }
43496b4cac81SBjoern A. Zeeb 
43502e183d99SBjoern A. Zeeb int
43516b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
43526b4cac81SBjoern A. Zeeb {
43536b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
43546b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
4355c5b96b3eSBjoern A. Zeeb 	int band, i;
43566b4cac81SBjoern A. Zeeb 
43576b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
43586b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
43596b4cac81SBjoern A. Zeeb 
4360652e22d3SBjoern A. Zeeb 	/* We do it this late as wiphy->dev should be set for the name. */
4361652e22d3SBjoern A. Zeeb 	lhw->workq = alloc_ordered_workqueue(wiphy_name(hw->wiphy), 0);
4362652e22d3SBjoern A. Zeeb 	if (lhw->workq == NULL)
4363652e22d3SBjoern A. Zeeb 		return (-EAGAIN);
4364652e22d3SBjoern A. Zeeb 
43656b4cac81SBjoern A. Zeeb 	/* XXX-BZ figure this out how they count his... */
43666b4cac81SBjoern A. Zeeb 	if (!is_zero_ether_addr(hw->wiphy->perm_addr)) {
43676b4cac81SBjoern A. Zeeb 		IEEE80211_ADDR_COPY(ic->ic_macaddr,
43686b4cac81SBjoern A. Zeeb 		    hw->wiphy->perm_addr);
43696b4cac81SBjoern A. Zeeb 	} else if (hw->wiphy->n_addresses > 0) {
43706b4cac81SBjoern A. Zeeb 		/* We take the first one. */
43716b4cac81SBjoern A. Zeeb 		IEEE80211_ADDR_COPY(ic->ic_macaddr,
43726b4cac81SBjoern A. Zeeb 		    hw->wiphy->addresses[0].addr);
43736b4cac81SBjoern A. Zeeb 	} else {
43746b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: warning, no hardware address!\n", __func__);
43756b4cac81SBjoern A. Zeeb 	}
43766b4cac81SBjoern A. Zeeb 
43773d09d310SBjoern A. Zeeb #ifdef __not_yet__
43783d09d310SBjoern A. Zeeb 	/* See comment in lkpi_80211_txq_tx_one(). */
43796b4cac81SBjoern A. Zeeb 	ic->ic_headroom = hw->extra_tx_headroom;
43803d09d310SBjoern A. Zeeb #endif
43816b4cac81SBjoern A. Zeeb 
43826b4cac81SBjoern A. Zeeb 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
43836b4cac81SBjoern A. Zeeb 	ic->ic_opmode = IEEE80211_M_STA;
43846b4cac81SBjoern A. Zeeb 
43856b4cac81SBjoern A. Zeeb 	/* Set device capabilities. */
43866b4cac81SBjoern A. Zeeb 	/* XXX-BZ we need to get these from linux80211/drivers and convert. */
43876b4cac81SBjoern A. Zeeb 	ic->ic_caps =
43886b4cac81SBjoern A. Zeeb 	    IEEE80211_C_STA |
43896b4cac81SBjoern A. Zeeb 	    IEEE80211_C_MONITOR |
43906b4cac81SBjoern A. Zeeb 	    IEEE80211_C_WPA |		/* WPA/RSN */
43914a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
43926b4cac81SBjoern A. Zeeb 	    IEEE80211_C_WME |
43934a67f1dfSBjoern A. Zeeb #endif
43946b4cac81SBjoern A. Zeeb #if 0
43956b4cac81SBjoern A. Zeeb 	    IEEE80211_C_PMGT |
43966b4cac81SBjoern A. Zeeb #endif
43976b4cac81SBjoern A. Zeeb 	    IEEE80211_C_SHSLOT |	/* short slot time supported */
43986b4cac81SBjoern A. Zeeb 	    IEEE80211_C_SHPREAMBLE	/* short preamble supported */
43996b4cac81SBjoern A. Zeeb 	    ;
44006b4cac81SBjoern A. Zeeb #if 0
44016b4cac81SBjoern A. Zeeb 	/* Scanning is a different kind of beast to re-work. */
44026b4cac81SBjoern A. Zeeb 	ic->ic_caps |= IEEE80211_C_BGSCAN;
44036b4cac81SBjoern A. Zeeb #endif
4404cc4e78d5SBjoern A. Zeeb 	if (lhw->ops->hw_scan) {
4405cc4e78d5SBjoern A. Zeeb 		/*
4406cc4e78d5SBjoern A. Zeeb 		 * Advertise full-offload scanning.
4407cc4e78d5SBjoern A. Zeeb 		 *
4408cc4e78d5SBjoern A. Zeeb 		 * Not limiting to SINGLE_SCAN_ON_ALL_BANDS here as otherwise
4409cc4e78d5SBjoern A. Zeeb 		 * we essentially disable hw_scan for all drivers not setting
4410cc4e78d5SBjoern A. Zeeb 		 * the flag.
4411cc4e78d5SBjoern A. Zeeb 		 */
44126b4cac81SBjoern A. Zeeb 		ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
4413a486fbbdSBjoern A. Zeeb 		lhw->scan_flags |= LKPI_LHW_SCAN_HW;
44146b4cac81SBjoern A. Zeeb 	}
44156b4cac81SBjoern A. Zeeb 
4416527687a9SBjoern A. Zeeb 	/*
4417527687a9SBjoern A. Zeeb 	 * The wiphy variables report bitmasks of avail antennas.
4418527687a9SBjoern A. Zeeb 	 * (*get_antenna) get the current bitmask sets which can be
4419527687a9SBjoern A. Zeeb 	 * altered by (*set_antenna) for some drivers.
4420527687a9SBjoern A. Zeeb 	 * XXX-BZ will the count alone do us much good long-term in net80211?
4421527687a9SBjoern A. Zeeb 	 */
4422527687a9SBjoern A. Zeeb 	if (hw->wiphy->available_antennas_rx ||
4423527687a9SBjoern A. Zeeb 	    hw->wiphy->available_antennas_tx) {
4424527687a9SBjoern A. Zeeb 		uint32_t rxs, txs;
4425527687a9SBjoern A. Zeeb 
4426527687a9SBjoern A. Zeeb 		if (lkpi_80211_mo_get_antenna(hw, &txs, &rxs) == 0) {
4427527687a9SBjoern A. Zeeb 			ic->ic_rxstream = bitcount32(rxs);
4428527687a9SBjoern A. Zeeb 			ic->ic_txstream = bitcount32(txs);
4429527687a9SBjoern A. Zeeb 		}
4430527687a9SBjoern A. Zeeb 	}
4431527687a9SBjoern A. Zeeb 
44326b4cac81SBjoern A. Zeeb 	ic->ic_cryptocaps = 0;
4433b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
44346b4cac81SBjoern A. Zeeb 	if (hw->wiphy->n_cipher_suites > 0) {
44356b4cac81SBjoern A. Zeeb 		for (i = 0; i < hw->wiphy->n_cipher_suites; i++)
44366b4cac81SBjoern A. Zeeb 			ic->ic_cryptocaps |= lkpi_l80211_to_net80211_cyphers(
44376b4cac81SBjoern A. Zeeb 			    hw->wiphy->cipher_suites[i]);
44386b4cac81SBjoern A. Zeeb 	}
44396b4cac81SBjoern A. Zeeb #endif
44406b4cac81SBjoern A. Zeeb 
44416b4cac81SBjoern A. Zeeb 	lkpi_ic_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
44426b4cac81SBjoern A. Zeeb 	    ic->ic_channels);
44436b4cac81SBjoern A. Zeeb 
44446b4cac81SBjoern A. Zeeb 	ieee80211_ifattach(ic);
44456b4cac81SBjoern A. Zeeb 
44466b4cac81SBjoern A. Zeeb 	ic->ic_update_mcast = lkpi_ic_update_mcast;
44476b4cac81SBjoern A. Zeeb 	ic->ic_update_promisc = lkpi_ic_update_promisc;
44486b4cac81SBjoern A. Zeeb 	ic->ic_update_chw = lkpi_ic_update_chw;
44496b4cac81SBjoern A. Zeeb 	ic->ic_parent = lkpi_ic_parent;
44506b4cac81SBjoern A. Zeeb 	ic->ic_scan_start = lkpi_ic_scan_start;
44516b4cac81SBjoern A. Zeeb 	ic->ic_scan_end = lkpi_ic_scan_end;
44526b4cac81SBjoern A. Zeeb 	ic->ic_set_channel = lkpi_ic_set_channel;
44536b4cac81SBjoern A. Zeeb 	ic->ic_transmit = lkpi_ic_transmit;
44546b4cac81SBjoern A. Zeeb 	ic->ic_raw_xmit = lkpi_ic_raw_xmit;
44556b4cac81SBjoern A. Zeeb 	ic->ic_vap_create = lkpi_ic_vap_create;
44566b4cac81SBjoern A. Zeeb 	ic->ic_vap_delete = lkpi_ic_vap_delete;
44576b4cac81SBjoern A. Zeeb 	ic->ic_getradiocaps = lkpi_ic_getradiocaps;
44586b4cac81SBjoern A. Zeeb 	ic->ic_wme.wme_update = lkpi_ic_wme_update;
44596b4cac81SBjoern A. Zeeb 
4460a486fbbdSBjoern A. Zeeb 	lhw->ic_scan_curchan = ic->ic_scan_curchan;
4461a486fbbdSBjoern A. Zeeb 	ic->ic_scan_curchan = lkpi_ic_scan_curchan;
4462a486fbbdSBjoern A. Zeeb 	lhw->ic_scan_mindwell = ic->ic_scan_mindwell;
4463a486fbbdSBjoern A. Zeeb 	ic->ic_scan_mindwell = lkpi_ic_scan_mindwell;
4464a486fbbdSBjoern A. Zeeb 
44656b4cac81SBjoern A. Zeeb 	lhw->ic_node_alloc = ic->ic_node_alloc;
44666b4cac81SBjoern A. Zeeb 	ic->ic_node_alloc = lkpi_ic_node_alloc;
44676b4cac81SBjoern A. Zeeb 	lhw->ic_node_init = ic->ic_node_init;
44686b4cac81SBjoern A. Zeeb 	ic->ic_node_init = lkpi_ic_node_init;
44696b4cac81SBjoern A. Zeeb 	lhw->ic_node_cleanup = ic->ic_node_cleanup;
44706b4cac81SBjoern A. Zeeb 	ic->ic_node_cleanup = lkpi_ic_node_cleanup;
44716b4cac81SBjoern A. Zeeb 	lhw->ic_node_free = ic->ic_node_free;
44726b4cac81SBjoern A. Zeeb 	ic->ic_node_free = lkpi_ic_node_free;
44736b4cac81SBjoern A. Zeeb 
44749fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
44759fb91463SBjoern A. Zeeb 	lhw->ic_recv_action = ic->ic_recv_action;
44769fb91463SBjoern A. Zeeb 	ic->ic_recv_action = lkpi_ic_recv_action;
44779fb91463SBjoern A. Zeeb 	lhw->ic_send_action = ic->ic_send_action;
44789fb91463SBjoern A. Zeeb 	ic->ic_send_action = lkpi_ic_send_action;
44799fb91463SBjoern A. Zeeb 
44809fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_enable = ic->ic_ampdu_enable;
44819fb91463SBjoern A. Zeeb 	ic->ic_ampdu_enable = lkpi_ic_ampdu_enable;
44829fb91463SBjoern A. Zeeb 
44839fb91463SBjoern A. Zeeb 	lhw->ic_addba_request = ic->ic_addba_request;
44849fb91463SBjoern A. Zeeb 	ic->ic_addba_request = lkpi_ic_addba_request;
44859fb91463SBjoern A. Zeeb 	lhw->ic_addba_response = ic->ic_addba_response;
44869fb91463SBjoern A. Zeeb 	ic->ic_addba_response = lkpi_ic_addba_response;
44879fb91463SBjoern A. Zeeb 	lhw->ic_addba_stop = ic->ic_addba_stop;
44889fb91463SBjoern A. Zeeb 	ic->ic_addba_stop = lkpi_ic_addba_stop;
44899fb91463SBjoern A. Zeeb 	lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout;
44909fb91463SBjoern A. Zeeb 	ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout;
44919fb91463SBjoern A. Zeeb 
44929fb91463SBjoern A. Zeeb 	lhw->ic_bar_response = ic->ic_bar_response;
44939fb91463SBjoern A. Zeeb 	ic->ic_bar_response = lkpi_ic_bar_response;
44949fb91463SBjoern A. Zeeb 
44959fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start;
44969fb91463SBjoern A. Zeeb 	ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start;
44979fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
44989fb91463SBjoern A. Zeeb 	ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop;
44999fb91463SBjoern A. Zeeb #endif
45009fb91463SBjoern A. Zeeb 
45016b4cac81SBjoern A. Zeeb 	lkpi_radiotap_attach(lhw);
45026b4cac81SBjoern A. Zeeb 
4503c5b96b3eSBjoern A. Zeeb 	/*
4504c5b96b3eSBjoern A. Zeeb 	 * Assign the first possible channel for now;  seems Realtek drivers
4505c5b96b3eSBjoern A. Zeeb 	 * expect one.
4506d9945d78SBjoern A. Zeeb 	 * Also remember the amount of bands we support and the most rates
4507d9945d78SBjoern A. Zeeb 	 * in any band so we can scale [(ext) sup rates] IE(s) accordingly.
4508c5b96b3eSBjoern A. Zeeb 	 */
4509d9945d78SBjoern A. Zeeb 	lhw->supbands = lhw->max_rates = 0;
4510f454a4a1SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
4511c5b96b3eSBjoern A. Zeeb 		struct ieee80211_supported_band *supband;
4512c5b96b3eSBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *channels;
4513c5b96b3eSBjoern A. Zeeb 
4514c5b96b3eSBjoern A. Zeeb 		supband = hw->wiphy->bands[band];
4515c5b96b3eSBjoern A. Zeeb 		if (supband == NULL || supband->n_channels == 0)
4516c5b96b3eSBjoern A. Zeeb 			continue;
4517c5b96b3eSBjoern A. Zeeb 
4518d9945d78SBjoern A. Zeeb 		lhw->supbands++;
4519d9945d78SBjoern A. Zeeb 		lhw->max_rates = max(lhw->max_rates, supband->n_bitrates);
4520d9945d78SBjoern A. Zeeb 
4521f454a4a1SBjoern A. Zeeb 		/* If we have a channel, we need to keep counting supbands. */
4522f454a4a1SBjoern A. Zeeb 		if (hw->conf.chandef.chan != NULL)
4523f454a4a1SBjoern A. Zeeb 			continue;
4524f454a4a1SBjoern A. Zeeb 
4525c5b96b3eSBjoern A. Zeeb 		channels = supband->channels;
4526c5b96b3eSBjoern A. Zeeb 		for (i = 0; i < supband->n_channels; i++) {
4527c5b96b3eSBjoern A. Zeeb 
4528c5b96b3eSBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED)
4529c5b96b3eSBjoern A. Zeeb 				continue;
4530c5b96b3eSBjoern A. Zeeb 
45314a07abdeSBjoern A. Zeeb 			cfg80211_chandef_create(&hw->conf.chandef, &channels[i],
45329fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
45339fb91463SBjoern A. Zeeb 			    (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
45349fb91463SBjoern A. Zeeb #endif
4535c5b96b3eSBjoern A. Zeeb 			    NL80211_CHAN_NO_HT);
4536c5b96b3eSBjoern A. Zeeb 			break;
4537c5b96b3eSBjoern A. Zeeb 		}
4538c5b96b3eSBjoern A. Zeeb 	}
4539c5b96b3eSBjoern A. Zeeb 
4540d9945d78SBjoern A. Zeeb 	IMPROVE("see net80211::ieee80211_chan_init vs. wiphy->bands[].bitrates possibly in lkpi_ic_getradiocaps?");
4541d9945d78SBjoern A. Zeeb 
4542d9945d78SBjoern A. Zeeb 	/* Make sure we do not support more than net80211 is willing to take. */
4543d9945d78SBjoern A. Zeeb 	if (lhw->max_rates > IEEE80211_RATE_MAXSIZE) {
4544d9945d78SBjoern A. Zeeb 		ic_printf(ic, "%s: limiting max_rates %d to %d!\n", __func__,
4545d9945d78SBjoern A. Zeeb 		    lhw->max_rates, IEEE80211_RATE_MAXSIZE);
4546d9945d78SBjoern A. Zeeb 		lhw->max_rates = IEEE80211_RATE_MAXSIZE;
4547d9945d78SBjoern A. Zeeb 	}
4548d9945d78SBjoern A. Zeeb 
4549d9945d78SBjoern A. Zeeb 	/*
4550d9945d78SBjoern A. Zeeb 	 * The maximum supported bitrates on any band + size for
4551d9945d78SBjoern A. Zeeb 	 * DSSS Parameter Set give our per-band IE size.
4552d9945d78SBjoern A. Zeeb 	 * SSID is the responsibility of the driver and goes on the side.
4553d9945d78SBjoern A. Zeeb 	 * The user specified bits coming from the vap go into the
4554d9945d78SBjoern A. Zeeb 	 * "common ies" fields.
4555d9945d78SBjoern A. Zeeb 	 */
4556d9945d78SBjoern A. Zeeb 	lhw->scan_ie_len = 2 + IEEE80211_RATE_SIZE;
4557d9945d78SBjoern A. Zeeb 	if (lhw->max_rates > IEEE80211_RATE_SIZE)
4558d9945d78SBjoern A. Zeeb 		lhw->scan_ie_len += 2 + (lhw->max_rates - IEEE80211_RATE_SIZE);
455978ca45dfSBjoern A. Zeeb 
456078ca45dfSBjoern A. Zeeb 	if (hw->wiphy->features & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) {
4561d9945d78SBjoern A. Zeeb 		/*
456278ca45dfSBjoern A. Zeeb 		 * net80211 does not seem to support the DSSS Parameter Set but
456378ca45dfSBjoern A. Zeeb 		 * some of the drivers insert it so calculate the extra fixed
456478ca45dfSBjoern A. Zeeb 		 * space in.
4565d9945d78SBjoern A. Zeeb 		 */
4566d9945d78SBjoern A. Zeeb 		lhw->scan_ie_len += 2 + 1;
456778ca45dfSBjoern A. Zeeb 	}
4568d9945d78SBjoern A. Zeeb 
45699fb91463SBjoern A. Zeeb #if defined(LKPI_80211_HT)
45709fb91463SBjoern A. Zeeb 	if ((ic->ic_htcaps & IEEE80211_HTC_HT) != 0)
45719fb91463SBjoern A. Zeeb 		lhw->scan_ie_len += sizeof(struct ieee80211_ie_htcap);
45729fb91463SBjoern A. Zeeb #endif
45739fb91463SBjoern A. Zeeb #if defined(LKPI_80211_VHT)
45749fb91463SBjoern A. Zeeb 	if ((ic->ic_flags_ext & IEEE80211_FEXT_VHT) != 0)
45759fb91463SBjoern A. Zeeb 		lhw->scan_ie_len += 2 + sizeof(struct ieee80211_vht_cap);
45769fb91463SBjoern A. Zeeb #endif
45779fb91463SBjoern A. Zeeb 
4578d9945d78SBjoern A. Zeeb 	/* Reduce the max_scan_ie_len "left" by the amount we consume already. */
457978ca45dfSBjoern A. Zeeb 	if (hw->wiphy->max_scan_ie_len > 0) {
458078ca45dfSBjoern A. Zeeb 		if (lhw->scan_ie_len > hw->wiphy->max_scan_ie_len)
458178ca45dfSBjoern A. Zeeb 			goto err;
4582d9945d78SBjoern A. Zeeb 		hw->wiphy->max_scan_ie_len -= lhw->scan_ie_len;
458378ca45dfSBjoern A. Zeeb 	}
4584d9945d78SBjoern A. Zeeb 
45856b4cac81SBjoern A. Zeeb 	if (bootverbose)
45866b4cac81SBjoern A. Zeeb 		ieee80211_announce(ic);
45872e183d99SBjoern A. Zeeb 
45882e183d99SBjoern A. Zeeb 	return (0);
458978ca45dfSBjoern A. Zeeb err:
459078ca45dfSBjoern A. Zeeb 	IMPROVE("TODO FIXME CLEANUP");
459178ca45dfSBjoern A. Zeeb 	return (-EAGAIN);
45926b4cac81SBjoern A. Zeeb }
45936b4cac81SBjoern A. Zeeb 
45946b4cac81SBjoern A. Zeeb void
45956b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_ifdetach(struct ieee80211_hw *hw)
45966b4cac81SBjoern A. Zeeb {
45976b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
45986b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
45996b4cac81SBjoern A. Zeeb 
46006b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
46016b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
46026b4cac81SBjoern A. Zeeb 	ieee80211_ifdetach(ic);
46036b4cac81SBjoern A. Zeeb }
46046b4cac81SBjoern A. Zeeb 
46056b4cac81SBjoern A. Zeeb void
46066b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
46076b4cac81SBjoern A. Zeeb     enum ieee80211_iface_iter flags,
46086b4cac81SBjoern A. Zeeb     void(*iterfunc)(void *, uint8_t *, struct ieee80211_vif *),
46096b4cac81SBjoern A. Zeeb     void *arg)
46106b4cac81SBjoern A. Zeeb {
46116b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
46126b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
46136b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
461461a68e50SBjoern A. Zeeb 	bool active, atomic, nin_drv;
46156b4cac81SBjoern A. Zeeb 
46166b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
46176b4cac81SBjoern A. Zeeb 
46186b4cac81SBjoern A. Zeeb 	if (flags & ~(IEEE80211_IFACE_ITER_NORMAL|
46196b4cac81SBjoern A. Zeeb 	    IEEE80211_IFACE_ITER_RESUME_ALL|
462061a68e50SBjoern A. Zeeb 	    IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER|
4621adff403fSBjoern A. Zeeb 	    IEEE80211_IFACE_ITER_ACTIVE|IEEE80211_IFACE_ITER__ATOMIC)) {
46226b4cac81SBjoern A. Zeeb 		ic_printf(lhw->ic, "XXX TODO %s flags(%#x) not yet supported.\n",
46236b4cac81SBjoern A. Zeeb 		    __func__, flags);
46246b4cac81SBjoern A. Zeeb 	}
46256b4cac81SBjoern A. Zeeb 
4626adff403fSBjoern A. Zeeb 	active = (flags & IEEE80211_IFACE_ITER_ACTIVE) != 0;
46276b4cac81SBjoern A. Zeeb 	atomic = (flags & IEEE80211_IFACE_ITER__ATOMIC) != 0;
462861a68e50SBjoern A. Zeeb 	nin_drv = (flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) != 0;
46296b4cac81SBjoern A. Zeeb 
46306b4cac81SBjoern A. Zeeb 	if (atomic)
46318891c455SBjoern A. Zeeb 		LKPI_80211_LHW_LVIF_LOCK(lhw);
46326b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
46336b4cac81SBjoern A. Zeeb 		struct ieee80211vap *vap;
46346b4cac81SBjoern A. Zeeb 
46356b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
46366b4cac81SBjoern A. Zeeb 
46376b4cac81SBjoern A. Zeeb 		/*
46386b4cac81SBjoern A. Zeeb 		 * If we want "active" interfaces, we need to distinguish on
46396b4cac81SBjoern A. Zeeb 		 * whether the driver knows about them or not to be able to
46406b4cac81SBjoern A. Zeeb 		 * handle the "resume" case correctly.  Skip the ones the
46416b4cac81SBjoern A. Zeeb 		 * driver does not know about.
46426b4cac81SBjoern A. Zeeb 		 */
46436b4cac81SBjoern A. Zeeb 		if (active && !lvif->added_to_drv &&
46446b4cac81SBjoern A. Zeeb 		    (flags & IEEE80211_IFACE_ITER_RESUME_ALL) != 0)
46456b4cac81SBjoern A. Zeeb 			continue;
46466b4cac81SBjoern A. Zeeb 
46476b4cac81SBjoern A. Zeeb 		/*
464861a68e50SBjoern A. Zeeb 		 * If we shall skip interfaces not added to the driver do so
464961a68e50SBjoern A. Zeeb 		 * if we haven't yet.
465061a68e50SBjoern A. Zeeb 		 */
465161a68e50SBjoern A. Zeeb 		if (nin_drv && !lvif->added_to_drv)
465261a68e50SBjoern A. Zeeb 			continue;
465361a68e50SBjoern A. Zeeb 
465461a68e50SBjoern A. Zeeb 		/*
46556b4cac81SBjoern A. Zeeb 		 * Run the iterator function if we are either not asking
46566b4cac81SBjoern A. Zeeb 		 * asking for active only or if the VAP is "running".
46576b4cac81SBjoern A. Zeeb 		 */
46586b4cac81SBjoern A. Zeeb 		/* XXX-BZ probably should have state in the lvif as well. */
46596b4cac81SBjoern A. Zeeb 		vap = LVIF_TO_VAP(lvif);
46606b4cac81SBjoern A. Zeeb 		if (!active || (vap->iv_state != IEEE80211_S_INIT))
46616b4cac81SBjoern A. Zeeb 			iterfunc(arg, vif->addr, vif);
46626b4cac81SBjoern A. Zeeb 	}
46636b4cac81SBjoern A. Zeeb 	if (atomic)
46648891c455SBjoern A. Zeeb 		LKPI_80211_LHW_LVIF_UNLOCK(lhw);
46656b4cac81SBjoern A. Zeeb }
46666b4cac81SBjoern A. Zeeb 
46676b4cac81SBjoern A. Zeeb void
46686b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw,
46696b4cac81SBjoern A. Zeeb     struct ieee80211_vif *vif,
46706b4cac81SBjoern A. Zeeb     void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
46716b4cac81SBjoern A. Zeeb         struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
46726b4cac81SBjoern A. Zeeb     void *arg)
46736b4cac81SBjoern A. Zeeb {
46746b4cac81SBjoern A. Zeeb 
46756b4cac81SBjoern A. Zeeb 	UNIMPLEMENTED;
46766b4cac81SBjoern A. Zeeb }
46776b4cac81SBjoern A. Zeeb 
46786b4cac81SBjoern A. Zeeb void
46796b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
46806b4cac81SBjoern A. Zeeb     void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *,
46816b4cac81SBjoern A. Zeeb 	void *),
46826b4cac81SBjoern A. Zeeb     void *arg)
46836b4cac81SBjoern A. Zeeb {
4684c5e25798SBjoern A. Zeeb 	struct lkpi_hw *lhw;
4685c5e25798SBjoern A. Zeeb 	struct lkpi_vif *lvif;
4686c5e25798SBjoern A. Zeeb 	struct ieee80211_vif *vif;
4687c5e25798SBjoern A. Zeeb 	struct lkpi_chanctx *lchanctx;
46886b4cac81SBjoern A. Zeeb 
4689c5e25798SBjoern A. Zeeb 	KASSERT(hw != NULL && iterfunc != NULL,
4690c5e25798SBjoern A. Zeeb 	    ("%s: hw %p iterfunc %p arg %p\n", __func__, hw, iterfunc, arg));
4691c5e25798SBjoern A. Zeeb 
4692c5e25798SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
4693c5e25798SBjoern A. Zeeb 
4694c5e25798SBjoern A. Zeeb 	IMPROVE("lchanctx should be its own list somewhere");
4695c5e25798SBjoern A. Zeeb 
4696c5e25798SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
4697c5e25798SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
4698c5e25798SBjoern A. Zeeb 
4699c5e25798SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
4700c5e25798SBjoern A. Zeeb 		if (vif->chanctx_conf == NULL)
4701c5e25798SBjoern A. Zeeb 			continue;
4702c5e25798SBjoern A. Zeeb 
4703c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(vif->chanctx_conf);
4704c5e25798SBjoern A. Zeeb 		if (!lchanctx->added_to_drv)
4705c5e25798SBjoern A. Zeeb 			continue;
4706c5e25798SBjoern A. Zeeb 
4707c5e25798SBjoern A. Zeeb 		iterfunc(hw, &lchanctx->conf, arg);
4708c5e25798SBjoern A. Zeeb 	}
4709c5e25798SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
47106b4cac81SBjoern A. Zeeb }
47116b4cac81SBjoern A. Zeeb 
47126b4cac81SBjoern A. Zeeb void
47136b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
47146b4cac81SBjoern A. Zeeb    void (*iterfunc)(void *, struct ieee80211_sta *), void *arg)
47156b4cac81SBjoern A. Zeeb {
47166b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47176b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
47186b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
47196b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
47206b4cac81SBjoern A. Zeeb 
47216b4cac81SBjoern A. Zeeb 	KASSERT(hw != NULL && iterfunc != NULL,
47226b4cac81SBjoern A. Zeeb 	    ("%s: hw %p iterfunc %p arg %p\n", __func__, hw, iterfunc, arg));
47236b4cac81SBjoern A. Zeeb 
47246b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
47256b4cac81SBjoern A. Zeeb 
47268891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
47276b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
47286b4cac81SBjoern A. Zeeb 
47296b4cac81SBjoern A. Zeeb 		LKPI_80211_LVIF_LOCK(lvif);
47306b4cac81SBjoern A. Zeeb 		TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
47316b4cac81SBjoern A. Zeeb 			if (!lsta->added_to_drv)
47326b4cac81SBjoern A. Zeeb 				continue;
47336b4cac81SBjoern A. Zeeb 			sta = LSTA_TO_STA(lsta);
47346b4cac81SBjoern A. Zeeb 			iterfunc(arg, sta);
47356b4cac81SBjoern A. Zeeb 		}
47366b4cac81SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
47376b4cac81SBjoern A. Zeeb 	}
47388891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
47396b4cac81SBjoern A. Zeeb }
47406b4cac81SBjoern A. Zeeb 
4741673d62fcSBjoern A. Zeeb struct linuxkpi_ieee80211_regdomain *
4742673d62fcSBjoern A. Zeeb lkpi_get_linuxkpi_ieee80211_regdomain(size_t n)
4743673d62fcSBjoern A. Zeeb {
4744673d62fcSBjoern A. Zeeb 	struct linuxkpi_ieee80211_regdomain *regd;
4745673d62fcSBjoern A. Zeeb 
4746673d62fcSBjoern A. Zeeb 	regd = kzalloc(sizeof(*regd) + n * sizeof(struct ieee80211_reg_rule),
4747673d62fcSBjoern A. Zeeb 	    GFP_KERNEL);
4748673d62fcSBjoern A. Zeeb 	return (regd);
4749673d62fcSBjoern A. Zeeb }
4750673d62fcSBjoern A. Zeeb 
47516b4cac81SBjoern A. Zeeb int
47526b4cac81SBjoern A. Zeeb linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
47536b4cac81SBjoern A. Zeeb     struct linuxkpi_ieee80211_regdomain *regd)
47546b4cac81SBjoern A. Zeeb {
47556b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47566b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
47576b4cac81SBjoern A. Zeeb 	struct ieee80211_regdomain *rd;
47586b4cac81SBjoern A. Zeeb 
47596b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
47606b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
47616b4cac81SBjoern A. Zeeb 
47626b4cac81SBjoern A. Zeeb 	rd = &ic->ic_regdomain;
47636b4cac81SBjoern A. Zeeb 	if (rd->isocc[0] == '\0') {
47646b4cac81SBjoern A. Zeeb 		rd->isocc[0] = regd->alpha2[0];
47656b4cac81SBjoern A. Zeeb 		rd->isocc[1] = regd->alpha2[1];
47666b4cac81SBjoern A. Zeeb 	}
47676b4cac81SBjoern A. Zeeb 
47686b4cac81SBjoern A. Zeeb 	TODO();
47696b4cac81SBjoern A. Zeeb 	/* XXX-BZ finish the rest. */
47706b4cac81SBjoern A. Zeeb 
47716b4cac81SBjoern A. Zeeb 	return (0);
47726b4cac81SBjoern A. Zeeb }
47736b4cac81SBjoern A. Zeeb 
47746b4cac81SBjoern A. Zeeb void
47756b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw,
47766b4cac81SBjoern A. Zeeb     struct cfg80211_scan_info *info)
47776b4cac81SBjoern A. Zeeb {
47786b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47796b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
47806b4cac81SBjoern A. Zeeb 	struct ieee80211_scan_state *ss;
47816b4cac81SBjoern A. Zeeb 
47826b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
47836b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
47846b4cac81SBjoern A. Zeeb 	ss = ic->ic_scan;
47856b4cac81SBjoern A. Zeeb 
47866b4cac81SBjoern A. Zeeb 	ieee80211_scan_done(ss->ss_vap);
47876b4cac81SBjoern A. Zeeb 
47888ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
47896b4cac81SBjoern A. Zeeb 	free(lhw->hw_req, M_LKPI80211);
47906b4cac81SBjoern A. Zeeb 	lhw->hw_req = NULL;
4791a486fbbdSBjoern A. Zeeb 	lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
47926b4cac81SBjoern A. Zeeb 	wakeup(lhw);
47938ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
47946b4cac81SBjoern A. Zeeb 
47956b4cac81SBjoern A. Zeeb 	return;
47966b4cac81SBjoern A. Zeeb }
47976b4cac81SBjoern A. Zeeb 
4798e30e05d3SBjoern A. Zeeb /* For %list see comment towards the end of the function. */
47996b4cac81SBjoern A. Zeeb void
48006b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
4801e30e05d3SBjoern A. Zeeb     struct ieee80211_sta *sta, struct napi_struct *napi __unused,
4802e30e05d3SBjoern A. Zeeb     struct list_head *list __unused)
48036b4cac81SBjoern A. Zeeb {
48046b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
48056b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
48066b4cac81SBjoern A. Zeeb 	struct mbuf *m;
48076b4cac81SBjoern A. Zeeb 	struct skb_shared_info *shinfo;
48086b4cac81SBjoern A. Zeeb 	struct ieee80211_rx_status *rx_status;
48096b4cac81SBjoern A. Zeeb 	struct ieee80211_rx_stats rx_stats;
48106b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
48116b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
48126b4cac81SBjoern A. Zeeb 	struct ieee80211_hdr *hdr;
48136b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
48149d9ba2b7SBjoern A. Zeeb 	int i, offset, ok;
4815170acccfSBjoern A. Zeeb 	int8_t rssi;
4816c0cadd99SBjoern A. Zeeb 	bool is_beacon;
48176b4cac81SBjoern A. Zeeb 
48186b4cac81SBjoern A. Zeeb 	if (skb->len < 2) {
48196b4cac81SBjoern A. Zeeb 		/* Need 80211 stats here. */
48206b4cac81SBjoern A. Zeeb 		IMPROVE();
48216b4cac81SBjoern A. Zeeb 		goto err;
48226b4cac81SBjoern A. Zeeb 	}
48236b4cac81SBjoern A. Zeeb 
48246b4cac81SBjoern A. Zeeb 	/*
48256b4cac81SBjoern A. Zeeb 	 * For now do the data copy; we can later improve things. Might even
48266b4cac81SBjoern A. Zeeb 	 * have an mbuf backing the skb data then?
48276b4cac81SBjoern A. Zeeb 	 */
48286b4cac81SBjoern A. Zeeb 	m = m_get2(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR);
48296b4cac81SBjoern A. Zeeb 	if (m == NULL)
48306b4cac81SBjoern A. Zeeb 		goto err;
48316b4cac81SBjoern A. Zeeb 	m_copyback(m, 0, skb->tail - skb->data, skb->data);
48326b4cac81SBjoern A. Zeeb 
48336b4cac81SBjoern A. Zeeb 	shinfo = skb_shinfo(skb);
48346b4cac81SBjoern A. Zeeb 	offset = m->m_len;
48356b4cac81SBjoern A. Zeeb 	for (i = 0; i < shinfo->nr_frags; i++) {
48366b4cac81SBjoern A. Zeeb 		m_copyback(m, offset, shinfo->frags[i].size,
48376b4cac81SBjoern A. Zeeb 		    (uint8_t *)linux_page_address(shinfo->frags[i].page) +
48386b4cac81SBjoern A. Zeeb 		    shinfo->frags[i].offset);
48396b4cac81SBjoern A. Zeeb 		offset += shinfo->frags[i].size;
48406b4cac81SBjoern A. Zeeb 	}
48416b4cac81SBjoern A. Zeeb 
48426b4cac81SBjoern A. Zeeb 	rx_status = IEEE80211_SKB_RXCB(skb);
48436b4cac81SBjoern A. Zeeb 
48446b4cac81SBjoern A. Zeeb 	hdr = (void *)skb->data;
4845c0cadd99SBjoern A. Zeeb 	is_beacon = ieee80211_is_beacon(hdr->frame_control);
4846c0cadd99SBjoern A. Zeeb 
4847c0cadd99SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
48489d9ba2b7SBjoern A. Zeeb 	if (is_beacon && (linuxkpi_debug_80211 & D80211_TRACE_RX_BEACONS) == 0)
48496b4cac81SBjoern A. Zeeb 		goto no_trace_beacons;
48506b4cac81SBjoern A. Zeeb 
48519d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
48526b4cac81SBjoern A. Zeeb 		printf("TRACE-RX: %s: skb %p a/l/d/t-len (%u/%u/%u/%u) "
4853c0cadd99SBjoern A. Zeeb 		    "h %p d %p t %p e %p sh %p (%u) m %p plen %u len %u%s\n",
48546b4cac81SBjoern A. Zeeb 		    __func__, skb, skb->_alloc_len, skb->len, skb->data_len,
48556b4cac81SBjoern A. Zeeb 		    skb->truesize, skb->head, skb->data, skb->tail, skb->end,
48566b4cac81SBjoern A. Zeeb 		    shinfo, shinfo->nr_frags,
4857c0cadd99SBjoern A. Zeeb 		    m, m->m_pkthdr.len, m->m_len, is_beacon ? " beacon" : "");
48586b4cac81SBjoern A. Zeeb 
48599d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX_DUMP)
48606b4cac81SBjoern A. Zeeb 		hexdump(mtod(m, const void *), m->m_len, "RX (raw) ", 0);
48616b4cac81SBjoern A. Zeeb 
48626b4cac81SBjoern A. Zeeb 	/* Implement a dump_rxcb() !!! */
48639d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
4864c8dafefaSBjoern A. Zeeb 		printf("TRACE %s: RXCB: %ju %ju %u, %#0x, %u, %#0x, %#0x, "
486560970a32SBjoern A. Zeeb 		    "%u band %u, %u { %d %d %d %d }, %d, %#x %#x %#x %#x %u %u %u\n",
48666b4cac81SBjoern A. Zeeb 			__func__,
4867c8dafefaSBjoern A. Zeeb 			(uintmax_t)rx_status->boottime_ns,
4868c8dafefaSBjoern A. Zeeb 			(uintmax_t)rx_status->mactime,
48696b4cac81SBjoern A. Zeeb 			rx_status->device_timestamp,
48706b4cac81SBjoern A. Zeeb 			rx_status->flag,
48716b4cac81SBjoern A. Zeeb 			rx_status->freq,
48726b4cac81SBjoern A. Zeeb 			rx_status->bw,
48736b4cac81SBjoern A. Zeeb 			rx_status->encoding,
48746b4cac81SBjoern A. Zeeb 			rx_status->ampdu_reference,
48756b4cac81SBjoern A. Zeeb 			rx_status->band,
48766b4cac81SBjoern A. Zeeb 			rx_status->chains,
48776b4cac81SBjoern A. Zeeb 			rx_status->chain_signal[0],
48786b4cac81SBjoern A. Zeeb 			rx_status->chain_signal[1],
48796b4cac81SBjoern A. Zeeb 			rx_status->chain_signal[2],
488060970a32SBjoern A. Zeeb 			rx_status->chain_signal[3],
48816b4cac81SBjoern A. Zeeb 			rx_status->signal,
48826b4cac81SBjoern A. Zeeb 			rx_status->enc_flags,
48836b4cac81SBjoern A. Zeeb 			rx_status->he_dcm,
48846b4cac81SBjoern A. Zeeb 			rx_status->he_gi,
48856b4cac81SBjoern A. Zeeb 			rx_status->he_ru,
48866b4cac81SBjoern A. Zeeb 			rx_status->zero_length_psdu_type,
48876b4cac81SBjoern A. Zeeb 			rx_status->nss,
48886b4cac81SBjoern A. Zeeb 			rx_status->rate_idx);
48896b4cac81SBjoern A. Zeeb no_trace_beacons:
48906b4cac81SBjoern A. Zeeb #endif
48916b4cac81SBjoern A. Zeeb 
48926b4cac81SBjoern A. Zeeb 	memset(&rx_stats, 0, sizeof(rx_stats));
48936b4cac81SBjoern A. Zeeb 	rx_stats.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
489460970a32SBjoern A. Zeeb 	/* XXX-BZ correct hardcoded rssi and noise floor, how? survey? */
489560970a32SBjoern A. Zeeb 	rx_stats.c_nf = -96;
48966b4cac81SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, SIGNAL_DBM) &&
48976b4cac81SBjoern A. Zeeb 	    !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
4898170acccfSBjoern A. Zeeb 		rssi = rx_status->signal;
48996b4cac81SBjoern A. Zeeb 	else
4900170acccfSBjoern A. Zeeb 		rssi = rx_stats.c_nf;
4901170acccfSBjoern A. Zeeb 	/*
4902170acccfSBjoern A. Zeeb 	 * net80211 signal strength data are in .5 dBm units relative to
4903170acccfSBjoern A. Zeeb 	 * the current noise floor (see comment in ieee80211_node.h).
4904170acccfSBjoern A. Zeeb 	 */
4905170acccfSBjoern A. Zeeb 	rssi -= rx_stats.c_nf;
4906170acccfSBjoern A. Zeeb 	rx_stats.c_rssi = rssi * 2;
49076b4cac81SBjoern A. Zeeb 	rx_stats.r_flags |= IEEE80211_R_BAND;
49086b4cac81SBjoern A. Zeeb 	rx_stats.c_band =
49096b4cac81SBjoern A. Zeeb 	    lkpi_nl80211_band_to_net80211_band(rx_status->band);
49106b4cac81SBjoern A. Zeeb 	rx_stats.r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE;
49116b4cac81SBjoern A. Zeeb 	rx_stats.c_freq = rx_status->freq;
49126b4cac81SBjoern A. Zeeb 	rx_stats.c_ieee = ieee80211_mhz2ieee(rx_stats.c_freq, rx_stats.c_band);
49136b4cac81SBjoern A. Zeeb 
49146b4cac81SBjoern A. Zeeb 	/* XXX (*sta_statistics)() to get to some of that? */
49156b4cac81SBjoern A. Zeeb 	/* XXX-BZ dump the FreeBSD version of rx_stats as well! */
49166b4cac81SBjoern A. Zeeb 
49176b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
49186b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
49196b4cac81SBjoern A. Zeeb 
49206b4cac81SBjoern A. Zeeb 	ok = ieee80211_add_rx_params(m, &rx_stats);
49216b4cac81SBjoern A. Zeeb 	if (ok == 0) {
4922dbc06dd9SBjoern A. Zeeb 		m_freem(m);
49236b4cac81SBjoern A. Zeeb 		counter_u64_add(ic->ic_ierrors, 1);
49246b4cac81SBjoern A. Zeeb 		goto err;
49256b4cac81SBjoern A. Zeeb 	}
49266b4cac81SBjoern A. Zeeb 
49276b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
49286b4cac81SBjoern A. Zeeb 		lsta = STA_TO_LSTA(sta);
49296b4cac81SBjoern A. Zeeb 		ni = ieee80211_ref_node(lsta->ni);
49306b4cac81SBjoern A. Zeeb 	} else {
4931c8dafefaSBjoern A. Zeeb 		struct ieee80211_frame_min *wh;
4932c8dafefaSBjoern A. Zeeb 
49336b4cac81SBjoern A. Zeeb 		wh = mtod(m, struct ieee80211_frame_min *);
49346b4cac81SBjoern A. Zeeb 		ni = ieee80211_find_rxnode(ic, wh);
49356b4cac81SBjoern A. Zeeb 		if (ni != NULL)
49366b4cac81SBjoern A. Zeeb 			lsta = ni->ni_drv_data;
49376b4cac81SBjoern A. Zeeb 	}
49386b4cac81SBjoern A. Zeeb 
49396b4cac81SBjoern A. Zeeb 	if (ni != NULL)
49406b4cac81SBjoern A. Zeeb 		vap = ni->ni_vap;
49416b4cac81SBjoern A. Zeeb 	else
49426b4cac81SBjoern A. Zeeb 		/*
49436b4cac81SBjoern A. Zeeb 		 * XXX-BZ can we improve this by looking at the frame hdr
49446b4cac81SBjoern A. Zeeb 		 * or other meta-data passed up?
49456b4cac81SBjoern A. Zeeb 		 */
49466b4cac81SBjoern A. Zeeb 		vap = TAILQ_FIRST(&ic->ic_vaps);
49476b4cac81SBjoern A. Zeeb 
49489d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
49499d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
4950c0cadd99SBjoern A. Zeeb 		printf("TRACE %s: sta %p lsta %p state %d ni %p vap %p%s\n",
4951c0cadd99SBjoern A. Zeeb 		    __func__, sta, lsta, (lsta != NULL) ? lsta->state : -1,
4952c0cadd99SBjoern A. Zeeb 		    ni, vap, is_beacon ? " beacon" : "");
49539d9ba2b7SBjoern A. Zeeb #endif
49546b4cac81SBjoern A. Zeeb 
4955c0cadd99SBjoern A. Zeeb 	if (ni != NULL && vap != NULL && is_beacon &&
4956c8dafefaSBjoern A. Zeeb 	    rx_status->device_timestamp > 0 &&
4957c8dafefaSBjoern A. Zeeb 	    m->m_pkthdr.len >= sizeof(struct ieee80211_frame)) {
4958c8dafefaSBjoern A. Zeeb 		struct lkpi_vif *lvif;
4959c8dafefaSBjoern A. Zeeb 		struct ieee80211_vif *vif;
4960c8dafefaSBjoern A. Zeeb 		struct ieee80211_frame *wh;
4961c8dafefaSBjoern A. Zeeb 
4962c8dafefaSBjoern A. Zeeb 		wh = mtod(m, struct ieee80211_frame *);
4963c8dafefaSBjoern A. Zeeb 		if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))
4964c8dafefaSBjoern A. Zeeb 			goto skip_device_ts;
4965c8dafefaSBjoern A. Zeeb 
4966c8dafefaSBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
4967c8dafefaSBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
4968c8dafefaSBjoern A. Zeeb 
4969c8dafefaSBjoern A. Zeeb 		IMPROVE("TIMING_BEACON_ONLY?");
4970c8dafefaSBjoern A. Zeeb 		/* mac80211 specific (not net80211) so keep it here. */
4971c8dafefaSBjoern A. Zeeb 		vif->bss_conf.sync_device_ts = rx_status->device_timestamp;
4972c8dafefaSBjoern A. Zeeb 		/*
4973c8dafefaSBjoern A. Zeeb 		 * net80211 should take care of the other information (sync_tsf,
4974c8dafefaSBjoern A. Zeeb 		 * sync_dtim_count) as otherwise we need to parse the beacon.
4975c8dafefaSBjoern A. Zeeb 		 */
4976c8dafefaSBjoern A. Zeeb skip_device_ts:
49779fb91463SBjoern A. Zeeb 		;
49789fb91463SBjoern A. Zeeb 	}
4979c8dafefaSBjoern A. Zeeb 
49806b4cac81SBjoern A. Zeeb 	if (vap != NULL && vap->iv_state > IEEE80211_S_INIT &&
49816b4cac81SBjoern A. Zeeb 	    ieee80211_radiotap_active_vap(vap)) {
49826b4cac81SBjoern A. Zeeb 		struct lkpi_radiotap_rx_hdr *rtap;
49836b4cac81SBjoern A. Zeeb 
49846b4cac81SBjoern A. Zeeb 		rtap = &lhw->rtap_rx;
49856b4cac81SBjoern A. Zeeb 		rtap->wr_tsft = rx_status->device_timestamp;
49866b4cac81SBjoern A. Zeeb 		rtap->wr_flags = 0;
49876b4cac81SBjoern A. Zeeb 		if (rx_status->enc_flags & RX_ENC_FLAG_SHORTPRE)
49886b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
49896b4cac81SBjoern A. Zeeb 		if (rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI)
49906b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
49916b4cac81SBjoern A. Zeeb #if 0	/* .. or it does not given we strip it below. */
49926b4cac81SBjoern A. Zeeb 		if (ieee80211_hw_check(hw, RX_INCLUDES_FCS))
49936b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_FCS;
49946b4cac81SBjoern A. Zeeb #endif
49956b4cac81SBjoern A. Zeeb 		if (rx_status->flag & RX_FLAG_FAILED_FCS_CRC)
49966b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
49976b4cac81SBjoern A. Zeeb 		rtap->wr_rate = 0;
49986b4cac81SBjoern A. Zeeb 		IMPROVE();
49996b4cac81SBjoern A. Zeeb 		/* XXX TODO status->encoding / rate_index / bw */
50006b4cac81SBjoern A. Zeeb 		rtap->wr_chan_freq = htole16(rx_stats.c_freq);
50016b4cac81SBjoern A. Zeeb 		if (ic->ic_curchan->ic_ieee == rx_stats.c_ieee)
50026b4cac81SBjoern A. Zeeb 			rtap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
5003170acccfSBjoern A. Zeeb 		rtap->wr_dbm_antsignal = rssi;
50046b4cac81SBjoern A. Zeeb 		rtap->wr_dbm_antnoise = rx_stats.c_nf;
50056b4cac81SBjoern A. Zeeb 	}
50066b4cac81SBjoern A. Zeeb 
50076b4cac81SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, RX_INCLUDES_FCS))
50086b4cac81SBjoern A. Zeeb 		m_adj(m, -IEEE80211_CRC_LEN);
50096b4cac81SBjoern A. Zeeb 
5010e30e05d3SBjoern A. Zeeb #if 0
5011e30e05d3SBjoern A. Zeeb 	if (list != NULL) {
5012e30e05d3SBjoern A. Zeeb 		/*
5013e30e05d3SBjoern A. Zeeb 		* Normally this would be queued up and delivered by
5014e30e05d3SBjoern A. Zeeb 		* netif_receive_skb_list(), napi_gro_receive(), or the like.
5015e30e05d3SBjoern A. Zeeb 		* See mt76::mac80211.c as only current possible consumer.
5016e30e05d3SBjoern A. Zeeb 		*/
5017e30e05d3SBjoern A. Zeeb 		IMPROVE("we simply pass the packet to net80211 to deal with.");
5018e30e05d3SBjoern A. Zeeb 	}
5019e30e05d3SBjoern A. Zeeb #endif
5020e30e05d3SBjoern A. Zeeb 
50216b4cac81SBjoern A. Zeeb 	if (ni != NULL) {
50229d9ba2b7SBjoern A. Zeeb 		ok = ieee80211_input_mimo(ni, m);
50236b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
5024dbc06dd9SBjoern A. Zeeb 		if (ok < 0)
5025dbc06dd9SBjoern A. Zeeb 			m_freem(m);
50266b4cac81SBjoern A. Zeeb 	} else {
50279d9ba2b7SBjoern A. Zeeb 		ok = ieee80211_input_mimo_all(ic, m);
5028dbc06dd9SBjoern A. Zeeb 		/* mbuf got consumed. */
50296b4cac81SBjoern A. Zeeb 	}
50306b4cac81SBjoern A. Zeeb 
50319d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
50329d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
50339d9ba2b7SBjoern A. Zeeb 		printf("TRACE %s: handled frame type %#0x\n", __func__, ok);
50349d9ba2b7SBjoern A. Zeeb #endif
50356b4cac81SBjoern A. Zeeb 
50366b4cac81SBjoern A. Zeeb 	IMPROVE();
50376b4cac81SBjoern A. Zeeb 
50386b4cac81SBjoern A. Zeeb err:
50396b4cac81SBjoern A. Zeeb 	/* The skb is ours so we can free it :-) */
50406b4cac81SBjoern A. Zeeb 	kfree_skb(skb);
50416b4cac81SBjoern A. Zeeb }
50426b4cac81SBjoern A. Zeeb 
50436b4cac81SBjoern A. Zeeb uint8_t
5044ec190d91SBjoern A. Zeeb linuxkpi_ieee80211_get_tid(struct ieee80211_hdr *hdr, bool nonqos_ok)
50456b4cac81SBjoern A. Zeeb {
50466b4cac81SBjoern A. Zeeb 	const struct ieee80211_frame *wh;
5047ec190d91SBjoern A. Zeeb 	uint8_t tid;
5048ec190d91SBjoern A. Zeeb 
5049ec190d91SBjoern A. Zeeb 	/* Linux seems to assume this is a QOS-Data-Frame */
5050ec190d91SBjoern A. Zeeb 	KASSERT(nonqos_ok || ieee80211_is_data_qos(hdr->frame_control),
5051ec190d91SBjoern A. Zeeb 	   ("%s: hdr %p fc %#06x not qos_data\n", __func__, hdr,
5052ec190d91SBjoern A. Zeeb 	   hdr->frame_control));
50536b4cac81SBjoern A. Zeeb 
50546b4cac81SBjoern A. Zeeb 	wh = (const struct ieee80211_frame *)hdr;
5055ec190d91SBjoern A. Zeeb 	tid = ieee80211_gettid(wh);
5056ec190d91SBjoern A. Zeeb 	KASSERT(nonqos_ok || tid == (tid & IEEE80211_QOS_TID), ("%s: tid %u "
5057ec190d91SBjoern A. Zeeb 	   "not expected (%u?)\n", __func__, tid, IEEE80211_NONQOS_TID));
5058ec190d91SBjoern A. Zeeb 
5059ec190d91SBjoern A. Zeeb 	return (tid);
50606b4cac81SBjoern A. Zeeb }
50616b4cac81SBjoern A. Zeeb 
50626b4cac81SBjoern A. Zeeb struct wiphy *
50636b4cac81SBjoern A. Zeeb linuxkpi_wiphy_new(const struct cfg80211_ops *ops, size_t priv_len)
50646b4cac81SBjoern A. Zeeb {
50656b4cac81SBjoern A. Zeeb 	struct lkpi_wiphy *lwiphy;
50666b4cac81SBjoern A. Zeeb 
50676b4cac81SBjoern A. Zeeb 	lwiphy = kzalloc(sizeof(*lwiphy) + priv_len, GFP_KERNEL);
50686b4cac81SBjoern A. Zeeb 	if (lwiphy == NULL)
50696b4cac81SBjoern A. Zeeb 		return (NULL);
50706b4cac81SBjoern A. Zeeb 	lwiphy->ops = ops;
50716b4cac81SBjoern A. Zeeb 
50726b4cac81SBjoern A. Zeeb 	/* XXX TODO */
50736b4cac81SBjoern A. Zeeb 	return (LWIPHY_TO_WIPHY(lwiphy));
50746b4cac81SBjoern A. Zeeb }
50756b4cac81SBjoern A. Zeeb 
50766b4cac81SBjoern A. Zeeb void
50776b4cac81SBjoern A. Zeeb linuxkpi_wiphy_free(struct wiphy *wiphy)
50786b4cac81SBjoern A. Zeeb {
50796b4cac81SBjoern A. Zeeb 	struct lkpi_wiphy *lwiphy;
50806b4cac81SBjoern A. Zeeb 
50816b4cac81SBjoern A. Zeeb 	if (wiphy == NULL)
50826b4cac81SBjoern A. Zeeb 		return;
50836b4cac81SBjoern A. Zeeb 
50846b4cac81SBjoern A. Zeeb 	lwiphy = WIPHY_TO_LWIPHY(wiphy);
50856b4cac81SBjoern A. Zeeb 	kfree(lwiphy);
50866b4cac81SBjoern A. Zeeb }
50876b4cac81SBjoern A. Zeeb 
50886b4cac81SBjoern A. Zeeb uint32_t
50896b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_channel_to_frequency(uint32_t channel,
50906b4cac81SBjoern A. Zeeb     enum nl80211_band band)
50916b4cac81SBjoern A. Zeeb {
50926b4cac81SBjoern A. Zeeb 
50936b4cac81SBjoern A. Zeeb 	switch (band) {
50946b4cac81SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
50956b4cac81SBjoern A. Zeeb 		return (ieee80211_ieee2mhz(channel, IEEE80211_CHAN_2GHZ));
50966b4cac81SBjoern A. Zeeb 		break;
50976b4cac81SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
50986b4cac81SBjoern A. Zeeb 		return (ieee80211_ieee2mhz(channel, IEEE80211_CHAN_5GHZ));
50996b4cac81SBjoern A. Zeeb 		break;
51006b4cac81SBjoern A. Zeeb 	default:
51016b4cac81SBjoern A. Zeeb 		/* XXX abort, retry, error, panic? */
51026b4cac81SBjoern A. Zeeb 		break;
51036b4cac81SBjoern A. Zeeb 	}
51046b4cac81SBjoern A. Zeeb 
51056b4cac81SBjoern A. Zeeb 	return (0);
51066b4cac81SBjoern A. Zeeb }
51076b4cac81SBjoern A. Zeeb 
51086b4cac81SBjoern A. Zeeb uint32_t
51096b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_frequency_to_channel(uint32_t freq, uint32_t flags __unused)
51106b4cac81SBjoern A. Zeeb {
51116b4cac81SBjoern A. Zeeb 
51126b4cac81SBjoern A. Zeeb 	return (ieee80211_mhz2ieee(freq, 0));
51136b4cac81SBjoern A. Zeeb }
51146b4cac81SBjoern A. Zeeb 
51156b4cac81SBjoern A. Zeeb static struct lkpi_sta *
51166b4cac81SBjoern A. Zeeb lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni)
51176b4cac81SBjoern A. Zeeb {
51186b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta, *temp;
51196b4cac81SBjoern A. Zeeb 
51206b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
51216b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) {
51226b4cac81SBjoern A. Zeeb 		if (lsta->ni == ni) {
51236b4cac81SBjoern A. Zeeb 			LKPI_80211_LVIF_UNLOCK(lvif);
51246b4cac81SBjoern A. Zeeb 			return (lsta);
51256b4cac81SBjoern A. Zeeb 		}
51266b4cac81SBjoern A. Zeeb 	}
51276b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
51286b4cac81SBjoern A. Zeeb 
51296b4cac81SBjoern A. Zeeb 	return (NULL);
51306b4cac81SBjoern A. Zeeb }
51316b4cac81SBjoern A. Zeeb 
51326b4cac81SBjoern A. Zeeb struct ieee80211_sta *
51336b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer)
51346b4cac81SBjoern A. Zeeb {
51356b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
51366b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta, *temp;
51376b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
51386b4cac81SBjoern A. Zeeb 
51396b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
51406b4cac81SBjoern A. Zeeb 
51416b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
51426b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) {
51436b4cac81SBjoern A. Zeeb 		sta = LSTA_TO_STA(lsta);
51446b4cac81SBjoern A. Zeeb 		if (IEEE80211_ADDR_EQ(sta->addr, peer)) {
51456b4cac81SBjoern A. Zeeb 			LKPI_80211_LVIF_UNLOCK(lvif);
51466b4cac81SBjoern A. Zeeb 			return (sta);
51476b4cac81SBjoern A. Zeeb 		}
51486b4cac81SBjoern A. Zeeb 	}
51496b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
51506b4cac81SBjoern A. Zeeb 	return (NULL);
51516b4cac81SBjoern A. Zeeb }
51526b4cac81SBjoern A. Zeeb 
51536b4cac81SBjoern A. Zeeb struct ieee80211_sta *
51542e183d99SBjoern A. Zeeb linuxkpi_ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
51552e183d99SBjoern A. Zeeb     const uint8_t *addr, const uint8_t *ourvifaddr)
51566b4cac81SBjoern A. Zeeb {
51576b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
51586b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
51596b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
51606b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
51616b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
51626b4cac81SBjoern A. Zeeb 
51636b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
51646b4cac81SBjoern A. Zeeb 	sta = NULL;
51656b4cac81SBjoern A. Zeeb 
51668891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
51676b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
51686b4cac81SBjoern A. Zeeb 
51696b4cac81SBjoern A. Zeeb 		/* XXX-BZ check our address from the vif. */
51706b4cac81SBjoern A. Zeeb 
51716b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
51726b4cac81SBjoern A. Zeeb 		if (ourvifaddr != NULL &&
51736b4cac81SBjoern A. Zeeb 		    !IEEE80211_ADDR_EQ(vif->addr, ourvifaddr))
51746b4cac81SBjoern A. Zeeb 			continue;
51756b4cac81SBjoern A. Zeeb 		sta = linuxkpi_ieee80211_find_sta(vif, addr);
51766b4cac81SBjoern A. Zeeb 		if (sta != NULL)
51776b4cac81SBjoern A. Zeeb 			break;
51786b4cac81SBjoern A. Zeeb 	}
51798891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
51806b4cac81SBjoern A. Zeeb 
51816b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
51826b4cac81SBjoern A. Zeeb 		lsta = STA_TO_LSTA(sta);
51836b4cac81SBjoern A. Zeeb 		if (!lsta->added_to_drv)
51846b4cac81SBjoern A. Zeeb 			return (NULL);
51856b4cac81SBjoern A. Zeeb 	}
51866b4cac81SBjoern A. Zeeb 
51876b4cac81SBjoern A. Zeeb 	return (sta);
51886b4cac81SBjoern A. Zeeb }
51896b4cac81SBjoern A. Zeeb 
51906b4cac81SBjoern A. Zeeb struct sk_buff *
51916b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
51926b4cac81SBjoern A. Zeeb     struct ieee80211_txq *txq)
51936b4cac81SBjoern A. Zeeb {
51946b4cac81SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
51950cbcfa19SBjoern A. Zeeb 	struct lkpi_vif *lvif;
51966b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
51976b4cac81SBjoern A. Zeeb 
51980cbcfa19SBjoern A. Zeeb 	skb = NULL;
51996b4cac81SBjoern A. Zeeb 	ltxq = TXQ_TO_LTXQ(txq);
52006b4cac81SBjoern A. Zeeb 	ltxq->seen_dequeue = true;
52016b4cac81SBjoern A. Zeeb 
52020cbcfa19SBjoern A. Zeeb 	if (ltxq->stopped)
52030cbcfa19SBjoern A. Zeeb 		goto stopped;
52040cbcfa19SBjoern A. Zeeb 
52050cbcfa19SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(ltxq->txq.vif);
52060cbcfa19SBjoern A. Zeeb 	if (lvif->hw_queue_stopped[ltxq->txq.ac]) {
52070cbcfa19SBjoern A. Zeeb 		ltxq->stopped = true;
52080cbcfa19SBjoern A. Zeeb 		goto stopped;
52090cbcfa19SBjoern A. Zeeb 	}
52100cbcfa19SBjoern A. Zeeb 
5211eac3646fSBjoern A. Zeeb 	IMPROVE("hw(TX_FRAG_LIST)");
5212eac3646fSBjoern A. Zeeb 
5213eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_LOCK(ltxq);
52146b4cac81SBjoern A. Zeeb 	skb = skb_dequeue(&ltxq->skbq);
5215eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_UNLOCK(ltxq);
52166b4cac81SBjoern A. Zeeb 
52170cbcfa19SBjoern A. Zeeb stopped:
52186b4cac81SBjoern A. Zeeb 	return (skb);
52196b4cac81SBjoern A. Zeeb }
52206b4cac81SBjoern A. Zeeb 
52216b4cac81SBjoern A. Zeeb void
52226b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *txq,
522386220d3cSBjoern A. Zeeb     unsigned long *frame_cnt, unsigned long *byte_cnt)
52246b4cac81SBjoern A. Zeeb {
52256b4cac81SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
52266b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
522786220d3cSBjoern A. Zeeb 	unsigned long fc, bc;
52286b4cac81SBjoern A. Zeeb 
52296b4cac81SBjoern A. Zeeb 	ltxq = TXQ_TO_LTXQ(txq);
52306b4cac81SBjoern A. Zeeb 
52316b4cac81SBjoern A. Zeeb 	fc = bc = 0;
5232eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_LOCK(ltxq);
52336b4cac81SBjoern A. Zeeb 	skb_queue_walk(&ltxq->skbq, skb) {
52346b4cac81SBjoern A. Zeeb 		fc++;
52356b4cac81SBjoern A. Zeeb 		bc += skb->len;
52366b4cac81SBjoern A. Zeeb 	}
5237eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_UNLOCK(ltxq);
52386b4cac81SBjoern A. Zeeb 	if (frame_cnt)
52396b4cac81SBjoern A. Zeeb 		*frame_cnt = fc;
52406b4cac81SBjoern A. Zeeb 	if (byte_cnt)
52416b4cac81SBjoern A. Zeeb 		*byte_cnt = bc;
52426b4cac81SBjoern A. Zeeb 
52436b4cac81SBjoern A. Zeeb 	/* Validate that this is doing the correct thing. */
52446b4cac81SBjoern A. Zeeb 	/* Should we keep track on en/dequeue? */
52456b4cac81SBjoern A. Zeeb 	IMPROVE();
52466b4cac81SBjoern A. Zeeb }
52476b4cac81SBjoern A. Zeeb 
52486b4cac81SBjoern A. Zeeb /*
52496b4cac81SBjoern A. Zeeb  * We are called from ieee80211_free_txskb() or ieee80211_tx_status().
52506b4cac81SBjoern A. Zeeb  * The latter tries to derive the success status from the info flags
52516b4cac81SBjoern A. Zeeb  * passed back from the driver.  rawx_mit() saves the ni on the m and the
52526b4cac81SBjoern A. Zeeb  * m on the skb for us to be able to give feedback to net80211.
52536b4cac81SBjoern A. Zeeb  */
5254a8397571SBjoern A. Zeeb static void
5255a8397571SBjoern A. Zeeb _lkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
52566b4cac81SBjoern A. Zeeb     int status)
52576b4cac81SBjoern A. Zeeb {
52586b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
52596b4cac81SBjoern A. Zeeb 	struct mbuf *m;
52606b4cac81SBjoern A. Zeeb 
52616b4cac81SBjoern A. Zeeb 	m = skb->m;
52626b4cac81SBjoern A. Zeeb 	skb->m = NULL;
52636b4cac81SBjoern A. Zeeb 
52646b4cac81SBjoern A. Zeeb 	if (m != NULL) {
52656b4cac81SBjoern A. Zeeb 		ni = m->m_pkthdr.PH_loc.ptr;
52666b4cac81SBjoern A. Zeeb 		/* Status: 0 is ok, != 0 is error. */
52676b4cac81SBjoern A. Zeeb 		ieee80211_tx_complete(ni, m, status);
52686b4cac81SBjoern A. Zeeb 		/* ni & mbuf were consumed. */
52696b4cac81SBjoern A. Zeeb 	}
5270a8397571SBjoern A. Zeeb }
52716b4cac81SBjoern A. Zeeb 
5272a8397571SBjoern A. Zeeb void
5273a8397571SBjoern A. Zeeb linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
5274a8397571SBjoern A. Zeeb     int status)
5275a8397571SBjoern A. Zeeb {
5276a8397571SBjoern A. Zeeb 
5277a8397571SBjoern A. Zeeb 	_lkpi_ieee80211_free_txskb(hw, skb, status);
52786b4cac81SBjoern A. Zeeb 	kfree_skb(skb);
52796b4cac81SBjoern A. Zeeb }
52806b4cac81SBjoern A. Zeeb 
5281383b3e8fSBjoern A. Zeeb void
5282a8397571SBjoern A. Zeeb linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
5283a8397571SBjoern A. Zeeb     struct ieee80211_tx_status *txstat)
5284383b3e8fSBjoern A. Zeeb {
5285a8397571SBjoern A. Zeeb 	struct sk_buff *skb;
5286383b3e8fSBjoern A. Zeeb 	struct ieee80211_tx_info *info;
5287383b3e8fSBjoern A. Zeeb 	struct ieee80211_ratectl_tx_status txs;
5288383b3e8fSBjoern A. Zeeb 	struct ieee80211_node *ni;
5289383b3e8fSBjoern A. Zeeb 	int status;
5290383b3e8fSBjoern A. Zeeb 
5291a8397571SBjoern A. Zeeb 	skb = txstat->skb;
5292383b3e8fSBjoern A. Zeeb 	if (skb->m != NULL) {
5293383b3e8fSBjoern A. Zeeb 		struct mbuf *m;
5294383b3e8fSBjoern A. Zeeb 
5295383b3e8fSBjoern A. Zeeb 		m = skb->m;
5296383b3e8fSBjoern A. Zeeb 		ni = m->m_pkthdr.PH_loc.ptr;
5297383b3e8fSBjoern A. Zeeb 		memset(&txs, 0, sizeof(txs));
5298383b3e8fSBjoern A. Zeeb 	} else {
5299383b3e8fSBjoern A. Zeeb 		ni = NULL;
5300383b3e8fSBjoern A. Zeeb 	}
5301383b3e8fSBjoern A. Zeeb 
5302a8397571SBjoern A. Zeeb 	info = txstat->info;
5303383b3e8fSBjoern A. Zeeb 	if (info->flags & IEEE80211_TX_STAT_ACK) {
5304383b3e8fSBjoern A. Zeeb 		status = 0;	/* No error. */
5305383b3e8fSBjoern A. Zeeb 		txs.status = IEEE80211_RATECTL_TX_SUCCESS;
5306383b3e8fSBjoern A. Zeeb 	} else {
5307383b3e8fSBjoern A. Zeeb 		status = 1;
5308383b3e8fSBjoern A. Zeeb 		txs.status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED;
5309383b3e8fSBjoern A. Zeeb 	}
5310383b3e8fSBjoern A. Zeeb 
5311383b3e8fSBjoern A. Zeeb 	if (ni != NULL) {
5312e2c5ab09SJohn Baldwin 		int ridx __unused;
5313383b3e8fSBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
5314383b3e8fSBjoern A. Zeeb 		int old_rate;
5315383b3e8fSBjoern A. Zeeb 
5316383b3e8fSBjoern A. Zeeb 		old_rate = ni->ni_vap->iv_bss->ni_txrate;
5317383b3e8fSBjoern A. Zeeb #endif
5318383b3e8fSBjoern A. Zeeb 		txs.pktlen = skb->len;
5319383b3e8fSBjoern A. Zeeb 		txs.flags |= IEEE80211_RATECTL_STATUS_PKTLEN;
5320383b3e8fSBjoern A. Zeeb 		if (info->status.rates[0].count > 1) {
5321383b3e8fSBjoern A. Zeeb 			txs.long_retries = info->status.rates[0].count - 1;	/* 1 + retries in drivers. */
5322383b3e8fSBjoern A. Zeeb 			txs.flags |= IEEE80211_RATECTL_STATUS_LONG_RETRY;
5323383b3e8fSBjoern A. Zeeb 		}
5324383b3e8fSBjoern A. Zeeb #if 0		/* Unused in net80211 currently. */
5325a8397571SBjoern A. Zeeb 		/* XXX-BZ convert check .flags for MCS/VHT/.. */
5326383b3e8fSBjoern A. Zeeb 		txs.final_rate = info->status.rates[0].idx;
5327383b3e8fSBjoern A. Zeeb 		txs.flags |= IEEE80211_RATECTL_STATUS_FINAL_RATE;
5328383b3e8fSBjoern A. Zeeb #endif
5329adff403fSBjoern A. Zeeb 		if (info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID) {
5330383b3e8fSBjoern A. Zeeb 			txs.rssi = info->status.ack_signal;		/* XXX-BZ CONVERT? */
5331383b3e8fSBjoern A. Zeeb 			txs.flags |= IEEE80211_RATECTL_STATUS_RSSI;
5332383b3e8fSBjoern A. Zeeb 		}
5333383b3e8fSBjoern A. Zeeb 
5334383b3e8fSBjoern A. Zeeb 		IMPROVE("only update of rate matches but that requires us to get a proper rate");
5335383b3e8fSBjoern A. Zeeb 		ieee80211_ratectl_tx_complete(ni, &txs);
5336383b3e8fSBjoern A. Zeeb 		ridx = ieee80211_ratectl_rate(ni->ni_vap->iv_bss, NULL, 0);
5337383b3e8fSBjoern A. Zeeb 
5338383b3e8fSBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
5339383b3e8fSBjoern A. Zeeb 		if (linuxkpi_debug_80211 & D80211_TRACE_TX) {
5340383b3e8fSBjoern A. Zeeb 			printf("TX-RATE: %s: old %d new %d ridx %d, "
5341383b3e8fSBjoern A. Zeeb 			    "long_retries %d\n", __func__,
5342383b3e8fSBjoern A. Zeeb 			    old_rate, ni->ni_vap->iv_bss->ni_txrate,
5343383b3e8fSBjoern A. Zeeb 			    ridx, txs.long_retries);
5344383b3e8fSBjoern A. Zeeb 		}
5345383b3e8fSBjoern A. Zeeb #endif
5346383b3e8fSBjoern A. Zeeb 	}
5347383b3e8fSBjoern A. Zeeb 
5348383b3e8fSBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
5349383b3e8fSBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
5350383b3e8fSBjoern A. Zeeb 		printf("TX-STATUS: %s: hw %p skb %p status %d : flags %#x "
5351383b3e8fSBjoern A. Zeeb 		    "band %u hw_queue %u tx_time_est %d : "
5352383b3e8fSBjoern A. Zeeb 		    "rates [ %u %u %#x, %u %u %#x, %u %u %#x, %u %u %#x ] "
5353383b3e8fSBjoern A. Zeeb 		    "ack_signal %u ampdu_ack_len %u ampdu_len %u antenna %u "
5354adff403fSBjoern A. Zeeb 		    "tx_time %u flags %#x "
5355383b3e8fSBjoern A. Zeeb 		    "status_driver_data [ %p %p ]\n",
5356383b3e8fSBjoern A. Zeeb 		    __func__, hw, skb, status, info->flags,
5357383b3e8fSBjoern A. Zeeb 		    info->band, info->hw_queue, info->tx_time_est,
5358383b3e8fSBjoern A. Zeeb 		    info->status.rates[0].idx, info->status.rates[0].count,
5359383b3e8fSBjoern A. Zeeb 		    info->status.rates[0].flags,
5360383b3e8fSBjoern A. Zeeb 		    info->status.rates[1].idx, info->status.rates[1].count,
5361383b3e8fSBjoern A. Zeeb 		    info->status.rates[1].flags,
5362383b3e8fSBjoern A. Zeeb 		    info->status.rates[2].idx, info->status.rates[2].count,
5363383b3e8fSBjoern A. Zeeb 		    info->status.rates[2].flags,
5364383b3e8fSBjoern A. Zeeb 		    info->status.rates[3].idx, info->status.rates[3].count,
5365383b3e8fSBjoern A. Zeeb 		    info->status.rates[3].flags,
5366383b3e8fSBjoern A. Zeeb 		    info->status.ack_signal, info->status.ampdu_ack_len,
5367383b3e8fSBjoern A. Zeeb 		    info->status.ampdu_len, info->status.antenna,
5368adff403fSBjoern A. Zeeb 		    info->status.tx_time, info->status.flags,
5369383b3e8fSBjoern A. Zeeb 		    info->status.status_driver_data[0],
5370383b3e8fSBjoern A. Zeeb 		    info->status.status_driver_data[1]);
5371383b3e8fSBjoern A. Zeeb #endif
5372383b3e8fSBjoern A. Zeeb 
5373a8397571SBjoern A. Zeeb 	if (txstat->free_list) {
5374a8397571SBjoern A. Zeeb 		_lkpi_ieee80211_free_txskb(hw, skb, status);
5375a8397571SBjoern A. Zeeb 		list_add_tail(&skb->list, txstat->free_list);
5376a8397571SBjoern A. Zeeb 	} else {
5377383b3e8fSBjoern A. Zeeb 		linuxkpi_ieee80211_free_txskb(hw, skb, status);
5378383b3e8fSBjoern A. Zeeb 	}
5379a8397571SBjoern A. Zeeb }
5380a8397571SBjoern A. Zeeb 
5381a8397571SBjoern A. Zeeb void
5382a8397571SBjoern A. Zeeb linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
5383a8397571SBjoern A. Zeeb {
5384a8397571SBjoern A. Zeeb 	struct ieee80211_tx_status status;
5385a8397571SBjoern A. Zeeb 
5386a8397571SBjoern A. Zeeb 	memset(&status, 0, sizeof(status));
5387a8397571SBjoern A. Zeeb 	status.info = IEEE80211_SKB_CB(skb);
5388a8397571SBjoern A. Zeeb 	status.skb = skb;
5389a8397571SBjoern A. Zeeb 	/* sta, n_rates, rates, free_list? */
5390a8397571SBjoern A. Zeeb 
5391a8397571SBjoern A. Zeeb 	ieee80211_tx_status_ext(hw, &status);
5392a8397571SBjoern A. Zeeb }
5393383b3e8fSBjoern A. Zeeb 
53946b4cac81SBjoern A. Zeeb /*
53956b4cac81SBjoern A. Zeeb  * This is an internal bandaid for the moment for the way we glue
53966b4cac81SBjoern A. Zeeb  * skbs and mbufs together for TX.  Once we have skbs backed by
53976b4cac81SBjoern A. Zeeb  * mbufs this should go away.
53986b4cac81SBjoern A. Zeeb  * This is a public function but kept on the private KPI (lkpi_)
53996b4cac81SBjoern A. Zeeb  * and is not exposed by a header file.
54006b4cac81SBjoern A. Zeeb  */
54016b4cac81SBjoern A. Zeeb static void
54026b4cac81SBjoern A. Zeeb lkpi_ieee80211_free_skb_mbuf(void *p)
54036b4cac81SBjoern A. Zeeb {
54046b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
54056b4cac81SBjoern A. Zeeb 	struct mbuf *m;
54066b4cac81SBjoern A. Zeeb 
54076b4cac81SBjoern A. Zeeb 	if (p == NULL)
54086b4cac81SBjoern A. Zeeb 		return;
54096b4cac81SBjoern A. Zeeb 
54106b4cac81SBjoern A. Zeeb 	m = (struct mbuf *)p;
54116b4cac81SBjoern A. Zeeb 	M_ASSERTPKTHDR(m);
54126b4cac81SBjoern A. Zeeb 
54136b4cac81SBjoern A. Zeeb 	ni = m->m_pkthdr.PH_loc.ptr;
54146b4cac81SBjoern A. Zeeb 	m->m_pkthdr.PH_loc.ptr = NULL;
54156b4cac81SBjoern A. Zeeb 	if (ni != NULL)
54166b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
54176b4cac81SBjoern A. Zeeb 	m_freem(m);
54186b4cac81SBjoern A. Zeeb }
54196b4cac81SBjoern A. Zeeb 
54206b4cac81SBjoern A. Zeeb void
54216b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
54226b4cac81SBjoern A. Zeeb     struct delayed_work *w, int delay)
54236b4cac81SBjoern A. Zeeb {
54246b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
54256b4cac81SBjoern A. Zeeb 
54266b4cac81SBjoern A. Zeeb 	/* Need to make sure hw is in a stable (non-suspended) state. */
54276b4cac81SBjoern A. Zeeb 	IMPROVE();
54286b4cac81SBjoern A. Zeeb 
54296b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
54306b4cac81SBjoern A. Zeeb 	queue_delayed_work(lhw->workq, w, delay);
54316b4cac81SBjoern A. Zeeb }
54326b4cac81SBjoern A. Zeeb 
54336b4cac81SBjoern A. Zeeb void
54346b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_queue_work(struct ieee80211_hw *hw,
54356b4cac81SBjoern A. Zeeb     struct work_struct *w)
54366b4cac81SBjoern A. Zeeb {
54376b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
54386b4cac81SBjoern A. Zeeb 
54396b4cac81SBjoern A. Zeeb 	/* Need to make sure hw is in a stable (non-suspended) state. */
54406b4cac81SBjoern A. Zeeb 	IMPROVE();
54416b4cac81SBjoern A. Zeeb 
54426b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
54436b4cac81SBjoern A. Zeeb 	queue_work(lhw->workq, w);
54446b4cac81SBjoern A. Zeeb }
54456b4cac81SBjoern A. Zeeb 
54466b4cac81SBjoern A. Zeeb struct sk_buff *
5447ade774b1SBjoern A. Zeeb linuxkpi_ieee80211_probereq_get(struct ieee80211_hw *hw, uint8_t *addr,
5448ade774b1SBjoern A. Zeeb     uint8_t *ssid, size_t ssid_len, size_t tailroom)
5449ade774b1SBjoern A. Zeeb {
5450ade774b1SBjoern A. Zeeb 	struct sk_buff *skb;
5451ade774b1SBjoern A. Zeeb 	struct ieee80211_frame *wh;
5452ade774b1SBjoern A. Zeeb 	uint8_t *p;
5453ade774b1SBjoern A. Zeeb 	size_t len;
5454ade774b1SBjoern A. Zeeb 
5455ade774b1SBjoern A. Zeeb 	len = sizeof(*wh);
5456ade774b1SBjoern A. Zeeb 	len += 2 + ssid_len;
5457ade774b1SBjoern A. Zeeb 
5458ade774b1SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + len + tailroom);
5459ade774b1SBjoern A. Zeeb 	if (skb == NULL)
5460ade774b1SBjoern A. Zeeb 		return (NULL);
5461ade774b1SBjoern A. Zeeb 
5462ade774b1SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
5463ade774b1SBjoern A. Zeeb 
5464ade774b1SBjoern A. Zeeb 	wh = skb_put_zero(skb, sizeof(*wh));
5465ade774b1SBjoern A. Zeeb 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0;
5466ade774b1SBjoern A. Zeeb 	wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT;
5467ade774b1SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
5468ade774b1SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(wh->i_addr2, addr);
5469ade774b1SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr);
5470ade774b1SBjoern A. Zeeb 
5471ade774b1SBjoern A. Zeeb 	p = skb_put(skb, 2 + ssid_len);
5472ade774b1SBjoern A. Zeeb 	*p++ = IEEE80211_ELEMID_SSID;
5473ade774b1SBjoern A. Zeeb 	*p++ = ssid_len;
5474ade774b1SBjoern A. Zeeb 	if (ssid_len > 0)
5475ade774b1SBjoern A. Zeeb 		memcpy(p, ssid, ssid_len);
5476ade774b1SBjoern A. Zeeb 
5477ade774b1SBjoern A. Zeeb 	return (skb);
5478ade774b1SBjoern A. Zeeb }
5479ade774b1SBjoern A. Zeeb 
5480ade774b1SBjoern A. Zeeb struct sk_buff *
54816b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_pspoll_get(struct ieee80211_hw *hw,
54826b4cac81SBjoern A. Zeeb     struct ieee80211_vif *vif)
54836b4cac81SBjoern A. Zeeb {
54846b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
54856b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
54866b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
54876b4cac81SBjoern A. Zeeb 	struct ieee80211_frame_pspoll *psp;
54886b4cac81SBjoern A. Zeeb 	uint16_t v;
54896b4cac81SBjoern A. Zeeb 
54906b4cac81SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*psp));
54916b4cac81SBjoern A. Zeeb 	if (skb == NULL)
54926b4cac81SBjoern A. Zeeb 		return (NULL);
54936b4cac81SBjoern A. Zeeb 
54946b4cac81SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
54956b4cac81SBjoern A. Zeeb 
54966b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
54976b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
54986b4cac81SBjoern A. Zeeb 
54996b4cac81SBjoern A. Zeeb 	psp = skb_put_zero(skb, sizeof(*psp));
55006b4cac81SBjoern A. Zeeb 	psp->i_fc[0] = IEEE80211_FC0_VERSION_0;
55016b4cac81SBjoern A. Zeeb 	psp->i_fc[0] |= IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL;
5502616e1330SBjoern A. Zeeb 	v = htole16(vif->cfg.aid | 1<<15 | 1<<16);
55036b4cac81SBjoern A. Zeeb 	memcpy(&psp->i_aid, &v, sizeof(v));
55046b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(psp->i_bssid, vap->iv_bss->ni_macaddr);
55056b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(psp->i_ta, vif->addr);
55066b4cac81SBjoern A. Zeeb 
55076b4cac81SBjoern A. Zeeb 	return (skb);
55086b4cac81SBjoern A. Zeeb }
55096b4cac81SBjoern A. Zeeb 
55106b4cac81SBjoern A. Zeeb struct sk_buff *
55116b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw,
5512adff403fSBjoern A. Zeeb     struct ieee80211_vif *vif, int linkid, bool qos)
55136b4cac81SBjoern A. Zeeb {
55146b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55156b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
55166b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
55176b4cac81SBjoern A. Zeeb 	struct ieee80211_frame *nullf;
55186b4cac81SBjoern A. Zeeb 
5519adff403fSBjoern A. Zeeb 	IMPROVE("linkid");
5520adff403fSBjoern A. Zeeb 
55216b4cac81SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*nullf));
55226b4cac81SBjoern A. Zeeb 	if (skb == NULL)
55236b4cac81SBjoern A. Zeeb 		return (NULL);
55246b4cac81SBjoern A. Zeeb 
55256b4cac81SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
55266b4cac81SBjoern A. Zeeb 
55276b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
55286b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
55296b4cac81SBjoern A. Zeeb 
55306b4cac81SBjoern A. Zeeb 	nullf = skb_put_zero(skb, sizeof(*nullf));
55316b4cac81SBjoern A. Zeeb 	nullf->i_fc[0] = IEEE80211_FC0_VERSION_0;
55326b4cac81SBjoern A. Zeeb 	nullf->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA;
55336b4cac81SBjoern A. Zeeb 	nullf->i_fc[1] = IEEE80211_FC1_DIR_TODS;
55346b4cac81SBjoern A. Zeeb 
55356b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(nullf->i_addr1, vap->iv_bss->ni_bssid);
55366b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(nullf->i_addr2, vif->addr);
55376b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(nullf->i_addr3, vap->iv_bss->ni_macaddr);
55386b4cac81SBjoern A. Zeeb 
55396b4cac81SBjoern A. Zeeb 	return (skb);
55406b4cac81SBjoern A. Zeeb }
55416b4cac81SBjoern A. Zeeb 
55426b4cac81SBjoern A. Zeeb struct wireless_dev *
55436b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
55446b4cac81SBjoern A. Zeeb {
55456b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55466b4cac81SBjoern A. Zeeb 
55476b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
55486b4cac81SBjoern A. Zeeb 	return (&lvif->wdev);
55496b4cac81SBjoern A. Zeeb }
55506b4cac81SBjoern A. Zeeb 
55516b4cac81SBjoern A. Zeeb void
55526b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *vif)
55536b4cac81SBjoern A. Zeeb {
55546b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55556b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
55566b4cac81SBjoern A. Zeeb 	enum ieee80211_state nstate;
55576b4cac81SBjoern A. Zeeb 	int arg;
55586b4cac81SBjoern A. Zeeb 
55596b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
55606b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
55616b4cac81SBjoern A. Zeeb 
55626b4cac81SBjoern A. Zeeb 	/*
5563f3229b62SBjoern A. Zeeb 	 * Go to init; otherwise we need to elaborately check state and
55646b4cac81SBjoern A. Zeeb 	 * handle accordingly, e.g., if in RUN we could call iv_bmiss.
55656b4cac81SBjoern A. Zeeb 	 * Let the statemachine handle all neccessary changes.
55666b4cac81SBjoern A. Zeeb 	 */
5567f3229b62SBjoern A. Zeeb 	nstate = IEEE80211_S_INIT;
5568bb81db90SBjoern A. Zeeb 	arg = 0;	/* Not a valid reason. */
55696b4cac81SBjoern A. Zeeb 
5570018d93ecSBjoern A. Zeeb 	ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
5571018d93ecSBjoern A. Zeeb 	    vif, vap, ieee80211_state_name[vap->iv_state]);
55726b4cac81SBjoern A. Zeeb 	ieee80211_new_state(vap, nstate, arg);
55736b4cac81SBjoern A. Zeeb }
55746b4cac81SBjoern A. Zeeb 
5575bb81db90SBjoern A. Zeeb void
5576bb81db90SBjoern A. Zeeb linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *vif)
5577bb81db90SBjoern A. Zeeb {
5578bb81db90SBjoern A. Zeeb 	struct lkpi_vif *lvif;
5579bb81db90SBjoern A. Zeeb 	struct ieee80211vap *vap;
5580bb81db90SBjoern A. Zeeb 
5581bb81db90SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
5582bb81db90SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
5583bb81db90SBjoern A. Zeeb 
5584bb81db90SBjoern A. Zeeb 	ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
5585bb81db90SBjoern A. Zeeb 	    vif, vap, ieee80211_state_name[vap->iv_state]);
55863540911bSBjoern A. Zeeb 	ieee80211_beacon_miss(vap->iv_ic);
5587bb81db90SBjoern A. Zeeb }
5588bb81db90SBjoern A. Zeeb 
55895edde07cSBjoern A. Zeeb /* -------------------------------------------------------------------------- */
55905edde07cSBjoern A. Zeeb 
55915a9a0d78SBjoern A. Zeeb void
55925a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_stop_queue(struct ieee80211_hw *hw, int qnum)
55935a9a0d78SBjoern A. Zeeb {
55945a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
55955a9a0d78SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55965a9a0d78SBjoern A. Zeeb 	struct ieee80211_vif *vif;
55975a9a0d78SBjoern A. Zeeb 	int ac_count, ac;
55985a9a0d78SBjoern A. Zeeb 
55995a9a0d78SBjoern A. Zeeb 	KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
56005a9a0d78SBjoern A. Zeeb 	    __func__, qnum, hw->queues, hw));
56015a9a0d78SBjoern A. Zeeb 
56025a9a0d78SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
56035a9a0d78SBjoern A. Zeeb 
56045a9a0d78SBjoern A. Zeeb 	/* See lkpi_ic_vap_create(). */
56055a9a0d78SBjoern A. Zeeb 	if (hw->queues >= IEEE80211_NUM_ACS)
56065a9a0d78SBjoern A. Zeeb 		ac_count = IEEE80211_NUM_ACS;
56075a9a0d78SBjoern A. Zeeb 	else
56085a9a0d78SBjoern A. Zeeb 		ac_count = 1;
56095a9a0d78SBjoern A. Zeeb 
56105a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
56115a9a0d78SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
56125a9a0d78SBjoern A. Zeeb 
56135a9a0d78SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
56145a9a0d78SBjoern A. Zeeb 		for (ac = 0; ac < ac_count; ac++) {
56155a9a0d78SBjoern A. Zeeb 			IMPROVE_TXQ("LOCKING");
56165a9a0d78SBjoern A. Zeeb 			if (qnum == vif->hw_queue[ac]) {
56170d2cb6a6SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
56185a9a0d78SBjoern A. Zeeb 				/*
56195a9a0d78SBjoern A. Zeeb 				 * For now log this to better understand
56205a9a0d78SBjoern A. Zeeb 				 * how this is supposed to work.
56215a9a0d78SBjoern A. Zeeb 				 */
56220d2cb6a6SBjoern A. Zeeb 				if (lvif->hw_queue_stopped[ac] &&
56230d2cb6a6SBjoern A. Zeeb 				    (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) != 0)
56245a9a0d78SBjoern A. Zeeb 					ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
56255a9a0d78SBjoern A. Zeeb 					    "lvif %p vif %p ac %d qnum %d already "
56265a9a0d78SBjoern A. Zeeb 					    "stopped\n", __func__, __LINE__,
56275a9a0d78SBjoern A. Zeeb 					    lhw, hw, lvif, vif, ac, qnum);
56280d2cb6a6SBjoern A. Zeeb #endif
56295a9a0d78SBjoern A. Zeeb 				lvif->hw_queue_stopped[ac] = true;
56305a9a0d78SBjoern A. Zeeb 			}
56315a9a0d78SBjoern A. Zeeb 		}
56325a9a0d78SBjoern A. Zeeb 	}
56335a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
56345a9a0d78SBjoern A. Zeeb }
56355a9a0d78SBjoern A. Zeeb 
56365a9a0d78SBjoern A. Zeeb void
56375a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_stop_queues(struct ieee80211_hw *hw)
56385a9a0d78SBjoern A. Zeeb {
56395a9a0d78SBjoern A. Zeeb 	int i;
56405a9a0d78SBjoern A. Zeeb 
56415a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Locking; do we need further info?");
56425a9a0d78SBjoern A. Zeeb 	for (i = 0; i < hw->queues; i++)
56435a9a0d78SBjoern A. Zeeb 		linuxkpi_ieee80211_stop_queue(hw, i);
56445a9a0d78SBjoern A. Zeeb }
56455a9a0d78SBjoern A. Zeeb 
56465a9a0d78SBjoern A. Zeeb 
56475a9a0d78SBjoern A. Zeeb static void
56485a9a0d78SBjoern A. Zeeb lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
56495a9a0d78SBjoern A. Zeeb {
56505a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
56515a9a0d78SBjoern A. Zeeb 	struct lkpi_vif *lvif;
56525a9a0d78SBjoern A. Zeeb 	struct lkpi_sta *lsta;
56535a9a0d78SBjoern A. Zeeb 	int ac_count, ac, tid;
56545a9a0d78SBjoern A. Zeeb 
56555a9a0d78SBjoern A. Zeeb 	/* See lkpi_ic_vap_create(). */
56565a9a0d78SBjoern A. Zeeb 	if (hw->queues >= IEEE80211_NUM_ACS)
56575a9a0d78SBjoern A. Zeeb 		ac_count = IEEE80211_NUM_ACS;
56585a9a0d78SBjoern A. Zeeb 	else
56595a9a0d78SBjoern A. Zeeb 		ac_count = 1;
56605a9a0d78SBjoern A. Zeeb 
56615a9a0d78SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
56625a9a0d78SBjoern A. Zeeb 
56635a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Locking");
56645a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
56655a9a0d78SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
56665a9a0d78SBjoern A. Zeeb 		struct ieee80211_vif *vif;
56675a9a0d78SBjoern A. Zeeb 
56685a9a0d78SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
56695a9a0d78SBjoern A. Zeeb 		for (ac = 0; ac < ac_count; ac++) {
56705a9a0d78SBjoern A. Zeeb 
56715a9a0d78SBjoern A. Zeeb 			if (hwq == vif->hw_queue[ac]) {
56725a9a0d78SBjoern A. Zeeb 
56735a9a0d78SBjoern A. Zeeb 				/* XXX-BZ what about software scan? */
56745a9a0d78SBjoern A. Zeeb 
56750d2cb6a6SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
56765a9a0d78SBjoern A. Zeeb 				/*
56775a9a0d78SBjoern A. Zeeb 				 * For now log this to better understand
56785a9a0d78SBjoern A. Zeeb 				 * how this is supposed to work.
56795a9a0d78SBjoern A. Zeeb 				 */
56800d2cb6a6SBjoern A. Zeeb 				if (!lvif->hw_queue_stopped[ac] &&
56810d2cb6a6SBjoern A. Zeeb 				    (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) != 0)
56825a9a0d78SBjoern A. Zeeb 					ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
56835a9a0d78SBjoern A. Zeeb 					    "lvif %p vif %p ac %d hw_q not stopped\n",
56845a9a0d78SBjoern A. Zeeb 					    __func__, __LINE__,
56855a9a0d78SBjoern A. Zeeb 					    lhw, hw, lvif, vif, ac);
56860d2cb6a6SBjoern A. Zeeb #endif
56875a9a0d78SBjoern A. Zeeb 				lvif->hw_queue_stopped[ac] = false;
56885a9a0d78SBjoern A. Zeeb 
56895a9a0d78SBjoern A. Zeeb 				LKPI_80211_LVIF_LOCK(lvif);
56905a9a0d78SBjoern A. Zeeb 				TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
56915a9a0d78SBjoern A. Zeeb 					struct ieee80211_sta *sta;
56925a9a0d78SBjoern A. Zeeb 
56935a9a0d78SBjoern A. Zeeb 					sta = LSTA_TO_STA(lsta);
56945a9a0d78SBjoern A. Zeeb 					for (tid = 0; tid < nitems(sta->txq); tid++) {
56955a9a0d78SBjoern A. Zeeb 						struct lkpi_txq *ltxq;
56965a9a0d78SBjoern A. Zeeb 
56975a9a0d78SBjoern A. Zeeb 						if (sta->txq[tid] == NULL)
56985a9a0d78SBjoern A. Zeeb 							continue;
56995a9a0d78SBjoern A. Zeeb 
57005a9a0d78SBjoern A. Zeeb 						if (sta->txq[tid]->ac != ac)
57015a9a0d78SBjoern A. Zeeb 							continue;
57025a9a0d78SBjoern A. Zeeb 
57035a9a0d78SBjoern A. Zeeb 						ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
57045a9a0d78SBjoern A. Zeeb 						if (!ltxq->stopped)
57055a9a0d78SBjoern A. Zeeb 							continue;
57065a9a0d78SBjoern A. Zeeb 
57075a9a0d78SBjoern A. Zeeb 						ltxq->stopped = false;
57085a9a0d78SBjoern A. Zeeb 
57095a9a0d78SBjoern A. Zeeb 						/* XXX-BZ see when this explodes with all the locking. taskq? */
57105a9a0d78SBjoern A. Zeeb 						lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
57115a9a0d78SBjoern A. Zeeb 					}
57125a9a0d78SBjoern A. Zeeb 				}
57135a9a0d78SBjoern A. Zeeb 				LKPI_80211_LVIF_UNLOCK(lvif);
57145a9a0d78SBjoern A. Zeeb 			}
57155a9a0d78SBjoern A. Zeeb 		}
57165a9a0d78SBjoern A. Zeeb 	}
57175a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
57185a9a0d78SBjoern A. Zeeb }
57195a9a0d78SBjoern A. Zeeb 
57205a9a0d78SBjoern A. Zeeb void
57215a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
57225a9a0d78SBjoern A. Zeeb {
57235a9a0d78SBjoern A. Zeeb 	int i;
57245a9a0d78SBjoern A. Zeeb 
57255a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Is this all/enough here?");
57265a9a0d78SBjoern A. Zeeb 	for (i = 0; i < hw->queues; i++)
57275a9a0d78SBjoern A. Zeeb 		lkpi_ieee80211_wake_queues(hw, i);
57285a9a0d78SBjoern A. Zeeb }
57295a9a0d78SBjoern A. Zeeb 
57305a9a0d78SBjoern A. Zeeb void
57315a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
57325a9a0d78SBjoern A. Zeeb {
57335a9a0d78SBjoern A. Zeeb 
57345a9a0d78SBjoern A. Zeeb 	KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
57355a9a0d78SBjoern A. Zeeb 	    __func__, qnum, hw->queues, hw));
57365a9a0d78SBjoern A. Zeeb 
57375a9a0d78SBjoern A. Zeeb 	lkpi_ieee80211_wake_queues(hw, qnum);
57385a9a0d78SBjoern A. Zeeb }
57395a9a0d78SBjoern A. Zeeb 
57405a9a0d78SBjoern A. Zeeb /* This is just hardware queues. */
57415a9a0d78SBjoern A. Zeeb void
57425a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
57435a9a0d78SBjoern A. Zeeb {
57445a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
57455a9a0d78SBjoern A. Zeeb 
57465a9a0d78SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
57475a9a0d78SBjoern A. Zeeb 
57485a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Are there reasons why we wouldn't schedule?");
57495a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("LOCKING");
57505a9a0d78SBjoern A. Zeeb 	if (++lhw->txq_generation[ac] == 0)
57515a9a0d78SBjoern A. Zeeb 		lhw->txq_generation[ac]++;
57525a9a0d78SBjoern A. Zeeb }
57535a9a0d78SBjoern A. Zeeb 
57545a9a0d78SBjoern A. Zeeb struct ieee80211_txq *
57555a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
57565a9a0d78SBjoern A. Zeeb {
57575a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
57585a9a0d78SBjoern A. Zeeb 	struct ieee80211_txq *txq;
57595a9a0d78SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
57605a9a0d78SBjoern A. Zeeb 
57615a9a0d78SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
57625a9a0d78SBjoern A. Zeeb 	txq = NULL;
57635a9a0d78SBjoern A. Zeeb 
57645a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("LOCKING");
57655a9a0d78SBjoern A. Zeeb 
57665a9a0d78SBjoern A. Zeeb 	/* Check that we are scheduled. */
57675a9a0d78SBjoern A. Zeeb 	if (lhw->txq_generation[ac] == 0)
57685a9a0d78SBjoern A. Zeeb 		goto out;
57695a9a0d78SBjoern A. Zeeb 
57705a9a0d78SBjoern A. Zeeb 	ltxq = TAILQ_FIRST(&lhw->scheduled_txqs[ac]);
57715a9a0d78SBjoern A. Zeeb 	if (ltxq == NULL)
57725a9a0d78SBjoern A. Zeeb 		goto out;
57735a9a0d78SBjoern A. Zeeb 	if (ltxq->txq_generation == lhw->txq_generation[ac])
57745a9a0d78SBjoern A. Zeeb 		goto out;
57755a9a0d78SBjoern A. Zeeb 
57765a9a0d78SBjoern A. Zeeb 	ltxq->txq_generation = lhw->txq_generation[ac];
57775a9a0d78SBjoern A. Zeeb 	TAILQ_REMOVE(&lhw->scheduled_txqs[ac], ltxq, txq_entry);
57785a9a0d78SBjoern A. Zeeb 	txq = &ltxq->txq;
57795a9a0d78SBjoern A. Zeeb 	TAILQ_ELEM_INIT(ltxq, txq_entry);
57805a9a0d78SBjoern A. Zeeb 
57815a9a0d78SBjoern A. Zeeb out:
57825a9a0d78SBjoern A. Zeeb 	return (txq);
57835a9a0d78SBjoern A. Zeeb }
57845a9a0d78SBjoern A. Zeeb 
57855a9a0d78SBjoern A. Zeeb void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
57865a9a0d78SBjoern A. Zeeb     struct ieee80211_txq *txq, bool withoutpkts)
57875a9a0d78SBjoern A. Zeeb {
57885a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
57895a9a0d78SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
5790eac3646fSBjoern A. Zeeb 	bool ltxq_empty;
57915a9a0d78SBjoern A. Zeeb 
57925a9a0d78SBjoern A. Zeeb 	ltxq = TXQ_TO_LTXQ(txq);
57935a9a0d78SBjoern A. Zeeb 
57945a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("LOCKING");
57955a9a0d78SBjoern A. Zeeb 
57965a9a0d78SBjoern A. Zeeb 	/* Only schedule if work to do or asked to anyway. */
5797eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_LOCK(ltxq);
5798eac3646fSBjoern A. Zeeb 	ltxq_empty = skb_queue_empty(&ltxq->skbq);
5799eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_UNLOCK(ltxq);
5800eac3646fSBjoern A. Zeeb 	if (!withoutpkts && ltxq_empty)
58015a9a0d78SBjoern A. Zeeb 		goto out;
58025a9a0d78SBjoern A. Zeeb 
58035a9a0d78SBjoern A. Zeeb 	/* Make sure we do not double-schedule. */
58045a9a0d78SBjoern A. Zeeb 	if (ltxq->txq_entry.tqe_next != NULL)
58055a9a0d78SBjoern A. Zeeb 		goto out;
58065a9a0d78SBjoern A. Zeeb 
58075a9a0d78SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
58085a9a0d78SBjoern A. Zeeb 	TAILQ_INSERT_TAIL(&lhw->scheduled_txqs[txq->ac], ltxq, txq_entry);
58095a9a0d78SBjoern A. Zeeb out:
58105a9a0d78SBjoern A. Zeeb 	return;
58115a9a0d78SBjoern A. Zeeb }
58125a9a0d78SBjoern A. Zeeb 
5813eac3646fSBjoern A. Zeeb void
5814eac3646fSBjoern A. Zeeb linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
5815eac3646fSBjoern A. Zeeb     struct ieee80211_txq *txq)
5816eac3646fSBjoern A. Zeeb {
5817eac3646fSBjoern A. Zeeb 	struct lkpi_hw *lhw;
5818eac3646fSBjoern A. Zeeb 	struct ieee80211_txq *ntxq;
5819eac3646fSBjoern A. Zeeb 	struct ieee80211_tx_control control;
5820eac3646fSBjoern A. Zeeb         struct sk_buff *skb;
5821eac3646fSBjoern A. Zeeb 
5822eac3646fSBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
5823eac3646fSBjoern A. Zeeb 
5824eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_LOCK(lhw);
5825eac3646fSBjoern A. Zeeb 	ieee80211_txq_schedule_start(hw, txq->ac);
5826eac3646fSBjoern A. Zeeb 	do {
5827eac3646fSBjoern A. Zeeb 		ntxq = ieee80211_next_txq(hw, txq->ac);
5828eac3646fSBjoern A. Zeeb 		if (ntxq == NULL)
5829eac3646fSBjoern A. Zeeb 			break;
5830eac3646fSBjoern A. Zeeb 
5831eac3646fSBjoern A. Zeeb 		memset(&control, 0, sizeof(control));
5832eac3646fSBjoern A. Zeeb 		control.sta = ntxq->sta;
5833eac3646fSBjoern A. Zeeb 		do {
5834eac3646fSBjoern A. Zeeb 			skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
5835eac3646fSBjoern A. Zeeb 			if (skb == NULL)
5836eac3646fSBjoern A. Zeeb 				break;
5837eac3646fSBjoern A. Zeeb 			lkpi_80211_mo_tx(hw, &control, skb);
5838eac3646fSBjoern A. Zeeb 		} while(1);
5839eac3646fSBjoern A. Zeeb 
5840eac3646fSBjoern A. Zeeb 		ieee80211_return_txq(hw, ntxq, false);
5841eac3646fSBjoern A. Zeeb 	} while (1);
5842eac3646fSBjoern A. Zeeb 	ieee80211_txq_schedule_end(hw, txq->ac);
5843eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_UNLOCK(lhw);
5844eac3646fSBjoern A. Zeeb }
5845eac3646fSBjoern A. Zeeb 
58465a9a0d78SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
58475a9a0d78SBjoern A. Zeeb 
58485edde07cSBjoern A. Zeeb struct lkpi_cfg80211_bss {
58495edde07cSBjoern A. Zeeb 	u_int refcnt;
58505edde07cSBjoern A. Zeeb 	struct cfg80211_bss bss;
58515edde07cSBjoern A. Zeeb };
58525edde07cSBjoern A. Zeeb 
58535edde07cSBjoern A. Zeeb struct lkpi_cfg80211_get_bss_iter_lookup {
58545edde07cSBjoern A. Zeeb 	struct wiphy *wiphy;
58555edde07cSBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
58565edde07cSBjoern A. Zeeb 	const uint8_t *bssid;
58575edde07cSBjoern A. Zeeb 	const uint8_t *ssid;
58585edde07cSBjoern A. Zeeb 	size_t ssid_len;
58595edde07cSBjoern A. Zeeb 	enum ieee80211_bss_type bss_type;
58605edde07cSBjoern A. Zeeb 	enum ieee80211_privacy privacy;
58615edde07cSBjoern A. Zeeb 
58625edde07cSBjoern A. Zeeb 	/*
58635edde07cSBjoern A. Zeeb 	 * Something to store a copy of the result as the net80211 scan cache
58645edde07cSBjoern A. Zeeb 	 * is not refoucnted so a scan entry might go away any time.
58655edde07cSBjoern A. Zeeb 	 */
58665edde07cSBjoern A. Zeeb 	bool match;
58675edde07cSBjoern A. Zeeb 	struct cfg80211_bss *bss;
58685edde07cSBjoern A. Zeeb };
58695edde07cSBjoern A. Zeeb 
58705edde07cSBjoern A. Zeeb static void
58715edde07cSBjoern A. Zeeb lkpi_cfg80211_get_bss_iterf(void *arg, const struct ieee80211_scan_entry *se)
58725edde07cSBjoern A. Zeeb {
58735edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_get_bss_iter_lookup *lookup;
58745edde07cSBjoern A. Zeeb 	size_t ielen;
58755edde07cSBjoern A. Zeeb 
58765edde07cSBjoern A. Zeeb 	lookup = arg;
58775edde07cSBjoern A. Zeeb 
58785edde07cSBjoern A. Zeeb 	/* Do not try to find another match. */
58795edde07cSBjoern A. Zeeb 	if (lookup->match)
58805edde07cSBjoern A. Zeeb 		return;
58815edde07cSBjoern A. Zeeb 
58825edde07cSBjoern A. Zeeb 	/* Nothing to store result. */
58835edde07cSBjoern A. Zeeb 	if (lookup->bss == NULL)
58845edde07cSBjoern A. Zeeb 		return;
58855edde07cSBjoern A. Zeeb 
58865edde07cSBjoern A. Zeeb 	if (lookup->privacy != IEEE80211_PRIVACY_ANY) {
58875edde07cSBjoern A. Zeeb 		/* if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) */
58885edde07cSBjoern A. Zeeb 		/* We have no idea what to compare to as the drivers only request ANY */
58895edde07cSBjoern A. Zeeb 		return;
58905edde07cSBjoern A. Zeeb 	}
58915edde07cSBjoern A. Zeeb 
58925edde07cSBjoern A. Zeeb 	if (lookup->bss_type != IEEE80211_BSS_TYPE_ANY) {
58935edde07cSBjoern A. Zeeb 		/* if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS)) */
58945edde07cSBjoern A. Zeeb 		/* We have no idea what to compare to as the drivers only request ANY */
58955edde07cSBjoern A. Zeeb 		return;
58965edde07cSBjoern A. Zeeb 	}
58975edde07cSBjoern A. Zeeb 
58985edde07cSBjoern A. Zeeb 	if (lookup->chan != NULL) {
58995edde07cSBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *chan;
59005edde07cSBjoern A. Zeeb 
59015edde07cSBjoern A. Zeeb 		chan = linuxkpi_ieee80211_get_channel(lookup->wiphy,
59025edde07cSBjoern A. Zeeb 		    se->se_chan->ic_freq);
59035edde07cSBjoern A. Zeeb 		if (chan == NULL || chan != lookup->chan)
59045edde07cSBjoern A. Zeeb 			return;
59055edde07cSBjoern A. Zeeb 	}
59065edde07cSBjoern A. Zeeb 
59075edde07cSBjoern A. Zeeb 	if (lookup->bssid && !IEEE80211_ADDR_EQ(lookup->bssid, se->se_bssid))
59085edde07cSBjoern A. Zeeb 		return;
59095edde07cSBjoern A. Zeeb 
59105edde07cSBjoern A. Zeeb 	if (lookup->ssid) {
59115edde07cSBjoern A. Zeeb 		if (lookup->ssid_len != se->se_ssid[1] ||
59125edde07cSBjoern A. Zeeb 		    se->se_ssid[1] == 0)
59135edde07cSBjoern A. Zeeb 			return;
59145edde07cSBjoern A. Zeeb 		if (memcmp(lookup->ssid, se->se_ssid+2, lookup->ssid_len) != 0)
59155edde07cSBjoern A. Zeeb 			return;
59165edde07cSBjoern A. Zeeb 	}
59175edde07cSBjoern A. Zeeb 
59185edde07cSBjoern A. Zeeb 	ielen = se->se_ies.len;
59195edde07cSBjoern A. Zeeb 
59205edde07cSBjoern A. Zeeb 	lookup->bss->ies = malloc(sizeof(*lookup->bss->ies) + ielen,
59215edde07cSBjoern A. Zeeb 	    M_LKPI80211, M_NOWAIT | M_ZERO);
59225edde07cSBjoern A. Zeeb 	if (lookup->bss->ies == NULL)
59235edde07cSBjoern A. Zeeb 		return;
59245edde07cSBjoern A. Zeeb 
59255edde07cSBjoern A. Zeeb 	lookup->bss->ies->data = (uint8_t *)lookup->bss->ies + sizeof(*lookup->bss->ies);
59265edde07cSBjoern A. Zeeb 	lookup->bss->ies->len = ielen;
59275edde07cSBjoern A. Zeeb 	if (ielen)
59285edde07cSBjoern A. Zeeb 		memcpy(lookup->bss->ies->data, se->se_ies.data, ielen);
59295edde07cSBjoern A. Zeeb 
59305edde07cSBjoern A. Zeeb 	lookup->match = true;
59315edde07cSBjoern A. Zeeb }
59325edde07cSBjoern A. Zeeb 
59335edde07cSBjoern A. Zeeb struct cfg80211_bss *
59345edde07cSBjoern A. Zeeb linuxkpi_cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan,
59355edde07cSBjoern A. Zeeb     const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len,
59365edde07cSBjoern A. Zeeb     enum ieee80211_bss_type bss_type, enum ieee80211_privacy privacy)
59375edde07cSBjoern A. Zeeb {
59385edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_bss *lbss;
59395edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_get_bss_iter_lookup lookup;
59405edde07cSBjoern A. Zeeb 	struct lkpi_hw *lhw;
59415edde07cSBjoern A. Zeeb 	struct ieee80211vap *vap;
59425edde07cSBjoern A. Zeeb 
59435edde07cSBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
59445edde07cSBjoern A. Zeeb 
59455edde07cSBjoern A. Zeeb 	/* Let's hope we can alloc. */
59465edde07cSBjoern A. Zeeb 	lbss = malloc(sizeof(*lbss), M_LKPI80211, M_NOWAIT | M_ZERO);
59475edde07cSBjoern A. Zeeb 	if (lbss == NULL) {
59485edde07cSBjoern A. Zeeb 		ic_printf(lhw->ic, "%s: alloc failed.\n", __func__);
59495edde07cSBjoern A. Zeeb 		return (NULL);
59505edde07cSBjoern A. Zeeb 	}
59515edde07cSBjoern A. Zeeb 
59525edde07cSBjoern A. Zeeb 	lookup.wiphy = wiphy;
59535edde07cSBjoern A. Zeeb 	lookup.chan = chan;
59545edde07cSBjoern A. Zeeb 	lookup.bssid = bssid;
59555edde07cSBjoern A. Zeeb 	lookup.ssid = ssid;
59565edde07cSBjoern A. Zeeb 	lookup.ssid_len = ssid_len;
59575edde07cSBjoern A. Zeeb 	lookup.bss_type = bss_type;
59585edde07cSBjoern A. Zeeb 	lookup.privacy = privacy;
59595edde07cSBjoern A. Zeeb 	lookup.match = false;
59605edde07cSBjoern A. Zeeb 	lookup.bss = &lbss->bss;
59615edde07cSBjoern A. Zeeb 
59625edde07cSBjoern A. Zeeb 	IMPROVE("Iterate over all VAPs comparing perm_addr and addresses?");
59635edde07cSBjoern A. Zeeb 	vap = TAILQ_FIRST(&lhw->ic->ic_vaps);
59645edde07cSBjoern A. Zeeb 	ieee80211_scan_iterate(vap, lkpi_cfg80211_get_bss_iterf, &lookup);
59655edde07cSBjoern A. Zeeb 	if (!lookup.match) {
59665edde07cSBjoern A. Zeeb 		free(lbss, M_LKPI80211);
59675edde07cSBjoern A. Zeeb 		return (NULL);
59685edde07cSBjoern A. Zeeb 	}
59695edde07cSBjoern A. Zeeb 
59705edde07cSBjoern A. Zeeb 	refcount_init(&lbss->refcnt, 1);
59715edde07cSBjoern A. Zeeb 	return (&lbss->bss);
59725edde07cSBjoern A. Zeeb }
59735edde07cSBjoern A. Zeeb 
59745edde07cSBjoern A. Zeeb void
59755edde07cSBjoern A. Zeeb linuxkpi_cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss)
59765edde07cSBjoern A. Zeeb {
59775edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_bss *lbss;
59785edde07cSBjoern A. Zeeb 
59795edde07cSBjoern A. Zeeb 	lbss = container_of(bss, struct lkpi_cfg80211_bss, bss);
59805edde07cSBjoern A. Zeeb 
59815edde07cSBjoern A. Zeeb 	/* Free everything again on refcount ... */
59825edde07cSBjoern A. Zeeb 	if (refcount_release(&lbss->refcnt)) {
59835edde07cSBjoern A. Zeeb 		free(lbss->bss.ies, M_LKPI80211);
59845edde07cSBjoern A. Zeeb 		free(lbss, M_LKPI80211);
59855edde07cSBjoern A. Zeeb 	}
59865edde07cSBjoern A. Zeeb }
59875edde07cSBjoern A. Zeeb 
59885edde07cSBjoern A. Zeeb void
59895edde07cSBjoern A. Zeeb linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
59905edde07cSBjoern A. Zeeb {
59915edde07cSBjoern A. Zeeb 	struct lkpi_hw *lhw;
59925edde07cSBjoern A. Zeeb 	struct ieee80211com *ic;
59935edde07cSBjoern A. Zeeb 	struct ieee80211vap *vap;
59945edde07cSBjoern A. Zeeb 
59955edde07cSBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
59965edde07cSBjoern A. Zeeb 	ic = lhw->ic;
59975edde07cSBjoern A. Zeeb 
59985edde07cSBjoern A. Zeeb 	/*
59995edde07cSBjoern A. Zeeb 	 * If we haven't called ieee80211_ifattach() yet
60005edde07cSBjoern A. Zeeb 	 * or there is no VAP, there are no scans to flush.
60015edde07cSBjoern A. Zeeb 	 */
60025edde07cSBjoern A. Zeeb 	if (ic == NULL ||
60035edde07cSBjoern A. Zeeb 	    (lhw->sc_flags & LKPI_MAC80211_DRV_STARTED) == 0)
60045edde07cSBjoern A. Zeeb 		return;
60055edde07cSBjoern A. Zeeb 
60065edde07cSBjoern A. Zeeb 	/* Should only happen on the current one? Not seen it late enough. */
60075edde07cSBjoern A. Zeeb 	IEEE80211_LOCK(ic);
60085edde07cSBjoern A. Zeeb 	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
60095edde07cSBjoern A. Zeeb 		ieee80211_scan_flush(vap);
60105edde07cSBjoern A. Zeeb 	IEEE80211_UNLOCK(ic);
60115edde07cSBjoern A. Zeeb }
60125edde07cSBjoern A. Zeeb 
60135edde07cSBjoern A. Zeeb /* -------------------------------------------------------------------------- */
60145edde07cSBjoern A. Zeeb 
60156b4cac81SBjoern A. Zeeb MODULE_VERSION(linuxkpi_wlan, 1);
60166b4cac81SBjoern A. Zeeb MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1);
60176b4cac81SBjoern A. Zeeb MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1);
6018