xref: /freebsd/sys/compat/linuxkpi/common/src/linux_80211.c (revision 0936c648ad0ee5152dc19f261e77fe9c1833fe05)
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);
252*0936c648SBjoern A. Zeeb 	KASSERT(lsta->lsta_entry.tqe_prev != NULL,
253*0936c648SBjoern A. Zeeb 	    ("%s: lsta %p lsta_entry.tqe_prev %p ni %p\n", __func__,
254*0936c648SBjoern 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 	/*
279*0936c648SBjoern A. Zeeb 	 * Link the ni to the lsta here without taking a reference.
280*0936c648SBjoern A. Zeeb 	 * For one we would have to take the reference in node_init()
281*0936c648SBjoern A. Zeeb 	 * as ieee80211_alloc_node() will initialise the refcount after us.
282*0936c648SBjoern A. Zeeb 	 * For the other a ni and an lsta are 1:1 mapped and always together
283*0936c648SBjoern A. Zeeb 	 * from [ic_]node_alloc() to [ic_]node_free() so we are essentally
284*0936c648SBjoern A. Zeeb 	 * using the ni references for the lsta as well despite it being
285*0936c648SBjoern A. Zeeb 	 * two separate allocations.
2864f61ef8bSBjoern A. Zeeb 	 */
287*0936c648SBjoern 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. */
3694f61ef8bSBjoern A. Zeeb 	mtx_init(&lsta->txq_mtx, "lsta_txq", NULL, MTX_DEF);
3704f61ef8bSBjoern A. Zeeb 	TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta);
3714f61ef8bSBjoern A. Zeeb 	mbufq_init(&lsta->txq, IFQ_MAXLEN);
372*0936c648SBjoern 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 
388*0936c648SBjoern A. Zeeb static void
389*0936c648SBjoern A. Zeeb lkpi_lsta_free(struct lkpi_sta *lsta, struct ieee80211_node *ni)
390*0936c648SBjoern A. Zeeb {
391*0936c648SBjoern A. Zeeb 	struct mbuf *m;
392*0936c648SBjoern A. Zeeb 
393*0936c648SBjoern A. Zeeb 	if (lsta->added_to_drv)
394*0936c648SBjoern A. Zeeb 		panic("%s: Trying to free an lsta still known to firmware: "
395*0936c648SBjoern A. Zeeb 		    "lsta %p ni %p added_to_drv %d\n",
396*0936c648SBjoern A. Zeeb 		    __func__, lsta, ni, lsta->added_to_drv);
397*0936c648SBjoern A. Zeeb 
398*0936c648SBjoern A. Zeeb 	/* XXX-BZ free resources, ... */
399*0936c648SBjoern A. Zeeb 	IMPROVE();
400*0936c648SBjoern A. Zeeb 
401*0936c648SBjoern A. Zeeb 	/* XXX locking */
402*0936c648SBjoern A. Zeeb 	lsta->txq_ready = false;
403*0936c648SBjoern A. Zeeb 
404*0936c648SBjoern A. Zeeb 	/* Drain taskq, won't be restarted until added_to_drv is set again. */
405*0936c648SBjoern A. Zeeb 	while (taskqueue_cancel(taskqueue_thread, &lsta->txq_task, NULL) != 0)
406*0936c648SBjoern A. Zeeb 		taskqueue_drain(taskqueue_thread, &lsta->txq_task);
407*0936c648SBjoern A. Zeeb 
408*0936c648SBjoern A. Zeeb 	/* Flush mbufq (make sure to release ni refs!). */
409*0936c648SBjoern A. Zeeb 	m = mbufq_dequeue(&lsta->txq);
410*0936c648SBjoern A. Zeeb 	while (m != NULL) {
411*0936c648SBjoern A. Zeeb 		struct ieee80211_node *nim;
412*0936c648SBjoern A. Zeeb 
413*0936c648SBjoern A. Zeeb 		nim = (struct ieee80211_node *)m->m_pkthdr.rcvif;
414*0936c648SBjoern A. Zeeb 		if (nim != NULL)
415*0936c648SBjoern A. Zeeb 			ieee80211_free_node(nim);
416*0936c648SBjoern A. Zeeb 		m_freem(m);
417*0936c648SBjoern A. Zeeb 		m = mbufq_dequeue(&lsta->txq);
418*0936c648SBjoern A. Zeeb 	}
419*0936c648SBjoern A. Zeeb 	KASSERT(mbufq_empty(&lsta->txq), ("%s: lsta %p has txq len %d != 0\n",
420*0936c648SBjoern A. Zeeb 	    __func__, lsta, mbufq_len(&lsta->txq)));
421*0936c648SBjoern A. Zeeb 
422*0936c648SBjoern A. Zeeb 	/* Drain sta->txq[] */
423*0936c648SBjoern A. Zeeb 	mtx_destroy(&lsta->txq_mtx);
424*0936c648SBjoern A. Zeeb 
425*0936c648SBjoern A. Zeeb 	/* Remove lsta from vif; that is done by the state machine.  Should assert it? */
426*0936c648SBjoern A. Zeeb 
427*0936c648SBjoern A. Zeeb 	IMPROVE("Make sure everything is cleaned up.");
428*0936c648SBjoern A. Zeeb 
429*0936c648SBjoern A. Zeeb 	/* Free lsta. */
430*0936c648SBjoern A. Zeeb 	lsta->ni = NULL;
431*0936c648SBjoern A. Zeeb 	ni->ni_drv_data = NULL;
432*0936c648SBjoern A. Zeeb 	free(lsta, M_LKPI80211);
433*0936c648SBjoern A. Zeeb }
434*0936c648SBjoern A. Zeeb 
435*0936c648SBjoern A. Zeeb 
4366b4cac81SBjoern A. Zeeb static enum nl80211_band
4376b4cac81SBjoern A. Zeeb lkpi_net80211_chan_to_nl80211_band(struct ieee80211_channel *c)
4386b4cac81SBjoern A. Zeeb {
4396b4cac81SBjoern A. Zeeb 
4406b4cac81SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_2GHZ(c))
4416b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_2GHZ);
4426b4cac81SBjoern A. Zeeb 	else if (IEEE80211_IS_CHAN_5GHZ(c))
4436b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_5GHZ);
4446b4cac81SBjoern A. Zeeb #ifdef __notyet__
4456b4cac81SBjoern A. Zeeb 	else if ()
4466b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_6GHZ);
4476b4cac81SBjoern A. Zeeb 	else if ()
4486b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_60GHZ);
4496b4cac81SBjoern A. Zeeb 	else if (IEEE80211_IS_CHAN_GSM(c))
4506b4cac81SBjoern A. Zeeb 		return (NL80211_BAND_XXX);
4516b4cac81SBjoern A. Zeeb #endif
4526b4cac81SBjoern A. Zeeb 	else
4536b4cac81SBjoern A. Zeeb 		panic("%s: unsupported band. c %p flags %#x\n",
4546b4cac81SBjoern A. Zeeb 		    __func__, c, c->ic_flags);
4556b4cac81SBjoern A. Zeeb }
4566b4cac81SBjoern A. Zeeb 
4576b4cac81SBjoern A. Zeeb static uint32_t
4586b4cac81SBjoern A. Zeeb lkpi_nl80211_band_to_net80211_band(enum nl80211_band band)
4596b4cac81SBjoern A. Zeeb {
4606b4cac81SBjoern A. Zeeb 
4616b4cac81SBjoern A. Zeeb 	/* XXX-BZ this is just silly; net80211 is too convoluted. */
4626b4cac81SBjoern A. Zeeb 	/* IEEE80211_CHAN_A / _G / .. doesn't really work either. */
4636b4cac81SBjoern A. Zeeb 	switch (band) {
4646b4cac81SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
4656b4cac81SBjoern A. Zeeb 		return (IEEE80211_CHAN_2GHZ);
4666b4cac81SBjoern A. Zeeb 		break;
4676b4cac81SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
4686b4cac81SBjoern A. Zeeb 		return (IEEE80211_CHAN_5GHZ);
4696b4cac81SBjoern A. Zeeb 		break;
4706b4cac81SBjoern A. Zeeb 	case NL80211_BAND_60GHZ:
4716b4cac81SBjoern A. Zeeb 		break;
4726b4cac81SBjoern A. Zeeb 	case NL80211_BAND_6GHZ:
4736b4cac81SBjoern A. Zeeb 		break;
4746b4cac81SBjoern A. Zeeb 	default:
4756b4cac81SBjoern A. Zeeb 		panic("%s: unsupported band %u\n", __func__, band);
4766b4cac81SBjoern A. Zeeb 		break;
4776b4cac81SBjoern A. Zeeb 	}
4786b4cac81SBjoern A. Zeeb 
4796b4cac81SBjoern A. Zeeb 	IMPROVE();
4806b4cac81SBjoern A. Zeeb 	return (0x00);
4816b4cac81SBjoern A. Zeeb }
4826b4cac81SBjoern A. Zeeb 
483e3a0b120SBjoern A. Zeeb #if 0
4846b4cac81SBjoern A. Zeeb static enum ieee80211_ac_numbers
4856b4cac81SBjoern A. Zeeb lkpi_ac_net_to_l80211(int ac)
4866b4cac81SBjoern A. Zeeb {
4876b4cac81SBjoern A. Zeeb 
4886b4cac81SBjoern A. Zeeb 	switch (ac) {
4896b4cac81SBjoern A. Zeeb 	case WME_AC_VO:
4906b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_VO);
4916b4cac81SBjoern A. Zeeb 	case WME_AC_VI:
4926b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_VI);
4936b4cac81SBjoern A. Zeeb 	case WME_AC_BE:
4946b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_BE);
4956b4cac81SBjoern A. Zeeb 	case WME_AC_BK:
4966b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_BK);
4976b4cac81SBjoern A. Zeeb 	default:
4986b4cac81SBjoern A. Zeeb 		printf("%s: invalid WME_AC_* input: ac = %d\n", __func__, ac);
4996b4cac81SBjoern A. Zeeb 		return (IEEE80211_AC_BE);
5006b4cac81SBjoern A. Zeeb 	}
5016b4cac81SBjoern A. Zeeb }
502e3a0b120SBjoern A. Zeeb #endif
5036b4cac81SBjoern A. Zeeb 
5046b4cac81SBjoern A. Zeeb static enum nl80211_iftype
5056b4cac81SBjoern A. Zeeb lkpi_opmode_to_vif_type(enum ieee80211_opmode opmode)
5066b4cac81SBjoern A. Zeeb {
5076b4cac81SBjoern A. Zeeb 
5086b4cac81SBjoern A. Zeeb 	switch (opmode) {
5096b4cac81SBjoern A. Zeeb 	case IEEE80211_M_IBSS:
5106b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_ADHOC);
5116b4cac81SBjoern A. Zeeb 		break;
5126b4cac81SBjoern A. Zeeb 	case IEEE80211_M_STA:
5136b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_STATION);
5146b4cac81SBjoern A. Zeeb 		break;
5156b4cac81SBjoern A. Zeeb 	case IEEE80211_M_WDS:
5166b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_WDS);
5176b4cac81SBjoern A. Zeeb 		break;
5186b4cac81SBjoern A. Zeeb 	case IEEE80211_M_HOSTAP:
5196b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_AP);
5206b4cac81SBjoern A. Zeeb 		break;
5216b4cac81SBjoern A. Zeeb 	case IEEE80211_M_MONITOR:
5226b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_MONITOR);
5236b4cac81SBjoern A. Zeeb 		break;
5246b4cac81SBjoern A. Zeeb 	case IEEE80211_M_MBSS:
5256b4cac81SBjoern A. Zeeb 		return (NL80211_IFTYPE_MESH_POINT);
5266b4cac81SBjoern A. Zeeb 		break;
5276b4cac81SBjoern A. Zeeb 	case IEEE80211_M_AHDEMO:
5286b4cac81SBjoern A. Zeeb 		/* FALLTHROUGH */
5296b4cac81SBjoern A. Zeeb 	default:
5306b4cac81SBjoern A. Zeeb 		printf("ERROR: %s: unsupported opmode %d\n", __func__, opmode);
5316b4cac81SBjoern A. Zeeb 		/* FALLTHROUGH */
5326b4cac81SBjoern A. Zeeb 	}
5336b4cac81SBjoern A. Zeeb 	return (NL80211_IFTYPE_UNSPECIFIED);
5346b4cac81SBjoern A. Zeeb }
5356b4cac81SBjoern A. Zeeb 
536b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
5376b4cac81SBjoern A. Zeeb static uint32_t
5386b4cac81SBjoern A. Zeeb lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite)
5396b4cac81SBjoern A. Zeeb {
5406b4cac81SBjoern A. Zeeb 
5416b4cac81SBjoern A. Zeeb 	switch (wlan_cipher_suite) {
5426b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP40:
5436b4cac81SBjoern A. Zeeb 		return (IEEE80211_CRYPTO_WEP);
5446b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_TKIP:
5456b4cac81SBjoern A. Zeeb 		return (IEEE80211_CRYPTO_TKIP);
5466b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP:
5476b4cac81SBjoern A. Zeeb 		return (IEEE80211_CIPHER_AES_CCM);
5486b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_WEP104:
5496b4cac81SBjoern A. Zeeb 		return (IEEE80211_CRYPTO_WEP);
5506b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_AES_CMAC:
5516b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_GCMP:
5526b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_GCMP_256:
5536b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP_256:
5546b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_BIP_GMAC_128:
5556b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_BIP_GMAC_256:
5566b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_BIP_CMAC_256:
5576b4cac81SBjoern A. Zeeb 		printf("%s: unsupported WLAN Cipher Suite %#08x | %u\n", __func__,
5586b4cac81SBjoern A. Zeeb 		    wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff);
5596b4cac81SBjoern A. Zeeb 		break;
5606b4cac81SBjoern A. Zeeb 	default:
5616b4cac81SBjoern A. Zeeb 		printf("%s: unknown WLAN Cipher Suite %#08x | %u\n", __func__,
5626b4cac81SBjoern A. Zeeb 		    wlan_cipher_suite >> 8, wlan_cipher_suite & 0xff);
5636b4cac81SBjoern A. Zeeb 	}
5646b4cac81SBjoern A. Zeeb 
5656b4cac81SBjoern A. Zeeb 	return (0);
5666b4cac81SBjoern A. Zeeb }
5676b4cac81SBjoern A. Zeeb 
5686b4cac81SBjoern A. Zeeb static uint32_t
5696b4cac81SBjoern A. Zeeb lkpi_net80211_to_l80211_cipher_suite(uint32_t cipher, uint8_t keylen)
5706b4cac81SBjoern A. Zeeb {
5716b4cac81SBjoern A. Zeeb 
5726b4cac81SBjoern A. Zeeb 	switch (cipher) {
5736b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_TKIP:
5746b4cac81SBjoern A. Zeeb 		return (WLAN_CIPHER_SUITE_TKIP);
5756b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_AES_CCM:
5766b4cac81SBjoern A. Zeeb 		return (WLAN_CIPHER_SUITE_CCMP);
5776b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_WEP:
5786b4cac81SBjoern A. Zeeb 		if (keylen < 8)
5796b4cac81SBjoern A. Zeeb 			return (WLAN_CIPHER_SUITE_WEP40);
5806b4cac81SBjoern A. Zeeb 		else
5816b4cac81SBjoern A. Zeeb 			return (WLAN_CIPHER_SUITE_WEP104);
5826b4cac81SBjoern A. Zeeb 		break;
5836b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_AES_OCB:
5846b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_TKIPMIC:
5856b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_CKIP:
5866b4cac81SBjoern A. Zeeb 	case IEEE80211_CIPHER_NONE:
5876b4cac81SBjoern A. Zeeb 		printf("%s: unsupported cipher %#010x\n", __func__, cipher);
5886b4cac81SBjoern A. Zeeb 		break;
5896b4cac81SBjoern A. Zeeb 	default:
5906b4cac81SBjoern A. Zeeb 		printf("%s: unknown cipher %#010x\n", __func__, cipher);
5916b4cac81SBjoern A. Zeeb 	};
5926b4cac81SBjoern A. Zeeb 	return (0);
5936b4cac81SBjoern A. Zeeb }
5946b4cac81SBjoern A. Zeeb #endif
5956b4cac81SBjoern A. Zeeb 
5966b4cac81SBjoern A. Zeeb #ifdef __notyet__
5976b4cac81SBjoern A. Zeeb static enum ieee80211_sta_state
5986b4cac81SBjoern A. Zeeb lkpi_net80211_state_to_sta_state(enum ieee80211_state state)
5996b4cac81SBjoern A. Zeeb {
6006b4cac81SBjoern A. Zeeb 
6016b4cac81SBjoern A. Zeeb 	/*
6026b4cac81SBjoern A. Zeeb 	 * XXX-BZ The net80211 states are "try to ..", the lkpi8011 states are
6036b4cac81SBjoern A. Zeeb 	 * "done".  Also ASSOC/AUTHORIZED are both "RUN" then?
6046b4cac81SBjoern A. Zeeb 	 */
6056b4cac81SBjoern A. Zeeb 	switch (state) {
6066b4cac81SBjoern A. Zeeb 	case IEEE80211_S_INIT:
6076b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_NOTEXIST);
6086b4cac81SBjoern A. Zeeb 	case IEEE80211_S_SCAN:
6096b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_NONE);
6106b4cac81SBjoern A. Zeeb 	case IEEE80211_S_AUTH:
6116b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_AUTH);
6126b4cac81SBjoern A. Zeeb 	case IEEE80211_S_ASSOC:
6136b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_ASSOC);
6146b4cac81SBjoern A. Zeeb 	case IEEE80211_S_RUN:
6156b4cac81SBjoern A. Zeeb 		return (IEEE80211_STA_AUTHORIZED);
6166b4cac81SBjoern A. Zeeb 	case IEEE80211_S_CAC:
6176b4cac81SBjoern A. Zeeb 	case IEEE80211_S_CSA:
6186b4cac81SBjoern A. Zeeb 	case IEEE80211_S_SLEEP:
6196b4cac81SBjoern A. Zeeb 	default:
6206b4cac81SBjoern A. Zeeb 		UNIMPLEMENTED;
6216b4cac81SBjoern A. Zeeb 	};
6226b4cac81SBjoern A. Zeeb 
6236b4cac81SBjoern A. Zeeb 	return (IEEE80211_STA_NOTEXIST);
6246b4cac81SBjoern A. Zeeb }
6256b4cac81SBjoern A. Zeeb #endif
6266b4cac81SBjoern A. Zeeb 
6276b4cac81SBjoern A. Zeeb static struct linuxkpi_ieee80211_channel *
6286b4cac81SBjoern A. Zeeb lkpi_find_lkpi80211_chan(struct lkpi_hw *lhw,
6296b4cac81SBjoern A. Zeeb     struct ieee80211_channel *c)
6306b4cac81SBjoern A. Zeeb {
6316b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
6326b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *channels;
6336b4cac81SBjoern A. Zeeb 	enum nl80211_band band;
6346b4cac81SBjoern A. Zeeb 	int i, nchans;
6356b4cac81SBjoern A. Zeeb 
6366b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
6376b4cac81SBjoern A. Zeeb 	band = lkpi_net80211_chan_to_nl80211_band(c);
6386b4cac81SBjoern A. Zeeb 	if (hw->wiphy->bands[band] == NULL)
6396b4cac81SBjoern A. Zeeb 		return (NULL);
6406b4cac81SBjoern A. Zeeb 
6416b4cac81SBjoern A. Zeeb 	nchans = hw->wiphy->bands[band]->n_channels;
6426b4cac81SBjoern A. Zeeb 	if (nchans <= 0)
6436b4cac81SBjoern A. Zeeb 		return (NULL);
6446b4cac81SBjoern A. Zeeb 
6456b4cac81SBjoern A. Zeeb 	channels = hw->wiphy->bands[band]->channels;
6466b4cac81SBjoern A. Zeeb 	for (i = 0; i < nchans; i++) {
6476b4cac81SBjoern A. Zeeb 		if (channels[i].hw_value == c->ic_ieee)
6486b4cac81SBjoern A. Zeeb 			return (&channels[i]);
6496b4cac81SBjoern A. Zeeb 	}
6506b4cac81SBjoern A. Zeeb 
6516b4cac81SBjoern A. Zeeb 	return (NULL);
6526b4cac81SBjoern A. Zeeb }
6536b4cac81SBjoern A. Zeeb 
6542ac8a218SBjoern A. Zeeb #if 0
6556b4cac81SBjoern A. Zeeb static struct linuxkpi_ieee80211_channel *
6566b4cac81SBjoern A. Zeeb lkpi_get_lkpi80211_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
6576b4cac81SBjoern A. Zeeb {
6586b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
6596b4cac81SBjoern A. Zeeb 	struct ieee80211_channel *c;
6606b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
6616b4cac81SBjoern A. Zeeb 
6626b4cac81SBjoern A. Zeeb 	chan = NULL;
6636b4cac81SBjoern A. Zeeb 	if (ni != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC)
6646b4cac81SBjoern A. Zeeb 		c = ni->ni_chan;
6656b4cac81SBjoern A. Zeeb 	else if (ic->ic_bsschan != IEEE80211_CHAN_ANYC)
6666b4cac81SBjoern A. Zeeb 		c = ic->ic_bsschan;
6676b4cac81SBjoern A. Zeeb 	else if (ic->ic_curchan != IEEE80211_CHAN_ANYC)
6686b4cac81SBjoern A. Zeeb 		c = ic->ic_curchan;
6696b4cac81SBjoern A. Zeeb 	else
6706b4cac81SBjoern A. Zeeb 		c = NULL;
6716b4cac81SBjoern A. Zeeb 
6726b4cac81SBjoern A. Zeeb 	if (c != NULL && c != IEEE80211_CHAN_ANYC) {
6736b4cac81SBjoern A. Zeeb 		lhw = ic->ic_softc;
6746b4cac81SBjoern A. Zeeb 		chan = lkpi_find_lkpi80211_chan(lhw, c);
6756b4cac81SBjoern A. Zeeb 	}
6766b4cac81SBjoern A. Zeeb 
6776b4cac81SBjoern A. Zeeb 	return (chan);
6786b4cac81SBjoern A. Zeeb }
6792ac8a218SBjoern A. Zeeb #endif
6806b4cac81SBjoern A. Zeeb 
6812e183d99SBjoern A. Zeeb struct linuxkpi_ieee80211_channel *
6822e183d99SBjoern A. Zeeb linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
6832e183d99SBjoern A. Zeeb {
6842e183d99SBjoern A. Zeeb 	enum nl80211_band band;
6852e183d99SBjoern A. Zeeb 
6862e183d99SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
6872e183d99SBjoern A. Zeeb 		struct ieee80211_supported_band *supband;
6882e183d99SBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *channels;
6892e183d99SBjoern A. Zeeb 		int i;
6902e183d99SBjoern A. Zeeb 
6912e183d99SBjoern A. Zeeb 		supband = wiphy->bands[band];
6922e183d99SBjoern A. Zeeb 		if (supband == NULL || supband->n_channels == 0)
6932e183d99SBjoern A. Zeeb 			continue;
6942e183d99SBjoern A. Zeeb 
6952e183d99SBjoern A. Zeeb 		channels = supband->channels;
6962e183d99SBjoern A. Zeeb 		for (i = 0; i < supband->n_channels; i++) {
6972e183d99SBjoern A. Zeeb 			if (channels[i].center_freq == freq)
6982e183d99SBjoern A. Zeeb 				return (&channels[i]);
6992e183d99SBjoern A. Zeeb 		}
7002e183d99SBjoern A. Zeeb 	}
7012e183d99SBjoern A. Zeeb 
7022e183d99SBjoern A. Zeeb 	return (NULL);
7032e183d99SBjoern A. Zeeb }
7042e183d99SBjoern A. Zeeb 
705b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
7066b4cac81SBjoern A. Zeeb static int
7076b4cac81SBjoern A. Zeeb _lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
7086b4cac81SBjoern A. Zeeb     enum set_key_cmd cmd)
7096b4cac81SBjoern A. Zeeb {
7106b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
7116b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
7126b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
7136b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
7146b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
7156b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
7166b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
7176b4cac81SBjoern A. Zeeb 	struct ieee80211_key_conf *kc;
7186b4cac81SBjoern A. Zeeb 	int error;
7196b4cac81SBjoern A. Zeeb 
7206b4cac81SBjoern A. Zeeb 	/* XXX TODO Check (k->wk_flags & IEEE80211_KEY_SWENCRYPT) and don't upload to driver/hw? */
7216b4cac81SBjoern A. Zeeb 
7226b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
7236b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
7246b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
7256b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
7266b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
7276b4cac81SBjoern A. Zeeb 
7286b4cac81SBjoern A. Zeeb 	memset(&kc, 0, sizeof(kc));
7296b4cac81SBjoern A. Zeeb 	kc = malloc(sizeof(*kc) + k->wk_keylen, M_LKPI80211, M_WAITOK | M_ZERO);
7306b4cac81SBjoern A. Zeeb 	kc->cipher = lkpi_net80211_to_l80211_cipher_suite(
7316b4cac81SBjoern A. Zeeb 	    k->wk_cipher->ic_cipher, k->wk_keylen);
7326b4cac81SBjoern A. Zeeb 	kc->keyidx = k->wk_keyix;
7336b4cac81SBjoern A. Zeeb #if 0
7346b4cac81SBjoern A. Zeeb 	kc->hw_key_idx = /* set by hw and needs to be passed for TX */;
7356b4cac81SBjoern A. Zeeb #endif
7366b4cac81SBjoern A. Zeeb 	atomic64_set(&kc->tx_pn, k->wk_keytsc);
7376b4cac81SBjoern A. Zeeb 	kc->keylen = k->wk_keylen;
7386b4cac81SBjoern A. Zeeb 	memcpy(kc->key, k->wk_key, k->wk_keylen);
7396b4cac81SBjoern A. Zeeb 
7406b4cac81SBjoern A. Zeeb 	switch (kc->cipher) {
7416b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_CCMP:
7426b4cac81SBjoern A. Zeeb 		kc->iv_len = k->wk_cipher->ic_header;
7436b4cac81SBjoern A. Zeeb 		kc->icv_len = k->wk_cipher->ic_trailer;
7446b4cac81SBjoern A. Zeeb 		break;
7456b4cac81SBjoern A. Zeeb 	case WLAN_CIPHER_SUITE_TKIP:
7466b4cac81SBjoern A. Zeeb 	default:
7476b4cac81SBjoern A. Zeeb 		IMPROVE();
7486b4cac81SBjoern A. Zeeb 		return (0);
7496b4cac81SBjoern A. Zeeb 	};
7506b4cac81SBjoern A. Zeeb 
7516b4cac81SBjoern A. Zeeb 	ni = vap->iv_bss;
7526b4cac81SBjoern A. Zeeb 	sta = ieee80211_find_sta(vif, ni->ni_bssid);
7536b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
7546b4cac81SBjoern A. Zeeb 		struct lkpi_sta *lsta;
7556b4cac81SBjoern A. Zeeb 
7566b4cac81SBjoern A. Zeeb 		lsta = STA_TO_LSTA(sta);
7576b4cac81SBjoern A. Zeeb 		lsta->kc = kc;
7586b4cac81SBjoern A. Zeeb 	}
7596b4cac81SBjoern A. Zeeb 
7606b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_set_key(hw, cmd, vif, sta, kc);
7616b4cac81SBjoern A. Zeeb 	if (error != 0) {
7626b4cac81SBjoern A. Zeeb 		/* XXX-BZ leaking kc currently */
7636b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: set_key failed: %d\n", __func__, error);
7646b4cac81SBjoern A. Zeeb 		return (0);
7656b4cac81SBjoern A. Zeeb 	} else {
7666b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: set_key succeeded: keyidx %u hw_key_idx %u "
7676b4cac81SBjoern A. Zeeb 		    "flags %#10x\n", __func__,
7686b4cac81SBjoern A. Zeeb 		    kc->keyidx, kc->hw_key_idx, kc->flags);
7696b4cac81SBjoern A. Zeeb 		return (1);
7706b4cac81SBjoern A. Zeeb 	}
7716b4cac81SBjoern A. Zeeb }
7726b4cac81SBjoern A. Zeeb 
7736b4cac81SBjoern A. Zeeb static int
7746b4cac81SBjoern A. Zeeb lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
7756b4cac81SBjoern A. Zeeb {
7766b4cac81SBjoern A. Zeeb 
7776b4cac81SBjoern A. Zeeb 	/* XXX-BZ one day we should replace this iterating over VIFs, or node list? */
7786b4cac81SBjoern A. Zeeb 	return (_lkpi_iv_key_set_delete(vap, k, DISABLE_KEY));
7796b4cac81SBjoern A. Zeeb }
7806b4cac81SBjoern A. Zeeb static  int
7816b4cac81SBjoern A. Zeeb lkpi_iv_key_set(struct ieee80211vap *vap, const struct ieee80211_key *k)
7826b4cac81SBjoern A. Zeeb {
7836b4cac81SBjoern A. Zeeb 
7846b4cac81SBjoern A. Zeeb 	return (_lkpi_iv_key_set_delete(vap, k, SET_KEY));
7856b4cac81SBjoern A. Zeeb }
7866b4cac81SBjoern A. Zeeb #endif
7876b4cac81SBjoern A. Zeeb 
7886b4cac81SBjoern A. Zeeb static u_int
7896b4cac81SBjoern A. Zeeb lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
7906b4cac81SBjoern A. Zeeb {
7916b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr_list *mc_list;
7926b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr *addr;
7936b4cac81SBjoern A. Zeeb 
7946b4cac81SBjoern A. Zeeb 	KASSERT(arg != NULL && sdl != NULL, ("%s: arg %p sdl %p cnt %u\n",
7956b4cac81SBjoern A. Zeeb 	    __func__, arg, sdl, cnt));
7966b4cac81SBjoern A. Zeeb 
7976b4cac81SBjoern A. Zeeb 	mc_list = arg;
7986b4cac81SBjoern A. Zeeb 	/* If it is on the list already skip it. */
7996b4cac81SBjoern A. Zeeb 	netdev_hw_addr_list_for_each(addr, mc_list) {
8006b4cac81SBjoern A. Zeeb 		if (!memcmp(addr->addr, LLADDR(sdl), sdl->sdl_alen))
8016b4cac81SBjoern A. Zeeb 			return (0);
8026b4cac81SBjoern A. Zeeb 	}
8036b4cac81SBjoern A. Zeeb 
8046b4cac81SBjoern A. Zeeb 	addr = malloc(sizeof(*addr), M_LKPI80211, M_NOWAIT | M_ZERO);
8056b4cac81SBjoern A. Zeeb 	if (addr == NULL)
8066b4cac81SBjoern A. Zeeb 		return (0);
8076b4cac81SBjoern A. Zeeb 
8086b4cac81SBjoern A. Zeeb 	INIT_LIST_HEAD(&addr->addr_list);
8096b4cac81SBjoern A. Zeeb 	memcpy(addr->addr, LLADDR(sdl), sdl->sdl_alen);
8106b4cac81SBjoern A. Zeeb 	/* XXX this should be a netdev function? */
8116b4cac81SBjoern A. Zeeb 	list_add(&addr->addr_list, &mc_list->addr_list);
8126b4cac81SBjoern A. Zeeb 	mc_list->count++;
8136b4cac81SBjoern A. Zeeb 
8149d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
8159d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
8166b4cac81SBjoern A. Zeeb 		printf("%s:%d: mc_list count %d: added %6D\n",
8176b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, mc_list->count, addr->addr, ":");
8189d9ba2b7SBjoern A. Zeeb #endif
8196b4cac81SBjoern A. Zeeb 
8206b4cac81SBjoern A. Zeeb 	return (1);
8216b4cac81SBjoern A. Zeeb }
8226b4cac81SBjoern A. Zeeb 
8236b4cac81SBjoern A. Zeeb static void
8246b4cac81SBjoern A. Zeeb lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
8256b4cac81SBjoern A. Zeeb {
8266b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
8276b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
8286b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr_list mc_list;
8296b4cac81SBjoern A. Zeeb 	struct list_head *le, *next;
8306b4cac81SBjoern A. Zeeb 	struct netdev_hw_addr *addr;
8316b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
8326b4cac81SBjoern A. Zeeb 	u64 mc;
8336b4cac81SBjoern A. Zeeb 	unsigned int changed_flags, total_flags;
8346b4cac81SBjoern A. Zeeb 
8356b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
8366b4cac81SBjoern A. Zeeb 
8376b4cac81SBjoern A. Zeeb 	if (lhw->ops->prepare_multicast == NULL ||
8386b4cac81SBjoern A. Zeeb 	    lhw->ops->configure_filter == NULL)
8396b4cac81SBjoern A. Zeeb 		return;
8406b4cac81SBjoern A. Zeeb 
8416b4cac81SBjoern A. Zeeb 	if (!lhw->update_mc && !force)
8426b4cac81SBjoern A. Zeeb 		return;
8436b4cac81SBjoern A. Zeeb 
8446b4cac81SBjoern A. Zeeb 	changed_flags = total_flags = 0;
8456b4cac81SBjoern A. Zeeb 	mc_list.count = 0;
8466b4cac81SBjoern A. Zeeb 	INIT_LIST_HEAD(&mc_list.addr_list);
8476b4cac81SBjoern A. Zeeb 	if (ic->ic_allmulti == 0) {
8486b4cac81SBjoern A. Zeeb 		TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
8496b4cac81SBjoern A. Zeeb 			if_foreach_llmaddr(vap->iv_ifp,
8506b4cac81SBjoern A. Zeeb 			    lkpi_ic_update_mcast_copy, &mc_list);
8516b4cac81SBjoern A. Zeeb 	} else {
8526b4cac81SBjoern A. Zeeb 		changed_flags |= FIF_ALLMULTI;
8536b4cac81SBjoern A. Zeeb 	}
8546b4cac81SBjoern A. Zeeb 
8556b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
8566b4cac81SBjoern A. Zeeb 	mc = lkpi_80211_mo_prepare_multicast(hw, &mc_list);
8576b4cac81SBjoern A. Zeeb 	/*
8586b4cac81SBjoern A. Zeeb 	 * XXX-BZ make sure to get this sorted what is a change,
8596b4cac81SBjoern A. Zeeb 	 * what gets all set; what was already set?
8606b4cac81SBjoern A. Zeeb 	 */
8616b4cac81SBjoern A. Zeeb 	total_flags = changed_flags;
8626b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_configure_filter(hw, changed_flags, &total_flags, mc);
8636b4cac81SBjoern A. Zeeb 
8649d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
8659d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
8666b4cac81SBjoern A. Zeeb 		printf("%s: changed_flags %#06x count %d total_flags %#010x\n",
8676b4cac81SBjoern A. Zeeb 		    __func__, changed_flags, mc_list.count, total_flags);
8689d9ba2b7SBjoern A. Zeeb #endif
8696b4cac81SBjoern A. Zeeb 
8706b4cac81SBjoern A. Zeeb 	if (mc_list.count != 0) {
8716b4cac81SBjoern A. Zeeb 		list_for_each_safe(le, next, &mc_list.addr_list) {
8726b4cac81SBjoern A. Zeeb 			addr = list_entry(le, struct netdev_hw_addr, addr_list);
8736b4cac81SBjoern A. Zeeb 			free(addr, M_LKPI80211);
8746b4cac81SBjoern A. Zeeb 			mc_list.count--;
8756b4cac81SBjoern A. Zeeb 		}
8766b4cac81SBjoern A. Zeeb 	}
8776b4cac81SBjoern A. Zeeb 	KASSERT(mc_list.count == 0, ("%s: mc_list %p count %d != 0\n",
8786b4cac81SBjoern A. Zeeb 	    __func__, &mc_list, mc_list.count));
8796b4cac81SBjoern A. Zeeb }
8806b4cac81SBjoern A. Zeeb 
881fa8f007dSBjoern A. Zeeb static enum ieee80211_bss_changed
882fa8f007dSBjoern A. Zeeb lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
883fa8f007dSBjoern A. Zeeb     struct ieee80211vap *vap, const char *_f, int _l)
884fa8f007dSBjoern A. Zeeb {
885fa8f007dSBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
886fa8f007dSBjoern A. Zeeb 
887fa8f007dSBjoern A. Zeeb 	bss_changed = 0;
888fa8f007dSBjoern A. Zeeb 
8899d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
8909d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
891fa8f007dSBjoern A. Zeeb 		printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
892fa8f007dSBjoern A. Zeeb 		    "dtim_period %u sync_dtim_count %u sync_tsf %ju "
893fa8f007dSBjoern A. Zeeb 		    "sync_device_ts %u bss_changed %#08x\n",
894fa8f007dSBjoern A. Zeeb 			__func__, __LINE__, _f, _l,
895616e1330SBjoern A. Zeeb 			vif->cfg.assoc, vif->cfg.aid,
896fa8f007dSBjoern A. Zeeb 			vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
897fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_dtim_count,
898fa8f007dSBjoern A. Zeeb 			(uintmax_t)vif->bss_conf.sync_tsf,
899fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_device_ts,
900fa8f007dSBjoern A. Zeeb 			bss_changed);
9019d9ba2b7SBjoern A. Zeeb #endif
902fa8f007dSBjoern A. Zeeb 
903fa8f007dSBjoern A. Zeeb 	if (vif->bss_conf.beacon_int != ni->ni_intval) {
904fa8f007dSBjoern A. Zeeb 		vif->bss_conf.beacon_int = ni->ni_intval;
905fa8f007dSBjoern A. Zeeb 		/* iwlwifi FW bug workaround; iwl_mvm_mac_sta_state. */
906fa8f007dSBjoern A. Zeeb 		if (vif->bss_conf.beacon_int < 16)
907fa8f007dSBjoern A. Zeeb 			vif->bss_conf.beacon_int = 16;
908fa8f007dSBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_BEACON_INT;
909fa8f007dSBjoern A. Zeeb 	}
910fa8f007dSBjoern A. Zeeb 	if (vif->bss_conf.dtim_period != vap->iv_dtim_period &&
911fa8f007dSBjoern A. Zeeb 	    vap->iv_dtim_period > 0) {
912fa8f007dSBjoern A. Zeeb 		vif->bss_conf.dtim_period = vap->iv_dtim_period;
913fa8f007dSBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_BEACON_INFO;
914fa8f007dSBjoern A. Zeeb 	}
915fa8f007dSBjoern A. Zeeb 
916fa8f007dSBjoern A. Zeeb 	vif->bss_conf.sync_dtim_count = vap->iv_dtim_count;
917fa8f007dSBjoern A. Zeeb 	vif->bss_conf.sync_tsf = le64toh(ni->ni_tstamp.tsf);
918fa8f007dSBjoern A. Zeeb 	/* vif->bss_conf.sync_device_ts = set in linuxkpi_ieee80211_rx. */
919fa8f007dSBjoern A. Zeeb 
9209d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
9219d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
922fa8f007dSBjoern A. Zeeb 		printf("%s:%d [%s:%d] assoc %d aid %d beacon_int %u "
923fa8f007dSBjoern A. Zeeb 		    "dtim_period %u sync_dtim_count %u sync_tsf %ju "
924fa8f007dSBjoern A. Zeeb 		    "sync_device_ts %u bss_changed %#08x\n",
925fa8f007dSBjoern A. Zeeb 			__func__, __LINE__, _f, _l,
926616e1330SBjoern A. Zeeb 			vif->cfg.assoc, vif->cfg.aid,
927fa8f007dSBjoern A. Zeeb 			vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
928fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_dtim_count,
929fa8f007dSBjoern A. Zeeb 			(uintmax_t)vif->bss_conf.sync_tsf,
930fa8f007dSBjoern A. Zeeb 			vif->bss_conf.sync_device_ts,
931fa8f007dSBjoern A. Zeeb 			bss_changed);
9329d9ba2b7SBjoern A. Zeeb #endif
933fa8f007dSBjoern A. Zeeb 
934fa8f007dSBjoern A. Zeeb 	return (bss_changed);
935fa8f007dSBjoern A. Zeeb }
936fa8f007dSBjoern A. Zeeb 
9376b4cac81SBjoern A. Zeeb static void
9386b4cac81SBjoern A. Zeeb lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
9396b4cac81SBjoern A. Zeeb {
9406b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
9416b4cac81SBjoern A. Zeeb 	int error;
9428ac540d3SBjoern A. Zeeb 	bool cancel;
9436b4cac81SBjoern A. Zeeb 
9448ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
9458ac540d3SBjoern A. Zeeb 	cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
9468ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
9478ac540d3SBjoern A. Zeeb 	if (!cancel)
9486b4cac81SBjoern A. Zeeb 		return;
9496b4cac81SBjoern A. Zeeb 
9506b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
9516b4cac81SBjoern A. Zeeb 
952bec76628SBjoern A. Zeeb 	IEEE80211_UNLOCK(lhw->ic);
953bec76628SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
9546b4cac81SBjoern A. Zeeb 	/* Need to cancel the scan. */
9556b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_cancel_hw_scan(hw, vif);
9568ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
9576b4cac81SBjoern A. Zeeb 
9586b4cac81SBjoern A. Zeeb 	/* Need to make sure we see ieee80211_scan_completed. */
9598ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
9608ac540d3SBjoern A. Zeeb 	if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0)
9618ac540d3SBjoern A. Zeeb 		error = msleep(lhw, &lhw->scan_mtx, 0, "lhwscanstop", hz/2);
9628ac540d3SBjoern A. Zeeb 	cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
9638ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
9648ac540d3SBjoern A. Zeeb 
965bec76628SBjoern A. Zeeb 	IEEE80211_LOCK(lhw->ic);
9666b4cac81SBjoern A. Zeeb 
9678ac540d3SBjoern A. Zeeb 	if (cancel)
9686b4cac81SBjoern A. Zeeb 		ic_printf(lhw->ic, "%s: failed to cancel scan: %d (%p, %p)\n",
9696b4cac81SBjoern A. Zeeb 		    __func__, error, lhw, vif);
9706b4cac81SBjoern A. Zeeb }
9716b4cac81SBjoern A. Zeeb 
9726b4cac81SBjoern A. Zeeb static void
973086be6a8SBjoern A. Zeeb lkpi_hw_conf_idle(struct ieee80211_hw *hw, bool new)
974086be6a8SBjoern A. Zeeb {
975086be6a8SBjoern A. Zeeb 	struct lkpi_hw *lhw;
976086be6a8SBjoern A. Zeeb 	int error;
977086be6a8SBjoern A. Zeeb 	bool old;
978086be6a8SBjoern A. Zeeb 
979086be6a8SBjoern A. Zeeb 	old = hw->conf.flags & IEEE80211_CONF_IDLE;
980086be6a8SBjoern A. Zeeb 	if (old == new)
981086be6a8SBjoern A. Zeeb 		return;
982086be6a8SBjoern A. Zeeb 
983086be6a8SBjoern A. Zeeb 	hw->conf.flags ^= IEEE80211_CONF_IDLE;
984086be6a8SBjoern A. Zeeb 	error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_IDLE);
985086be6a8SBjoern A. Zeeb 	if (error != 0 && error != EOPNOTSUPP) {
986086be6a8SBjoern A. Zeeb 		lhw = HW_TO_LHW(hw);
987086be6a8SBjoern A. Zeeb 		ic_printf(lhw->ic, "ERROR: %s: config %#0x returned %d\n",
988086be6a8SBjoern A. Zeeb 		    __func__, IEEE80211_CONF_CHANGE_IDLE, error);
989086be6a8SBjoern A. Zeeb 	}
990086be6a8SBjoern A. Zeeb }
991086be6a8SBjoern A. Zeeb 
992086be6a8SBjoern A. Zeeb static void
9936b4cac81SBjoern A. Zeeb lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
9946b4cac81SBjoern A. Zeeb     struct lkpi_hw *lhw)
9956b4cac81SBjoern A. Zeeb {
9966b4cac81SBjoern A. Zeeb 	sta->aid = 0;
997616e1330SBjoern A. Zeeb 	if (vif->cfg.assoc) {
9986b4cac81SBjoern A. Zeeb 		struct ieee80211_hw *hw;
9996b4cac81SBjoern A. Zeeb 		enum ieee80211_bss_changed changed;
10006b4cac81SBjoern A. Zeeb 
10016b4cac81SBjoern A. Zeeb 		lhw->update_mc = true;
10026b4cac81SBjoern A. Zeeb 		lkpi_update_mcast_filter(lhw->ic, true);
10036b4cac81SBjoern A. Zeeb 
10046b4cac81SBjoern A. Zeeb 		changed = 0;
1005616e1330SBjoern A. Zeeb 		vif->cfg.assoc = false;
1006616e1330SBjoern A. Zeeb 		vif->cfg.aid = 0;
10076b4cac81SBjoern A. Zeeb 		changed |= BSS_CHANGED_ASSOC;
1008d9f59799SBjoern A. Zeeb 		/*
1009d9f59799SBjoern A. Zeeb 		 * This will remove the sta from firmware for iwlwifi.
1010d9f59799SBjoern A. Zeeb 		 * So confusing that they use state and flags and ... ^%$%#%$^.
1011d9f59799SBjoern A. Zeeb 		 */
10126b4cac81SBjoern A. Zeeb 		IMPROVE();
10136b4cac81SBjoern A. Zeeb 		hw = LHW_TO_HW(lhw);
10146b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf,
10156b4cac81SBjoern A. Zeeb 		    changed);
1016086be6a8SBjoern A. Zeeb 
1017086be6a8SBjoern A. Zeeb 		lkpi_hw_conf_idle(hw, true);
10186b4cac81SBjoern A. Zeeb 	}
10196b4cac81SBjoern A. Zeeb }
10206b4cac81SBjoern A. Zeeb 
10216b4cac81SBjoern A. Zeeb static void
10226b4cac81SBjoern A. Zeeb lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
10236b4cac81SBjoern A. Zeeb     bool dequeue_seen, bool no_emptyq)
10246b4cac81SBjoern A. Zeeb {
10256b4cac81SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
10266b4cac81SBjoern A. Zeeb 	int tid;
1027eac3646fSBjoern A. Zeeb 	bool ltxq_empty;
10286b4cac81SBjoern A. Zeeb 
10296b4cac81SBjoern A. Zeeb 	/* Wake up all queues to know they are allocated in the driver. */
10306b4cac81SBjoern A. Zeeb 	for (tid = 0; tid < nitems(sta->txq); tid++) {
10316b4cac81SBjoern A. Zeeb 
10326b4cac81SBjoern A. Zeeb 		if (tid == IEEE80211_NUM_TIDS) {
10336b4cac81SBjoern A. Zeeb 			IMPROVE("station specific?");
10346b4cac81SBjoern A. Zeeb 			if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ))
10356b4cac81SBjoern A. Zeeb 				continue;
10366b4cac81SBjoern A. Zeeb 		} else if (tid >= hw->queues)
10376b4cac81SBjoern A. Zeeb 			continue;
10386b4cac81SBjoern A. Zeeb 
10396b4cac81SBjoern A. Zeeb 		if (sta->txq[tid] == NULL)
10406b4cac81SBjoern A. Zeeb 			continue;
10416b4cac81SBjoern A. Zeeb 
10426b4cac81SBjoern A. Zeeb 		ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
10436b4cac81SBjoern A. Zeeb 		if (dequeue_seen && !ltxq->seen_dequeue)
10446b4cac81SBjoern A. Zeeb 			continue;
10456b4cac81SBjoern A. Zeeb 
1046eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_LOCK(ltxq);
1047eac3646fSBjoern A. Zeeb 		ltxq_empty = skb_queue_empty(&ltxq->skbq);
1048eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_UNLOCK(ltxq);
1049eac3646fSBjoern A. Zeeb 		if (no_emptyq && ltxq_empty)
10506b4cac81SBjoern A. Zeeb 			continue;
10516b4cac81SBjoern A. Zeeb 
10526b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
10536b4cac81SBjoern A. Zeeb 	}
10546b4cac81SBjoern A. Zeeb }
10556b4cac81SBjoern A. Zeeb 
10566b4cac81SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
10576b4cac81SBjoern A. Zeeb 
10586b4cac81SBjoern A. Zeeb static int
10596b4cac81SBjoern A. Zeeb lkpi_sta_state_do_nada(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
10606b4cac81SBjoern A. Zeeb {
10616b4cac81SBjoern A. Zeeb 
10626b4cac81SBjoern A. Zeeb 	return (0);
10636b4cac81SBjoern A. Zeeb }
10646b4cac81SBjoern A. Zeeb 
10656b4cac81SBjoern A. Zeeb /* lkpi_iv_newstate() handles the stop scan case generally. */
10666b4cac81SBjoern A. Zeeb #define	lkpi_sta_scan_to_init(_v, _n, _a)	lkpi_sta_state_do_nada(_v, _n, _a)
10676b4cac81SBjoern A. Zeeb 
10686b4cac81SBjoern A. Zeeb static int
10696b4cac81SBjoern A. Zeeb lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
10706b4cac81SBjoern A. Zeeb {
10716b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
1072c5e25798SBjoern A. Zeeb 	struct lkpi_chanctx *lchanctx;
10736b4cac81SBjoern A. Zeeb 	struct ieee80211_chanctx_conf *conf;
10746b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
10756b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
10766b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
10776b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
10786b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
10796b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
10806b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
10816b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
10826b4cac81SBjoern A. Zeeb 	uint32_t changed;
10836b4cac81SBjoern A. Zeeb 	int error;
10846b4cac81SBjoern A. Zeeb 
10852ac8a218SBjoern A. Zeeb 	/*
10862ac8a218SBjoern A. Zeeb 	 * In here we use vap->iv_bss until lvif->lvif_bss is set.
10872ac8a218SBjoern A. Zeeb 	 * For all later (STATE >= AUTH) functions we need to use the lvif
10882ac8a218SBjoern A. Zeeb 	 * cache which will be tracked even through (*iv_update_bss)().
10892ac8a218SBjoern A. Zeeb 	 */
10902ac8a218SBjoern A. Zeeb 
10912ac8a218SBjoern A. Zeeb 	if (vap->iv_bss == NULL) {
10922ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: no iv_bss for vap %p\n", __func__, vap);
10932ac8a218SBjoern A. Zeeb 		return (EINVAL);
10942ac8a218SBjoern A. Zeeb 	}
1095*0936c648SBjoern A. Zeeb 	/*
1096*0936c648SBjoern A. Zeeb 	 * Keep the ni alive locally.  In theory (and practice) iv_bss can change
1097*0936c648SBjoern A. Zeeb 	 * once we unlock here.  This is due to net80211 allowing state changes
1098*0936c648SBjoern A. Zeeb 	 * and new join1() despite having an active node as well as due to
1099*0936c648SBjoern A. Zeeb 	 * the fact that the iv_bss can be swapped under the hood in (*iv_update_bss).
1100*0936c648SBjoern A. Zeeb 	 */
11012ac8a218SBjoern A. Zeeb 	ni = ieee80211_ref_node(vap->iv_bss);
11022ac8a218SBjoern A. Zeeb 	if (ni->ni_chan == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC) {
11032ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: no channel set for iv_bss ni %p "
11042ac8a218SBjoern A. Zeeb 		    "on vap %p\n", __func__, ni, vap);
1105*0936c648SBjoern A. Zeeb 		ieee80211_free_node(ni);	/* Error handling for the local ni. */
11062ac8a218SBjoern A. Zeeb 		return (EINVAL);
11076b4cac81SBjoern A. Zeeb 	}
11086b4cac81SBjoern A. Zeeb 
11096b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
11102ac8a218SBjoern A. Zeeb 	chan = lkpi_find_lkpi80211_chan(lhw, ni->ni_chan);
11112ac8a218SBjoern A. Zeeb 	if (chan == NULL) {
11122ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: failed to get LKPI channel from "
11132ac8a218SBjoern A. Zeeb 		    "iv_bss ni %p on vap %p\n", __func__, ni, vap);
1114*0936c648SBjoern A. Zeeb 		ieee80211_free_node(ni);	/* Error handling for the local ni. */
11152ac8a218SBjoern A. Zeeb 		return (ESRCH);
11162ac8a218SBjoern A. Zeeb 	}
11172ac8a218SBjoern A. Zeeb 
11186b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
11196b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
11206b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
11216b4cac81SBjoern A. Zeeb 
1122*0936c648SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
1123*0936c648SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
1124*0936c648SBjoern A. Zeeb 	if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL) {
1125*0936c648SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
1126*0936c648SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
1127*0936c648SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
1128*0936c648SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
1129*0936c648SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
1130*0936c648SBjoern A. Zeeb 		return (EBUSY);
1131*0936c648SBjoern A. Zeeb 	}
1132*0936c648SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1133*0936c648SBjoern A. Zeeb 
11346b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
11358ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
11366b4cac81SBjoern A. Zeeb 
11376b4cac81SBjoern A. Zeeb 	/* Add chanctx (or if exists, change it). */
11386b4cac81SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
11396b4cac81SBjoern A. Zeeb 		conf = vif->chanctx_conf;
1140c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
11416b4cac81SBjoern A. Zeeb 		IMPROVE("diff changes for changed, working on live copy, rcu");
11426b4cac81SBjoern A. Zeeb 	} else {
11436b4cac81SBjoern A. Zeeb 		/* Keep separate alloc as in Linux this is rcu managed? */
1144c5e25798SBjoern A. Zeeb 		lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size,
11456b4cac81SBjoern A. Zeeb 		    M_LKPI80211, M_WAITOK | M_ZERO);
1146c5e25798SBjoern A. Zeeb 		conf = &lchanctx->conf;
11476b4cac81SBjoern A. Zeeb 	}
11486b4cac81SBjoern A. Zeeb 
11496b4cac81SBjoern A. Zeeb 	conf->rx_chains_dynamic = 1;
11506b4cac81SBjoern A. Zeeb 	conf->rx_chains_static = 1;
11516b4cac81SBjoern A. Zeeb 	conf->radar_enabled =
11526b4cac81SBjoern A. Zeeb 	    (chan->flags & IEEE80211_CHAN_RADAR) ? true : false;
11536b4cac81SBjoern A. Zeeb 	conf->def.chan = chan;
11546b4cac81SBjoern A. Zeeb 	conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
11556b4cac81SBjoern A. Zeeb 	conf->def.center_freq1 = chan->center_freq;
11566b4cac81SBjoern A. Zeeb 	conf->def.center_freq2 = 0;
11579fb91463SBjoern A. Zeeb 	IMPROVE("Check vht_cap from band not just chan?");
11582ac8a218SBjoern A. Zeeb 	KASSERT(ni->ni_chan != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC,
11592ac8a218SBjoern A. Zeeb 	   ("%s:%d: ni %p ni_chan %p\n", __func__, __LINE__, ni, ni->ni_chan));
11609fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
11619fb91463SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
11629fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
11639fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_40;
11649fb91463SBjoern A. Zeeb 		} else
11659fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_20;
11669fb91463SBjoern A. Zeeb 	}
11679fb91463SBjoern A. Zeeb #endif
11689fb91463SBjoern A. Zeeb #ifdef LKPI_80211_VHT
11699fb91463SBjoern A. Zeeb 	if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
11709fb91463SBjoern A. Zeeb #ifdef __notyet__
11719fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
11729fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_80P80;
11739fb91463SBjoern A. Zeeb 			conf->def.center_freq2 = 0;	/* XXX */
11749fb91463SBjoern A. Zeeb 		} else
11759fb91463SBjoern A. Zeeb #endif
11769fb91463SBjoern A. Zeeb 		if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
11779fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_160;
11789fb91463SBjoern A. Zeeb 		else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
11799fb91463SBjoern A. Zeeb 			conf->def.width = NL80211_CHAN_WIDTH_80;
11809fb91463SBjoern A. Zeeb 	}
11819fb91463SBjoern A. Zeeb #endif
11826b4cac81SBjoern A. Zeeb 	/* Responder ... */
11836b4cac81SBjoern A. Zeeb 	conf->min_def.chan = chan;
11846b4cac81SBjoern A. Zeeb 	conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT;
11856b4cac81SBjoern A. Zeeb 	conf->min_def.center_freq1 = chan->center_freq;
11866b4cac81SBjoern A. Zeeb 	conf->min_def.center_freq2 = 0;
11879fb91463SBjoern A. Zeeb 	IMPROVE("currently 20_NOHT min_def only");
11886b4cac81SBjoern A. Zeeb 
11896ffb7bd4SBjoern A. Zeeb 	/* Set bss info (bss_info_changed). */
11906ffb7bd4SBjoern A. Zeeb 	bss_changed = 0;
11916ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.bssid = ni->ni_bssid;
11926ffb7bd4SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_BSSID;
11936ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.txpower = ni->ni_txpower;
11946ffb7bd4SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_TXPOWER;
11956ffb7bd4SBjoern A. Zeeb 	vif->cfg.idle = false;
11966ffb7bd4SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_IDLE;
11976ffb7bd4SBjoern A. Zeeb 
11986ffb7bd4SBjoern A. Zeeb 	/* vif->bss_conf.basic_rates ? Where exactly? */
11996ffb7bd4SBjoern A. Zeeb 
12006ffb7bd4SBjoern A. Zeeb 	/* Should almost assert it is this. */
12016ffb7bd4SBjoern A. Zeeb 	vif->cfg.assoc = false;
12026ffb7bd4SBjoern A. Zeeb 	vif->cfg.aid = 0;
12036ffb7bd4SBjoern A. Zeeb 
12046ffb7bd4SBjoern A. Zeeb 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
12056ffb7bd4SBjoern A. Zeeb 
12066b4cac81SBjoern A. Zeeb 	error = 0;
12076b4cac81SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
12086b4cac81SBjoern A. Zeeb 		changed = IEEE80211_CHANCTX_CHANGE_MIN_WIDTH;
12096b4cac81SBjoern A. Zeeb 		changed |= IEEE80211_CHANCTX_CHANGE_RADAR;
12106b4cac81SBjoern A. Zeeb 		changed |= IEEE80211_CHANCTX_CHANGE_RX_CHAINS;
12116b4cac81SBjoern A. Zeeb 		changed |= IEEE80211_CHANCTX_CHANGE_WIDTH;
12126b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_change_chanctx(hw, conf, changed);
12136b4cac81SBjoern A. Zeeb 	} else {
12146b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_add_chanctx(hw, conf);
12156b4cac81SBjoern A. Zeeb 		if (error == 0 || error == EOPNOTSUPP) {
12166b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.chan = conf->def.chan;
12176b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.width = conf->def.width;
12186b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.center_freq1 =
12196b4cac81SBjoern A. Zeeb 			    conf->def.center_freq1;
12209fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
12219fb91463SBjoern A. Zeeb 			if (vif->bss_conf.chandef.width == NL80211_CHAN_WIDTH_40) {
12229fb91463SBjoern A. Zeeb 				/* Note: it is 10 not 20. */
12239fb91463SBjoern A. Zeeb 				if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
12249fb91463SBjoern A. Zeeb 					vif->bss_conf.chandef.center_freq1 += 10;
12259fb91463SBjoern A. Zeeb 				else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
12269fb91463SBjoern A. Zeeb 					vif->bss_conf.chandef.center_freq1 -= 10;
12279fb91463SBjoern A. Zeeb 			}
12289fb91463SBjoern A. Zeeb #endif
12296b4cac81SBjoern A. Zeeb 			vif->bss_conf.chandef.center_freq2 =
12306b4cac81SBjoern A. Zeeb 			    conf->def.center_freq2;
12316b4cac81SBjoern A. Zeeb 		} else {
1232018d93ecSBjoern A. Zeeb 			ic_printf(vap->iv_ic, "%s:%d: mo_add_chanctx "
1233018d93ecSBjoern A. Zeeb 			    "failed: %d\n", __func__, __LINE__, error);
12346b4cac81SBjoern A. Zeeb 			goto out;
12356b4cac81SBjoern A. Zeeb 		}
12366ffb7bd4SBjoern A. Zeeb 
12376ffb7bd4SBjoern A. Zeeb 		vif->bss_conf.chanctx_conf = conf;
12386ffb7bd4SBjoern A. Zeeb 
12396b4cac81SBjoern A. Zeeb 		/* Assign vif chanctx. */
12406b4cac81SBjoern A. Zeeb 		if (error == 0)
124168541546SBjoern A. Zeeb 			error = lkpi_80211_mo_assign_vif_chanctx(hw, vif,
124268541546SBjoern A. Zeeb 			    &vif->bss_conf, conf);
12436b4cac81SBjoern A. Zeeb 		if (error == EOPNOTSUPP)
12446b4cac81SBjoern A. Zeeb 			error = 0;
12456b4cac81SBjoern A. Zeeb 		if (error != 0) {
1246018d93ecSBjoern A. Zeeb 			ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx "
1247018d93ecSBjoern A. Zeeb 			    "failed: %d\n", __func__, __LINE__, error);
12486b4cac81SBjoern A. Zeeb 			lkpi_80211_mo_remove_chanctx(hw, conf);
1249c5e25798SBjoern A. Zeeb 			lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
1250c5e25798SBjoern A. Zeeb 			free(lchanctx, M_LKPI80211);
12516b4cac81SBjoern A. Zeeb 			goto out;
12526b4cac81SBjoern A. Zeeb 		}
12536b4cac81SBjoern A. Zeeb 	}
12546b4cac81SBjoern A. Zeeb 	IMPROVE("update radiotap chan fields too");
12556b4cac81SBjoern A. Zeeb 
12566b4cac81SBjoern A. Zeeb 	/* RATES */
12576b4cac81SBjoern A. Zeeb 	IMPROVE("bss info: not all needs to come now and rates are missing");
12586b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
12596b4cac81SBjoern A. Zeeb 
1260d9f59799SBjoern A. Zeeb 	/*
1261*0936c648SBjoern A. Zeeb 	 * Given ni and lsta are 1:1 from alloc to free we can assert that
1262*0936c648SBjoern A. Zeeb 	 * ni always has lsta data attach despite net80211 node swapping
1263*0936c648SBjoern A. Zeeb 	 * under the hoods.
1264d9f59799SBjoern A. Zeeb 	 */
1265*0936c648SBjoern A. Zeeb 	KASSERT(ni->ni_drv_data != NULL, ("%s: ni %p ni_drv_data %p\n",
1266*0936c648SBjoern A. Zeeb 	    __func__, ni, ni->ni_drv_data));
12676b4cac81SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
1268e24e8103SBjoern A. Zeeb 
1269e24e8103SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
1270*0936c648SBjoern A. Zeeb 	/* Re-check given (*iv_update_bss) could have happened. */
1271*0936c648SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? or deal as error? */
12722ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL)
12732ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
12742ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d, ni %p lsta %p\n", __func__, __LINE__,
12752ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
12762ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
12772ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched, ni, lsta);
12782ac8a218SBjoern A. Zeeb 
1279*0936c648SBjoern A. Zeeb 	/*
1280*0936c648SBjoern A. Zeeb 	 * Reference the ni for this cache of lsta/ni on lvif->lvif_bss
1281*0936c648SBjoern A. Zeeb 	 * essentially out lsta version of the iv_bss.
1282*0936c648SBjoern A. Zeeb 	 * Do NOT use iv_bss here anymore as that may have diverged from our
1283*0936c648SBjoern A. Zeeb 	 * function local ni already and would lead to inconsistencies.
1284*0936c648SBjoern A. Zeeb 	 */
1285*0936c648SBjoern A. Zeeb 	ieee80211_ref_node(ni);
12862ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = lsta;
12872ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = true;
12882ac8a218SBjoern A. Zeeb 
12892ac8a218SBjoern A. Zeeb 	/* Insert the [l]sta into the list of known stations. */
1290e24e8103SBjoern A. Zeeb 	TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry);
1291e24e8103SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1292e24e8103SBjoern A. Zeeb 
1293d9f59799SBjoern A. Zeeb 	/* Add (or adjust) sta and change state (from NOTEXIST) to NONE. */
12946b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
12956b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NOTEXIST, ("%s: lsta %p state not "
12966b4cac81SBjoern A. Zeeb 	    "NOTEXIST: %#x\n", __func__, lsta, lsta->state));
1297e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
12986b4cac81SBjoern A. Zeeb 	if (error != 0) {
12996b4cac81SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
1300018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
1301018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
13026b4cac81SBjoern A. Zeeb 		goto out;
13036b4cac81SBjoern A. Zeeb 	}
13046b4cac81SBjoern A. Zeeb #if 0
13056b4cac81SBjoern A. Zeeb 	lsta->added_to_drv = true;	/* mo manages. */
13066b4cac81SBjoern A. Zeeb #endif
13076b4cac81SBjoern A. Zeeb 
13089d9ba2b7SBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
13099d9ba2b7SBjoern A. Zeeb 
13106b4cac81SBjoern A. Zeeb 	/*
13116b4cac81SBjoern A. Zeeb 	 * Wakeup all queues now that sta is there so we have as much time to
13126b4cac81SBjoern A. Zeeb 	 * possibly prepare the queue in the driver to be ready for the 1st
13136b4cac81SBjoern A. Zeeb 	 * packet;  lkpi_80211_txq_tx_one() still has a workaround as there
13146b4cac81SBjoern A. Zeeb 	 * is no guarantee or way to check.
1315d9f59799SBjoern A. Zeeb 	 * XXX-BZ and by now we know that this does not work on all drivers
1316d9f59799SBjoern A. Zeeb 	 * for all queues.
13176b4cac81SBjoern A. Zeeb 	 */
1318e7fe0373SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, false);
13196b4cac81SBjoern A. Zeeb 
13206b4cac81SBjoern A. Zeeb 	/* Start mgd_prepare_tx. */
13216b4cac81SBjoern A. Zeeb 	memset(&prep_tx_info, 0, sizeof(prep_tx_info));
13226b4cac81SBjoern A. Zeeb 	prep_tx_info.duration = PREP_TX_INFO_DURATION;
13236b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
13246b4cac81SBjoern A. Zeeb 	lsta->in_mgd = true;
13256b4cac81SBjoern A. Zeeb 
13266b4cac81SBjoern A. Zeeb 	/*
13276b4cac81SBjoern A. Zeeb 	 * What is going to happen next:
13286b4cac81SBjoern A. Zeeb 	 * - <twiddle> .. we should end up in "auth_to_assoc"
13296b4cac81SBjoern A. Zeeb 	 * - event_callback
13306b4cac81SBjoern A. Zeeb 	 * - update sta_state (NONE to AUTH)
13316b4cac81SBjoern A. Zeeb 	 * - mgd_complete_tx
13326b4cac81SBjoern A. Zeeb 	 * (ideally we'd do that on a callback for something else ...)
13336b4cac81SBjoern A. Zeeb 	 */
13346b4cac81SBjoern A. Zeeb 
13356b4cac81SBjoern A. Zeeb out:
13368ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
13376b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
1338*0936c648SBjoern A. Zeeb 	/*
1339*0936c648SBjoern A. Zeeb 	 * Release the reference that keop the ni stable locally
1340*0936c648SBjoern A. Zeeb 	 * during the work of this function.
1341*0936c648SBjoern A. Zeeb 	 */
13426b4cac81SBjoern A. Zeeb 	if (ni != NULL)
13436b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
13446b4cac81SBjoern A. Zeeb 	return (error);
13456b4cac81SBjoern A. Zeeb }
13466b4cac81SBjoern A. Zeeb 
13476b4cac81SBjoern A. Zeeb static int
13486b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
13496b4cac81SBjoern A. Zeeb {
13506b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
13516b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
13526b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
13536b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
13546b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
13556b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
13566b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
13576b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
13586b4cac81SBjoern A. Zeeb 	int error;
13596b4cac81SBjoern A. Zeeb 
13606b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
13616b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
13626b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
13636b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
13646b4cac81SBjoern A. Zeeb 
13652ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
13662ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
13672ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
13682ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
13692ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
13702ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
13712ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
13722ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
13732ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
13742ac8a218SBjoern A. Zeeb #endif
13752ac8a218SBjoern A. Zeeb 
13762ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
13772ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
13782ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
13792ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
13802ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
13812ac8a218SBjoern A. Zeeb 	ni = lsta->ni;			/* Reference held for lvif_bss. */
13826b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
13836b4cac81SBjoern A. Zeeb 
138467674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
13856b4cac81SBjoern A. Zeeb 
13866b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
13878ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
13886b4cac81SBjoern A. Zeeb 
1389d9f59799SBjoern A. Zeeb 	/* flush, drop. */
1390d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
1391d9f59799SBjoern A. Zeeb 
13926b4cac81SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
13936b4cac81SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
13946b4cac81SBjoern A. Zeeb 
13956b4cac81SBjoern A. Zeeb 	/* flush, no drop */
13966b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
13976b4cac81SBjoern A. Zeeb 
13986b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
13996b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
14006b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
14016b4cac81SBjoern A. Zeeb 		prep_tx_info.success = false;
14026b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
14036b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
14046b4cac81SBjoern A. Zeeb 	}
14056b4cac81SBjoern A. Zeeb 
14066b4cac81SBjoern A. Zeeb 	/* sync_rx_queues */
14076b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
14086b4cac81SBjoern A. Zeeb 
14096b4cac81SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
14106b4cac81SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
1411d9f59799SBjoern A. Zeeb 
1412d9f59799SBjoern A. Zeeb 	/* Take the station down. */
14136b4cac81SBjoern A. Zeeb 
14146b4cac81SBjoern A. Zeeb 	/* Adjust sta and change state (from NONE) to NOTEXIST. */
14156b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
14166b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
1417d9f59799SBjoern A. Zeeb 	    "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
1418e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
14196b4cac81SBjoern A. Zeeb 	if (error != 0) {
14206b4cac81SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
1421018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
1422018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
14236b4cac81SBjoern A. Zeeb 		goto out;
14246b4cac81SBjoern A. Zeeb 	}
14256b4cac81SBjoern A. Zeeb #if 0
14266b4cac81SBjoern A. Zeeb 	lsta->added_to_drv = false;	/* mo manages. */
14276b4cac81SBjoern A. Zeeb #endif
14286b4cac81SBjoern A. Zeeb 
142967674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
14306b4cac81SBjoern A. Zeeb 
14312ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
14322ac8a218SBjoern A. Zeeb 	/* Remove ni reference for this cache of lsta. */
14332ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
14342ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
14352ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1436d9f59799SBjoern A. Zeeb 	lkpi_lsta_remove(lsta, lvif);
1437*0936c648SBjoern A. Zeeb 	/*
1438*0936c648SBjoern A. Zeeb 	 * The very last release the reference on the ni for the ni/lsta on
1439*0936c648SBjoern A. Zeeb 	 * lvif->lvif_bss.  Upon return from this both ni and lsta are invalid
1440*0936c648SBjoern A. Zeeb 	 * and potentially freed.
1441*0936c648SBjoern A. Zeeb 	 */
1442*0936c648SBjoern A. Zeeb 	ieee80211_free_node(ni);
1443d9f59799SBjoern A. Zeeb 
1444d9f59799SBjoern A. Zeeb 	/* conf_tx */
1445d9f59799SBjoern A. Zeeb 
1446d9f59799SBjoern A. Zeeb 	/* Take the chan ctx down. */
14476b4cac81SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
1448c5e25798SBjoern A. Zeeb 		struct lkpi_chanctx *lchanctx;
14496b4cac81SBjoern A. Zeeb 		struct ieee80211_chanctx_conf *conf;
14506b4cac81SBjoern A. Zeeb 
14516b4cac81SBjoern A. Zeeb 		conf = vif->chanctx_conf;
14526b4cac81SBjoern A. Zeeb 		/* Remove vif context. */
145368541546SBjoern A. Zeeb 		lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
14546b4cac81SBjoern A. Zeeb 		/* NB: vif->chanctx_conf is NULL now. */
14556b4cac81SBjoern A. Zeeb 
14566b4cac81SBjoern A. Zeeb 		/* Remove chan ctx. */
14576b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_remove_chanctx(hw, conf);
1458c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
1459c5e25798SBjoern A. Zeeb 		free(lchanctx, M_LKPI80211);
14606b4cac81SBjoern A. Zeeb 	}
14616b4cac81SBjoern A. Zeeb 
14626b4cac81SBjoern A. Zeeb out:
14638ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
14646b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
14656b4cac81SBjoern A. Zeeb 	return (error);
14666b4cac81SBjoern A. Zeeb }
14676b4cac81SBjoern A. Zeeb 
14686b4cac81SBjoern A. Zeeb static int
14696b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
14706b4cac81SBjoern A. Zeeb {
14716b4cac81SBjoern A. Zeeb 	int error;
14726b4cac81SBjoern A. Zeeb 
14736b4cac81SBjoern A. Zeeb 	error = lkpi_sta_auth_to_scan(vap, nstate, arg);
14746b4cac81SBjoern A. Zeeb 	if (error == 0)
14756b4cac81SBjoern A. Zeeb 		error = lkpi_sta_scan_to_init(vap, nstate, arg);
14766b4cac81SBjoern A. Zeeb 	return (error);
14776b4cac81SBjoern A. Zeeb }
14786b4cac81SBjoern A. Zeeb 
14796b4cac81SBjoern A. Zeeb static int
14806b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
14816b4cac81SBjoern A. Zeeb {
14826b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
14836b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
14846b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
14856b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
14866b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
14876b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
14886b4cac81SBjoern A. Zeeb 	int error;
14896b4cac81SBjoern A. Zeeb 
14906b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
14916b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
14926b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
14936b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
14946b4cac81SBjoern A. Zeeb 
14956b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
14968ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
14972ac8a218SBjoern A. Zeeb 
14982ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
14992ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
15002ac8a218SBjoern A. Zeeb 	if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
15012ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
15022ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
15032ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
15042ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
15052ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
15062ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
15072ac8a218SBjoern A. Zeeb #endif
15082ac8a218SBjoern A. Zeeb 		error = ENOTRECOVERABLE;
15092ac8a218SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
15102ac8a218SBjoern A. Zeeb 		goto out;
15112ac8a218SBjoern A. Zeeb 	}
15122ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
15132ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
15142ac8a218SBjoern A. Zeeb 
15152ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: lsta %p\n", __func__, lsta));
15166b4cac81SBjoern A. Zeeb 
15176b4cac81SBjoern A. Zeeb 	/* Finish auth. */
15186b4cac81SBjoern A. Zeeb 	IMPROVE("event callback");
15196b4cac81SBjoern A. Zeeb 
15206b4cac81SBjoern A. Zeeb 	/* Update sta_state (NONE to AUTH). */
15216b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
15226b4cac81SBjoern A. Zeeb 	    "NONE: %#x\n", __func__, lsta, lsta->state));
1523e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
1524018d93ecSBjoern A. Zeeb 	if (error != 0) {
1525018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
1526018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
15276b4cac81SBjoern A. Zeeb 		goto out;
1528018d93ecSBjoern A. Zeeb 	}
15296b4cac81SBjoern A. Zeeb 
15306b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
15316b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
15326b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
15336b4cac81SBjoern A. Zeeb 		prep_tx_info.success = true;
15346b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
15356b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
15366b4cac81SBjoern A. Zeeb 	}
15376b4cac81SBjoern A. Zeeb 
15386b4cac81SBjoern A. Zeeb 	/* Now start assoc. */
15396b4cac81SBjoern A. Zeeb 
15406b4cac81SBjoern A. Zeeb 	/* Start mgd_prepare_tx. */
15416b4cac81SBjoern A. Zeeb 	if (!lsta->in_mgd) {
15426b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
15436b4cac81SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
15446b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
15456b4cac81SBjoern A. Zeeb 		lsta->in_mgd = true;
15466b4cac81SBjoern A. Zeeb 	}
15476b4cac81SBjoern A. Zeeb 
15486b4cac81SBjoern A. Zeeb 	/* Wake tx queue to get packet out. */
1549e7fe0373SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), true, true);
15506b4cac81SBjoern A. Zeeb 
15516b4cac81SBjoern A. Zeeb 	/*
15526b4cac81SBjoern A. Zeeb 	 * <twiddle> .. we end up in "assoc_to_run"
15536b4cac81SBjoern A. Zeeb 	 * - update sta_state (AUTH to ASSOC)
15546b4cac81SBjoern A. Zeeb 	 * - conf_tx [all]
15556b4cac81SBjoern A. Zeeb 	 * - bss_info_changed (assoc, aid, ssid, ..)
15566b4cac81SBjoern A. Zeeb 	 * - change_chanctx (if needed)
15576b4cac81SBjoern A. Zeeb 	 * - event_callback
15586b4cac81SBjoern A. Zeeb 	 * - mgd_complete_tx
15596b4cac81SBjoern A. Zeeb 	 */
15606b4cac81SBjoern A. Zeeb 
15616b4cac81SBjoern A. Zeeb out:
15628ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
15636b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
15646b4cac81SBjoern A. Zeeb 	return (error);
15656b4cac81SBjoern A. Zeeb }
15666b4cac81SBjoern A. Zeeb 
15676b4cac81SBjoern A. Zeeb /* auth_to_auth, assoc_to_assoc. */
15686b4cac81SBjoern A. Zeeb static int
15696b4cac81SBjoern A. Zeeb lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
15706b4cac81SBjoern A. Zeeb {
15716b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
15726b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
15736b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
15746b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
15756b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
15766b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
15772ac8a218SBjoern A. Zeeb 	int error;
15786b4cac81SBjoern A. Zeeb 
15796b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
15806b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
15816b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
15826b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
15836b4cac81SBjoern A. Zeeb 
15846b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
15858ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
15862ac8a218SBjoern A. Zeeb 
15872ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
15882ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
15892ac8a218SBjoern A. Zeeb 	if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
15902ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
15912ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
15922ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
15932ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
15942ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
15952ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
15962ac8a218SBjoern A. Zeeb #endif
15972ac8a218SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
15982ac8a218SBjoern A. Zeeb 		error = ENOTRECOVERABLE;
15992ac8a218SBjoern A. Zeeb 		goto out;
16002ac8a218SBjoern A. Zeeb 	}
16012ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
16022ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
16032ac8a218SBjoern A. Zeeb 
16042ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
16052ac8a218SBjoern A. Zeeb 	    lsta, lvif, vap));
16066b4cac81SBjoern A. Zeeb 
16076b4cac81SBjoern A. Zeeb 	IMPROVE("event callback?");
16086b4cac81SBjoern A. Zeeb 
16096b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
16106b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
16116b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
16126b4cac81SBjoern A. Zeeb 		prep_tx_info.success = false;
16136b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
16146b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
16156b4cac81SBjoern A. Zeeb 	}
16166b4cac81SBjoern A. Zeeb 
16176b4cac81SBjoern A. Zeeb 	/* Now start assoc. */
16186b4cac81SBjoern A. Zeeb 
16196b4cac81SBjoern A. Zeeb 	/* Start mgd_prepare_tx. */
16206b4cac81SBjoern A. Zeeb 	if (!lsta->in_mgd) {
16216b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
16226b4cac81SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
16236b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
16246b4cac81SBjoern A. Zeeb 		lsta->in_mgd = true;
16256b4cac81SBjoern A. Zeeb 	}
16266b4cac81SBjoern A. Zeeb 
16272ac8a218SBjoern A. Zeeb 	error = 0;
16282ac8a218SBjoern A. Zeeb out:
16298ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
16306b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
16316b4cac81SBjoern A. Zeeb 
16322ac8a218SBjoern A. Zeeb 	return (error);
16336b4cac81SBjoern A. Zeeb }
16346b4cac81SBjoern A. Zeeb 
16356b4cac81SBjoern A. Zeeb static int
1636d9f59799SBjoern A. Zeeb _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
16376b4cac81SBjoern A. Zeeb {
16386b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
16396b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
16406b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
16416b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
16426b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
16436b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
16446b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
16456b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
1646d9f59799SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
16476b4cac81SBjoern A. Zeeb 	int error;
16486b4cac81SBjoern A. Zeeb 
16496b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
16506b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
16516b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
16526b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
16536b4cac81SBjoern A. Zeeb 
16542ac8a218SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
16552ac8a218SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
16562ac8a218SBjoern A. Zeeb 
16572ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
16582ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
16592ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
16602ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
16612ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
16622ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
16632ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
16642ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
16652ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
16662ac8a218SBjoern A. Zeeb #endif
16672ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
16682ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
16692ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
16702ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
16712ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
16722ac8a218SBjoern A. Zeeb 
16732ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
16746b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
16756b4cac81SBjoern A. Zeeb 
167667674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
1677d9f59799SBjoern A. Zeeb 
1678d9f59799SBjoern A. Zeeb 	/* flush, drop. */
1679d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
1680d9f59799SBjoern A. Zeeb 
1681d9f59799SBjoern A. Zeeb 	IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
1682d9f59799SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
1683d9f59799SBjoern A. Zeeb 	    !lsta->in_mgd) {
1684d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
1685d9f59799SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
1686d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
1687d9f59799SBjoern A. Zeeb 		lsta->in_mgd = true;
1688d9f59799SBjoern A. Zeeb 	}
1689d9f59799SBjoern A. Zeeb 
16908ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
1691d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
1692d9f59799SBjoern A. Zeeb 
1693d9f59799SBjoern A. Zeeb 	/* Call iv_newstate first so we get potential DISASSOC packet out. */
1694d9f59799SBjoern A. Zeeb 	error = lvif->iv_newstate(vap, nstate, arg);
1695018d93ecSBjoern A. Zeeb 	if (error != 0) {
1696018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
1697018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
1698d9f59799SBjoern A. Zeeb 		goto outni;
1699018d93ecSBjoern A. Zeeb 	}
1700d9f59799SBjoern A. Zeeb 
1701d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
17028ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
1703d9f59799SBjoern A. Zeeb 
170467674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
1705d9f59799SBjoern A. Zeeb 
1706d9f59799SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
1707d9f59799SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
1708d9f59799SBjoern A. Zeeb 
1709d9f59799SBjoern A. Zeeb 	/* flush, no drop */
1710d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
1711d9f59799SBjoern A. Zeeb 
17126b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
17136b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
17146b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
17156b4cac81SBjoern A. Zeeb 		prep_tx_info.success = false;
17166b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
17176b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
17186b4cac81SBjoern A. Zeeb 	}
17196b4cac81SBjoern A. Zeeb 
1720d9f59799SBjoern A. Zeeb 	/* sync_rx_queues */
1721d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
1722d9f59799SBjoern A. Zeeb 
1723d9f59799SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
1724d9f59799SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
1725d9f59799SBjoern A. Zeeb 
1726d9f59799SBjoern A. Zeeb 	/* Take the station down. */
1727d9f59799SBjoern A. Zeeb 
17286b4cac81SBjoern A. Zeeb 	/* Update sta and change state (from AUTH) to NONE. */
17296b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
17306b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
17316b4cac81SBjoern A. Zeeb 	    "AUTH: %#x\n", __func__, lsta, lsta->state));
1732e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
1733018d93ecSBjoern A. Zeeb 	if (error != 0) {
1734018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
1735018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
17366b4cac81SBjoern A. Zeeb 		goto out;
1737018d93ecSBjoern A. Zeeb 	}
17386b4cac81SBjoern A. Zeeb 
173967674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
17406b4cac81SBjoern A. Zeeb 
174116e688b2SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
174216e688b2SBjoern A. Zeeb 	/*
174316e688b2SBjoern A. Zeeb 	 * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST
174416e688b2SBjoern A. Zeeb 	 * as otherwise drivers (iwlwifi at least) will silently not remove
174516e688b2SBjoern A. Zeeb 	 * the sta from the firmware and when we will add a new one trigger
174616e688b2SBjoern A. Zeeb 	 * a fw assert.
174716e688b2SBjoern A. Zeeb 	 */
174816e688b2SBjoern A. Zeeb 	lkpi_disassoc(sta, vif, lhw);
174916e688b2SBjoern A. Zeeb 
1750d9f59799SBjoern A. Zeeb 	/* Adjust sta and change state (from NONE) to NOTEXIST. */
1751d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
1752d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
1753d9f59799SBjoern A. Zeeb 	    "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
1754e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
1755d9f59799SBjoern A. Zeeb 	if (error != 0) {
1756d9f59799SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
1757018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
1758018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
1759d9f59799SBjoern A. Zeeb 		goto out;
1760d9f59799SBjoern A. Zeeb 	}
1761d9f59799SBjoern A. Zeeb 
176216e688b2SBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);	/* sta no longer save to use. */
1763d9f59799SBjoern A. Zeeb 
1764d9f59799SBjoern A. Zeeb 	IMPROVE("Any bss_info changes to announce?");
1765d9f59799SBjoern A. Zeeb 	bss_changed = 0;
1766d9f59799SBjoern A. Zeeb 	vif->bss_conf.qos = 0;
1767d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_QOS;
1768616e1330SBjoern A. Zeeb 	vif->cfg.ssid_len = 0;
1769616e1330SBjoern A. Zeeb 	memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
1770d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_BSSID;
1771d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
1772d9f59799SBjoern A. Zeeb 
17732ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
17742ac8a218SBjoern A. Zeeb 	/* Remove ni reference for this cache of lsta. */
17752ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
17762ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
17772ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
1778d9f59799SBjoern A. Zeeb 	lkpi_lsta_remove(lsta, lvif);
1779*0936c648SBjoern A. Zeeb 	/*
1780*0936c648SBjoern A. Zeeb 	 * The very last release the reference on the ni for the ni/lsta on
1781*0936c648SBjoern A. Zeeb 	 * lvif->lvif_bss.  Upon return from this both ni and lsta are invalid
1782*0936c648SBjoern A. Zeeb 	 * and potentially freed.
1783*0936c648SBjoern A. Zeeb 	 */
1784*0936c648SBjoern A. Zeeb 	ieee80211_free_node(ni);
1785d9f59799SBjoern A. Zeeb 
1786d9f59799SBjoern A. Zeeb 	/* conf_tx */
1787d9f59799SBjoern A. Zeeb 
1788d9f59799SBjoern A. Zeeb 	/* Take the chan ctx down. */
1789d9f59799SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
1790c5e25798SBjoern A. Zeeb 		struct lkpi_chanctx *lchanctx;
1791d9f59799SBjoern A. Zeeb 		struct ieee80211_chanctx_conf *conf;
1792d9f59799SBjoern A. Zeeb 
1793d9f59799SBjoern A. Zeeb 		conf = vif->chanctx_conf;
1794d9f59799SBjoern A. Zeeb 		/* Remove vif context. */
179568541546SBjoern A. Zeeb 		lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
1796d9f59799SBjoern A. Zeeb 		/* NB: vif->chanctx_conf is NULL now. */
1797d9f59799SBjoern A. Zeeb 
1798d9f59799SBjoern A. Zeeb 		/* Remove chan ctx. */
1799d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_remove_chanctx(hw, conf);
1800c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
1801c5e25798SBjoern A. Zeeb 		free(lchanctx, M_LKPI80211);
1802d9f59799SBjoern A. Zeeb 	}
1803d9f59799SBjoern A. Zeeb 
1804d9f59799SBjoern A. Zeeb 	error = EALREADY;
18056b4cac81SBjoern A. Zeeb out:
18068ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
18076b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
1808d9f59799SBjoern A. Zeeb outni:
18096b4cac81SBjoern A. Zeeb 	return (error);
18106b4cac81SBjoern A. Zeeb }
18116b4cac81SBjoern A. Zeeb 
18126b4cac81SBjoern A. Zeeb static int
1813d9f59799SBjoern A. Zeeb lkpi_sta_assoc_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
1814d9f59799SBjoern A. Zeeb {
1815d9f59799SBjoern A. Zeeb 	int error;
1816d9f59799SBjoern A. Zeeb 
1817d9f59799SBjoern A. Zeeb 	error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
1818d9f59799SBjoern A. Zeeb 	if (error != 0 && error != EALREADY)
1819d9f59799SBjoern A. Zeeb 		return (error);
1820d9f59799SBjoern A. Zeeb 
1821d9f59799SBjoern A. Zeeb 	/* At this point iv_bss is long a new node! */
1822d9f59799SBjoern A. Zeeb 
1823d9f59799SBjoern A. Zeeb 	error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
1824d9f59799SBjoern A. Zeeb 	return (error);
1825d9f59799SBjoern A. Zeeb }
1826d9f59799SBjoern A. Zeeb 
1827d9f59799SBjoern A. Zeeb static int
18286b4cac81SBjoern A. Zeeb lkpi_sta_assoc_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18296b4cac81SBjoern A. Zeeb {
18306b4cac81SBjoern A. Zeeb 	int error;
18316b4cac81SBjoern A. Zeeb 
1832d9f59799SBjoern A. Zeeb 	error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
18336b4cac81SBjoern A. Zeeb 	return (error);
18346b4cac81SBjoern A. Zeeb }
18356b4cac81SBjoern A. Zeeb 
18366b4cac81SBjoern A. Zeeb static int
18376b4cac81SBjoern A. Zeeb lkpi_sta_assoc_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18386b4cac81SBjoern A. Zeeb {
18396b4cac81SBjoern A. Zeeb 	int error;
18406b4cac81SBjoern A. Zeeb 
1841d9f59799SBjoern A. Zeeb 	error = _lkpi_sta_assoc_to_down(vap, nstate, arg);
18426b4cac81SBjoern A. Zeeb 	return (error);
18436b4cac81SBjoern A. Zeeb }
18446b4cac81SBjoern A. Zeeb 
18456b4cac81SBjoern A. Zeeb static int
18466b4cac81SBjoern A. Zeeb lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
18476b4cac81SBjoern A. Zeeb {
18486b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
18496b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
18506b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
18516b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
18526b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
18536b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
18546b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
18556b4cac81SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
18566b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
18576b4cac81SBjoern A. Zeeb 	int error;
18586b4cac81SBjoern A. Zeeb 
18596b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
18606b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
18616b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
18626b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
18636b4cac81SBjoern A. Zeeb 
18646b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
18658ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
18662ac8a218SBjoern A. Zeeb 
18672ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
18682ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later? */
18692ac8a218SBjoern A. Zeeb 	if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
18702ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
18712ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
18722ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
18732ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
18742ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
18752ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
18762ac8a218SBjoern A. Zeeb #endif
18772ac8a218SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
18782ac8a218SBjoern A. Zeeb 		error = ENOTRECOVERABLE;
18792ac8a218SBjoern A. Zeeb 		goto out;
18802ac8a218SBjoern A. Zeeb 	}
18812ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
18822ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
18832ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
18842ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
18852ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
18862ac8a218SBjoern A. Zeeb 
18872ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
18886b4cac81SBjoern A. Zeeb 
18896b4cac81SBjoern A. Zeeb 	IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, "
18906b4cac81SBjoern A. Zeeb 	    "and to lesser extend ieee80211_notify_node_join");
18916b4cac81SBjoern A. Zeeb 
18929597f7cbSBjoern A. Zeeb 	/* Finish assoc. */
18939597f7cbSBjoern A. Zeeb 	/* Update sta_state (AUTH to ASSOC) and set aid. */
18949597f7cbSBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
18959597f7cbSBjoern A. Zeeb 	    "AUTH: %#x\n", __func__, lsta, lsta->state));
18966b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
18979597f7cbSBjoern A. Zeeb 	sta->aid = IEEE80211_NODE_AID(ni);
18984a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
18994a67f1dfSBjoern A. Zeeb 	if (vap->iv_flags & IEEE80211_F_WME)
19004a67f1dfSBjoern A. Zeeb 		sta->wme = true;
19014a67f1dfSBjoern A. Zeeb #endif
1902e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
1903018d93ecSBjoern A. Zeeb 	if (error != 0) {
1904018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
1905018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
19069597f7cbSBjoern A. Zeeb 		goto out;
1907018d93ecSBjoern A. Zeeb 	}
19086b4cac81SBjoern A. Zeeb 
19096b4cac81SBjoern A. Zeeb 	IMPROVE("wme / conf_tx [all]");
19106b4cac81SBjoern A. Zeeb 
19116b4cac81SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
19126b4cac81SBjoern A. Zeeb 	bss_changed = 0;
19134a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
19144a67f1dfSBjoern A. Zeeb 	bss_changed |= lkpi_wme_update(lhw, vap, true);
19154a67f1dfSBjoern A. Zeeb #endif
1916616e1330SBjoern A. Zeeb 	if (!vif->cfg.assoc || vif->cfg.aid != IEEE80211_NODE_AID(ni)) {
1917616e1330SBjoern A. Zeeb 		vif->cfg.assoc = true;
1918616e1330SBjoern A. Zeeb 		vif->cfg.aid = IEEE80211_NODE_AID(ni);
19196b4cac81SBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_ASSOC;
19206b4cac81SBjoern A. Zeeb 	}
19216b4cac81SBjoern A. Zeeb 	/* We set SSID but this is not BSSID! */
1922616e1330SBjoern A. Zeeb 	vif->cfg.ssid_len = ni->ni_esslen;
1923616e1330SBjoern A. Zeeb 	memcpy(vif->cfg.ssid, ni->ni_essid, ni->ni_esslen);
19246b4cac81SBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) !=
19256b4cac81SBjoern A. Zeeb 	    vif->bss_conf.use_short_preamble) {
19266b4cac81SBjoern A. Zeeb 		vif->bss_conf.use_short_preamble ^= 1;
19276b4cac81SBjoern A. Zeeb 		/* bss_changed |= BSS_CHANGED_??? */
19286b4cac81SBjoern A. Zeeb 	}
19296b4cac81SBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_SHSLOT) !=
19306b4cac81SBjoern A. Zeeb 	    vif->bss_conf.use_short_slot) {
19316b4cac81SBjoern A. Zeeb 		vif->bss_conf.use_short_slot ^= 1;
19326b4cac81SBjoern A. Zeeb 		/* bss_changed |= BSS_CHANGED_??? */
19336b4cac81SBjoern A. Zeeb 	}
19346b4cac81SBjoern A. Zeeb 	if ((ni->ni_flags & IEEE80211_NODE_QOS) !=
19356b4cac81SBjoern A. Zeeb 	    vif->bss_conf.qos) {
19366b4cac81SBjoern A. Zeeb 		vif->bss_conf.qos ^= 1;
19376b4cac81SBjoern A. Zeeb 		bss_changed |= BSS_CHANGED_QOS;
19386b4cac81SBjoern A. Zeeb 	}
1939fa8f007dSBjoern A. Zeeb 
1940fa8f007dSBjoern A. Zeeb 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
1941fa8f007dSBjoern A. Zeeb 
19426b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
19436b4cac81SBjoern A. Zeeb 
19446b4cac81SBjoern A. Zeeb 	/* - change_chanctx (if needed)
19456b4cac81SBjoern A. Zeeb 	 * - event_callback
19466b4cac81SBjoern A. Zeeb 	 */
19476b4cac81SBjoern A. Zeeb 
19486b4cac81SBjoern A. Zeeb 	/* End mgd_complete_tx. */
19496b4cac81SBjoern A. Zeeb 	if (lsta->in_mgd) {
19506b4cac81SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
19516b4cac81SBjoern A. Zeeb 		prep_tx_info.success = true;
19526b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
19536b4cac81SBjoern A. Zeeb 		lsta->in_mgd = false;
19546b4cac81SBjoern A. Zeeb 	}
19556b4cac81SBjoern A. Zeeb 
1956086be6a8SBjoern A. Zeeb 	lkpi_hw_conf_idle(hw, false);
1957086be6a8SBjoern A. Zeeb 
19586b4cac81SBjoern A. Zeeb 	/*
19596b4cac81SBjoern A. Zeeb 	 * And then:
19606b4cac81SBjoern A. Zeeb 	 * - (more packets)?
19616b4cac81SBjoern A. Zeeb 	 * - set_key
19626b4cac81SBjoern A. Zeeb 	 * - set_default_unicast_key
19636b4cac81SBjoern A. Zeeb 	 * - set_key (?)
19646b4cac81SBjoern A. Zeeb 	 * - ipv6_addr_change (?)
19656b4cac81SBjoern A. Zeeb 	 */
19666b4cac81SBjoern A. Zeeb 	/* Prepare_multicast && configure_filter. */
19676b4cac81SBjoern A. Zeeb 	lhw->update_mc = true;
19686b4cac81SBjoern A. Zeeb 	lkpi_update_mcast_filter(vap->iv_ic, true);
19696b4cac81SBjoern A. Zeeb 
19706b4cac81SBjoern A. Zeeb 	if (!ieee80211_node_is_authorized(ni)) {
19716b4cac81SBjoern A. Zeeb 		IMPROVE("net80211 does not consider node authorized");
19726b4cac81SBjoern A. Zeeb 	}
19736b4cac81SBjoern A. Zeeb 
19749fb91463SBjoern A. Zeeb #if defined(LKPI_80211_HT)
19759fb91463SBjoern A. Zeeb 	IMPROVE("Is this the right spot, has net80211 done all updates already?");
19769fb91463SBjoern A. Zeeb 	lkpi_sta_sync_ht_from_ni(sta, ni, NULL);
19779fb91463SBjoern A. Zeeb #endif
19789fb91463SBjoern A. Zeeb 
19796b4cac81SBjoern A. Zeeb 	/* Update sta_state (ASSOC to AUTHORIZED). */
19806b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
19816b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
19826b4cac81SBjoern A. Zeeb 	    "ASSOC: %#x\n", __func__, lsta, lsta->state));
1983e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTHORIZED);
19846b4cac81SBjoern A. Zeeb 	if (error != 0) {
19856b4cac81SBjoern A. Zeeb 		IMPROVE("undo some changes?");
1986018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTHORIZED) "
1987018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
19886b4cac81SBjoern A. Zeeb 		goto out;
19896b4cac81SBjoern A. Zeeb 	}
19906b4cac81SBjoern A. Zeeb 
19916b4cac81SBjoern A. Zeeb 	/* - drv_config (?)
19926b4cac81SBjoern A. Zeeb 	 * - bss_info_changed
19936b4cac81SBjoern A. Zeeb 	 * - set_rekey_data (?)
19946b4cac81SBjoern A. Zeeb 	 *
19956b4cac81SBjoern A. Zeeb 	 * And now we should be passing packets.
19966b4cac81SBjoern A. Zeeb 	 */
19976b4cac81SBjoern A. Zeeb 	IMPROVE("Need that bssid setting, and the keys");
19986b4cac81SBjoern A. Zeeb 
1999fa8f007dSBjoern A. Zeeb 	bss_changed = 0;
2000fa8f007dSBjoern A. Zeeb 	bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
2001fa8f007dSBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
2002fa8f007dSBjoern A. Zeeb 
20036b4cac81SBjoern A. Zeeb out:
20048ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
20056b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
20066b4cac81SBjoern A. Zeeb 	return (error);
20076b4cac81SBjoern A. Zeeb }
20086b4cac81SBjoern A. Zeeb 
20096b4cac81SBjoern A. Zeeb static int
20106b4cac81SBjoern A. Zeeb lkpi_sta_auth_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
20116b4cac81SBjoern A. Zeeb {
20126b4cac81SBjoern A. Zeeb 	int error;
20136b4cac81SBjoern A. Zeeb 
20146b4cac81SBjoern A. Zeeb 	error = lkpi_sta_auth_to_assoc(vap, nstate, arg);
20156b4cac81SBjoern A. Zeeb 	if (error == 0)
20166b4cac81SBjoern A. Zeeb 		error = lkpi_sta_assoc_to_run(vap, nstate, arg);
20176b4cac81SBjoern A. Zeeb 	return (error);
20186b4cac81SBjoern A. Zeeb }
20196b4cac81SBjoern A. Zeeb 
20206b4cac81SBjoern A. Zeeb static int
20216b4cac81SBjoern A. Zeeb lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
20226b4cac81SBjoern A. Zeeb {
20236b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
20246b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
20256b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
20266b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
20276b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
20286b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
20296b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
2030d9f59799SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
2031d9f59799SBjoern A. Zeeb #if 0
2032d9f59799SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
2033d9f59799SBjoern A. Zeeb #endif
20346b4cac81SBjoern A. Zeeb 	int error;
20356b4cac81SBjoern A. Zeeb 
20366b4cac81SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
20376b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
20386b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
20396b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
20406b4cac81SBjoern A. Zeeb 
20412ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
20422ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
20432ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
20442ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
20452ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
20462ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
20472ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
20482ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
20492ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
20502ac8a218SBjoern A. Zeeb #endif
20512ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
20522ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
20532ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
20542ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
20552ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
20562ac8a218SBjoern A. Zeeb 
20572ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
20586b4cac81SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
20596b4cac81SBjoern A. Zeeb 
206067674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2061d9f59799SBjoern A. Zeeb 
2062d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
20638ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2064d9f59799SBjoern A. Zeeb 
2065d9f59799SBjoern A. Zeeb 	/* flush, drop. */
2066d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
2067d9f59799SBjoern A. Zeeb 
2068d9f59799SBjoern A. Zeeb 	IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
2069d9f59799SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
2070d9f59799SBjoern A. Zeeb 	    !lsta->in_mgd) {
2071d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2072d9f59799SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
2073d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
2074d9f59799SBjoern A. Zeeb 		lsta->in_mgd = true;
2075d9f59799SBjoern A. Zeeb 	}
2076d9f59799SBjoern A. Zeeb 
20778ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2078d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2079d9f59799SBjoern A. Zeeb 
2080d9f59799SBjoern A. Zeeb 	/* Call iv_newstate first so we get potential DISASSOC packet out. */
2081d9f59799SBjoern A. Zeeb 	error = lvif->iv_newstate(vap, nstate, arg);
2082018d93ecSBjoern A. Zeeb 	if (error != 0) {
2083018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
2084018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
2085d9f59799SBjoern A. Zeeb 		goto outni;
2086018d93ecSBjoern A. Zeeb 	}
2087d9f59799SBjoern A. Zeeb 
2088d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
20898ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2090d9f59799SBjoern A. Zeeb 
209167674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2092d9f59799SBjoern A. Zeeb 
2093d9f59799SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
2094d9f59799SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
2095d9f59799SBjoern A. Zeeb 
2096d9f59799SBjoern A. Zeeb 	/* flush, no drop */
2097d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
2098d9f59799SBjoern A. Zeeb 
2099d9f59799SBjoern A. Zeeb 	/* End mgd_complete_tx. */
2100d9f59799SBjoern A. Zeeb 	if (lsta->in_mgd) {
2101d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2102d9f59799SBjoern A. Zeeb 		prep_tx_info.success = false;
2103d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
2104d9f59799SBjoern A. Zeeb 		lsta->in_mgd = false;
2105d9f59799SBjoern A. Zeeb 	}
2106d9f59799SBjoern A. Zeeb 
2107d9f59799SBjoern A. Zeeb #if 0
2108d9f59799SBjoern A. Zeeb 	/* sync_rx_queues */
2109d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
2110d9f59799SBjoern A. Zeeb 
2111d9f59799SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
2112d9f59799SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
2113d9f59799SBjoern A. Zeeb #endif
2114d9f59799SBjoern A. Zeeb 
2115d9f59799SBjoern A. Zeeb 	/* Take the station down. */
2116d9f59799SBjoern A. Zeeb 
21176b4cac81SBjoern A. Zeeb 	/* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
21186b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
21196b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
21206b4cac81SBjoern A. Zeeb 	    "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
2121e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
2122018d93ecSBjoern A. Zeeb 	if (error != 0) {
2123018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
2124018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
21256b4cac81SBjoern A. Zeeb 		goto out;
2126018d93ecSBjoern A. Zeeb 	}
21276b4cac81SBjoern A. Zeeb 
212867674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
21296b4cac81SBjoern A. Zeeb 
21306b4cac81SBjoern A. Zeeb 	/* Update sta_state (ASSOC to AUTH). */
21316b4cac81SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
21326b4cac81SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
21336b4cac81SBjoern A. Zeeb 	    "ASSOC: %#x\n", __func__, lsta, lsta->state));
2134e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
2135018d93ecSBjoern A. Zeeb 	if (error != 0) {
2136018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
2137018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
21386b4cac81SBjoern A. Zeeb 		goto out;
2139018d93ecSBjoern A. Zeeb 	}
21406b4cac81SBjoern A. Zeeb 
214167674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
21426b4cac81SBjoern A. Zeeb 
2143d9f59799SBjoern A. Zeeb #if 0
2144d9f59799SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
2145d9f59799SBjoern A. Zeeb 	lkpi_disassoc(sta, vif, lhw);
2146d9f59799SBjoern A. Zeeb #endif
2147d9f59799SBjoern A. Zeeb 
2148d9f59799SBjoern A. Zeeb 	error = EALREADY;
21496b4cac81SBjoern A. Zeeb out:
21508ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
21516b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2152d9f59799SBjoern A. Zeeb outni:
21536b4cac81SBjoern A. Zeeb 	return (error);
21546b4cac81SBjoern A. Zeeb }
21556b4cac81SBjoern A. Zeeb 
21566b4cac81SBjoern A. Zeeb static int
2157d9f59799SBjoern A. Zeeb lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2158d9f59799SBjoern A. Zeeb {
2159d9f59799SBjoern A. Zeeb 	struct lkpi_hw *lhw;
2160d9f59799SBjoern A. Zeeb 	struct ieee80211_hw *hw;
2161d9f59799SBjoern A. Zeeb 	struct lkpi_vif *lvif;
2162d9f59799SBjoern A. Zeeb 	struct ieee80211_vif *vif;
2163d9f59799SBjoern A. Zeeb 	struct ieee80211_node *ni;
2164d9f59799SBjoern A. Zeeb 	struct lkpi_sta *lsta;
2165d9f59799SBjoern A. Zeeb 	struct ieee80211_sta *sta;
2166d9f59799SBjoern A. Zeeb 	struct ieee80211_prep_tx_info prep_tx_info;
2167d9f59799SBjoern A. Zeeb 	enum ieee80211_bss_changed bss_changed;
2168d9f59799SBjoern A. Zeeb 	int error;
2169d9f59799SBjoern A. Zeeb 
2170d9f59799SBjoern A. Zeeb 	lhw = vap->iv_ic->ic_softc;
2171d9f59799SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
2172d9f59799SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
2173d9f59799SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
2174d9f59799SBjoern A. Zeeb 
21752ac8a218SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
21762ac8a218SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
21772ac8a218SBjoern A. Zeeb 
21782ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
21792ac8a218SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
21802ac8a218SBjoern A. Zeeb 	/* XXX-BZ KASSERT later; state going down so no action. */
21812ac8a218SBjoern A. Zeeb 	if (lvif->lvif_bss == NULL)
21822ac8a218SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
21832ac8a218SBjoern A. Zeeb 		    "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
21842ac8a218SBjoern A. Zeeb 		    lvif, vap, vap->iv_bss, lvif->lvif_bss,
21852ac8a218SBjoern A. Zeeb 		    (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
21862ac8a218SBjoern A. Zeeb 		    lvif->lvif_bss_synched);
21872ac8a218SBjoern A. Zeeb #endif
21882ac8a218SBjoern A. Zeeb 	lsta = lvif->lvif_bss;
21892ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
21902ac8a218SBjoern A. Zeeb 	KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
21912ac8a218SBjoern A. Zeeb 	    "lvif %p vap %p\n", __func__,
21922ac8a218SBjoern A. Zeeb 	    lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
21932ac8a218SBjoern A. Zeeb 
21942ac8a218SBjoern A. Zeeb 	ni = lsta->ni;		/* Reference held for lvif_bss. */
2195d9f59799SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
2196d9f59799SBjoern A. Zeeb 
219767674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2198d9f59799SBjoern A. Zeeb 
2199d9f59799SBjoern A. Zeeb 	/* flush, drop. */
2200d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), true);
2201d9f59799SBjoern A. Zeeb 
2202d9f59799SBjoern A. Zeeb 	IMPROVE("What are the proper conditions for DEAUTH_NEED_MGD_TX_PREP?");
2203d9f59799SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, DEAUTH_NEED_MGD_TX_PREP) &&
2204d9f59799SBjoern A. Zeeb 	    !lsta->in_mgd) {
2205d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2206d9f59799SBjoern A. Zeeb 		prep_tx_info.duration = PREP_TX_INFO_DURATION;
2207d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_prepare_tx(hw, vif, &prep_tx_info);
2208d9f59799SBjoern A. Zeeb 		lsta->in_mgd = true;
2209d9f59799SBjoern A. Zeeb 	}
2210d9f59799SBjoern A. Zeeb 
22118ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2212d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2213d9f59799SBjoern A. Zeeb 
2214d9f59799SBjoern A. Zeeb 	/* Call iv_newstate first so we get potential DISASSOC packet out. */
2215d9f59799SBjoern A. Zeeb 	error = lvif->iv_newstate(vap, nstate, arg);
2216018d93ecSBjoern A. Zeeb 	if (error != 0) {
2217018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
2218018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
2219d9f59799SBjoern A. Zeeb 		goto outni;
2220018d93ecSBjoern A. Zeeb 	}
2221d9f59799SBjoern A. Zeeb 
2222d9f59799SBjoern A. Zeeb 	IEEE80211_UNLOCK(vap->iv_ic);
22238ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2224d9f59799SBjoern A. Zeeb 
222567674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2226d9f59799SBjoern A. Zeeb 
2227d9f59799SBjoern A. Zeeb 	/* Wake tx queues to get packet(s) out. */
2228d9f59799SBjoern A. Zeeb 	lkpi_wake_tx_queues(hw, sta, true, true);
2229d9f59799SBjoern A. Zeeb 
2230d9f59799SBjoern A. Zeeb 	/* flush, no drop */
2231d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_flush(hw, vif,  nitems(sta->txq), false);
2232d9f59799SBjoern A. Zeeb 
2233d9f59799SBjoern A. Zeeb 	/* End mgd_complete_tx. */
2234d9f59799SBjoern A. Zeeb 	if (lsta->in_mgd) {
2235d9f59799SBjoern A. Zeeb 		memset(&prep_tx_info, 0, sizeof(prep_tx_info));
2236d9f59799SBjoern A. Zeeb 		prep_tx_info.success = false;
2237d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_mgd_complete_tx(hw, vif, &prep_tx_info);
2238d9f59799SBjoern A. Zeeb 		lsta->in_mgd = false;
2239d9f59799SBjoern A. Zeeb 	}
2240d9f59799SBjoern A. Zeeb 
2241d9f59799SBjoern A. Zeeb 	/* sync_rx_queues */
2242d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_sync_rx_queues(hw);
2243d9f59799SBjoern A. Zeeb 
2244d9f59799SBjoern A. Zeeb 	/* sta_pre_rcu_remove */
2245d9f59799SBjoern A. Zeeb         lkpi_80211_mo_sta_pre_rcu_remove(hw, vif, sta);
2246d9f59799SBjoern A. Zeeb 
2247d9f59799SBjoern A. Zeeb 	/* Take the station down. */
2248d9f59799SBjoern A. Zeeb 
2249d9f59799SBjoern A. Zeeb 	/* Adjust sta and change state (from AUTHORIZED) to ASSOC. */
2250d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2251d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
2252d9f59799SBjoern A. Zeeb 	    "AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
2253e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
2254018d93ecSBjoern A. Zeeb 	if (error != 0) {
2255018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
2256018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2257d9f59799SBjoern A. Zeeb 		goto out;
2258018d93ecSBjoern A. Zeeb 	}
2259d9f59799SBjoern A. Zeeb 
226067674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2261d9f59799SBjoern A. Zeeb 
2262d9f59799SBjoern A. Zeeb 	/* Update sta_state (ASSOC to AUTH). */
2263d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2264d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
2265d9f59799SBjoern A. Zeeb 	    "ASSOC: %#x\n", __func__, lsta, lsta->state));
2266e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
2267018d93ecSBjoern A. Zeeb 	if (error != 0) {
2268018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
2269018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2270d9f59799SBjoern A. Zeeb 		goto out;
2271018d93ecSBjoern A. Zeeb 	}
2272d9f59799SBjoern A. Zeeb 
227367674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2274d9f59799SBjoern A. Zeeb 
2275d9f59799SBjoern A. Zeeb 	/* Update sta and change state (from AUTH) to NONE. */
2276d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2277d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
2278d9f59799SBjoern A. Zeeb 	    "AUTH: %#x\n", __func__, lsta, lsta->state));
2279e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
2280018d93ecSBjoern A. Zeeb 	if (error != 0) {
2281018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
2282018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2283d9f59799SBjoern A. Zeeb 		goto out;
2284018d93ecSBjoern A. Zeeb 	}
2285d9f59799SBjoern A. Zeeb 
228667674c1cSBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
2287d9f59799SBjoern A. Zeeb 
228816e688b2SBjoern A. Zeeb 	/* Update bss info (bss_info_changed) (assoc, aid, ..). */
228916e688b2SBjoern A. Zeeb 	/*
229016e688b2SBjoern A. Zeeb 	 * One would expect this to happen when going off AUTHORIZED.
229116e688b2SBjoern A. Zeeb 	 * See comment there; removes the sta from fw.
229216e688b2SBjoern A. Zeeb 	 */
229316e688b2SBjoern A. Zeeb 	lkpi_disassoc(sta, vif, lhw);
229416e688b2SBjoern A. Zeeb 
2295d9f59799SBjoern A. Zeeb 	/* Adjust sta and change state (from NONE) to NOTEXIST. */
2296d9f59799SBjoern A. Zeeb 	KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
2297d9f59799SBjoern A. Zeeb 	KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
2298d9f59799SBjoern A. Zeeb 	    "NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
2299e7fe0373SBjoern A. Zeeb 	error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
2300d9f59799SBjoern A. Zeeb 	if (error != 0) {
2301d9f59799SBjoern A. Zeeb 		IMPROVE("do we need to undo the chan ctx?");
2302018d93ecSBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
2303018d93ecSBjoern A. Zeeb 		    "failed: %d\n", __func__, __LINE__, error);
2304d9f59799SBjoern A. Zeeb 		goto out;
2305d9f59799SBjoern A. Zeeb 	}
2306d9f59799SBjoern A. Zeeb 
230716e688b2SBjoern A. Zeeb 	lkpi_lsta_dump(lsta, ni, __func__, __LINE__);	/* sta no longer save to use. */
2308d9f59799SBjoern A. Zeeb 
2309d9f59799SBjoern A. Zeeb 	IMPROVE("Any bss_info changes to announce?");
2310d9f59799SBjoern A. Zeeb 	bss_changed = 0;
2311d9f59799SBjoern A. Zeeb 	vif->bss_conf.qos = 0;
2312d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_QOS;
2313616e1330SBjoern A. Zeeb 	vif->cfg.ssid_len = 0;
2314616e1330SBjoern A. Zeeb 	memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
2315d9f59799SBjoern A. Zeeb 	bss_changed |= BSS_CHANGED_BSSID;
2316d9f59799SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
2317d9f59799SBjoern A. Zeeb 
23182ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
23192ac8a218SBjoern A. Zeeb 	/* Remove ni reference for this cache of lsta. */
23202ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
23212ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
23222ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
2323d9f59799SBjoern A. Zeeb 	lkpi_lsta_remove(lsta, lvif);
2324*0936c648SBjoern A. Zeeb 	/*
2325*0936c648SBjoern A. Zeeb 	 * The very last release the reference on the ni for the ni/lsta on
2326*0936c648SBjoern A. Zeeb 	 * lvif->lvif_bss.  Upon return from this both ni and lsta are invalid
2327*0936c648SBjoern A. Zeeb 	 * and potentially freed.
2328*0936c648SBjoern A. Zeeb 	 */
2329*0936c648SBjoern A. Zeeb 	ieee80211_free_node(ni);
2330d9f59799SBjoern A. Zeeb 
2331d9f59799SBjoern A. Zeeb 	/* conf_tx */
2332d9f59799SBjoern A. Zeeb 
2333d9f59799SBjoern A. Zeeb 	/* Take the chan ctx down. */
2334d9f59799SBjoern A. Zeeb 	if (vif->chanctx_conf != NULL) {
2335c5e25798SBjoern A. Zeeb 		struct lkpi_chanctx *lchanctx;
2336d9f59799SBjoern A. Zeeb 		struct ieee80211_chanctx_conf *conf;
2337d9f59799SBjoern A. Zeeb 
2338d9f59799SBjoern A. Zeeb 		conf = vif->chanctx_conf;
2339d9f59799SBjoern A. Zeeb 		/* Remove vif context. */
234068541546SBjoern A. Zeeb 		lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
2341d9f59799SBjoern A. Zeeb 		/* NB: vif->chanctx_conf is NULL now. */
2342d9f59799SBjoern A. Zeeb 
2343d9f59799SBjoern A. Zeeb 		/* Remove chan ctx. */
2344d9f59799SBjoern A. Zeeb 		lkpi_80211_mo_remove_chanctx(hw, conf);
2345c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
2346c5e25798SBjoern A. Zeeb 		free(lchanctx, M_LKPI80211);
2347d9f59799SBjoern A. Zeeb 	}
2348d9f59799SBjoern A. Zeeb 
2349d9f59799SBjoern A. Zeeb 	error = EALREADY;
2350d9f59799SBjoern A. Zeeb out:
23518ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2352d9f59799SBjoern A. Zeeb 	IEEE80211_LOCK(vap->iv_ic);
2353d9f59799SBjoern A. Zeeb outni:
2354d9f59799SBjoern A. Zeeb 	return (error);
2355d9f59799SBjoern A. Zeeb }
2356d9f59799SBjoern A. Zeeb 
2357d9f59799SBjoern A. Zeeb static int
2358d9f59799SBjoern A. Zeeb lkpi_sta_run_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
2359d9f59799SBjoern A. Zeeb {
2360d9f59799SBjoern A. Zeeb 
2361d9f59799SBjoern A. Zeeb 	return (lkpi_sta_run_to_init(vap, nstate, arg));
2362d9f59799SBjoern A. Zeeb }
2363d9f59799SBjoern A. Zeeb 
2364d9f59799SBjoern A. Zeeb static int
23656b4cac81SBjoern A. Zeeb lkpi_sta_run_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
23666b4cac81SBjoern A. Zeeb {
23676b4cac81SBjoern A. Zeeb 	int error;
23686b4cac81SBjoern A. Zeeb 
2369d9f59799SBjoern A. Zeeb 	error = lkpi_sta_run_to_init(vap, nstate, arg);
2370d9f59799SBjoern A. Zeeb 	if (error != 0 && error != EALREADY)
2371d9f59799SBjoern A. Zeeb 		return (error);
2372d9f59799SBjoern A. Zeeb 
2373d9f59799SBjoern A. Zeeb 	/* At this point iv_bss is long a new node! */
2374d9f59799SBjoern A. Zeeb 
2375d9f59799SBjoern A. Zeeb 	error |= lkpi_sta_scan_to_auth(vap, nstate, 0);
23766b4cac81SBjoern A. Zeeb 	return (error);
23776b4cac81SBjoern A. Zeeb }
23786b4cac81SBjoern A. Zeeb 
2379d9f59799SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
23806b4cac81SBjoern A. Zeeb 
23816b4cac81SBjoern A. Zeeb /*
23826b4cac81SBjoern A. Zeeb  * The matches the documented state changes in net80211::sta_newstate().
23836b4cac81SBjoern A. Zeeb  * XXX (1) without CSA and SLEEP yet, * XXX (2) not all unhandled cases
23846b4cac81SBjoern A. Zeeb  * there are "invalid" (so there is a room for failure here).
23856b4cac81SBjoern A. Zeeb  */
23866b4cac81SBjoern A. Zeeb struct fsm_state {
23876b4cac81SBjoern A. Zeeb 	/* INIT, SCAN, AUTH, ASSOC, CAC, RUN, CSA, SLEEP */
23886b4cac81SBjoern A. Zeeb 	enum ieee80211_state ostate;
23896b4cac81SBjoern A. Zeeb 	enum ieee80211_state nstate;
23906b4cac81SBjoern A. Zeeb 	int (*handler)(struct ieee80211vap *, enum ieee80211_state, int);
23916b4cac81SBjoern A. Zeeb } sta_state_fsm[] = {
23926b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_INIT, lkpi_sta_state_do_nada },
23936b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_SCAN,	IEEE80211_S_INIT, lkpi_sta_state_do_nada },	/* scan_to_init */
23946b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_INIT, lkpi_sta_auth_to_init },	/* not explicitly in sta_newstate() */
2395d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_INIT, lkpi_sta_assoc_to_init },	/* Send DEAUTH. */
2396d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_INIT, lkpi_sta_run_to_init },	/* Send DISASSOC. */
23976b4cac81SBjoern A. Zeeb 
23986b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
23996b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_SCAN,	IEEE80211_S_SCAN, lkpi_sta_state_do_nada },
24006b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_SCAN, lkpi_sta_auth_to_scan },
24016b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_SCAN, lkpi_sta_assoc_to_scan },
2402d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_SCAN, lkpi_sta_run_to_scan },	/* Beacon miss. */
24036b4cac81SBjoern A. Zeeb 
2404d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },	/* Send AUTH. */
2405d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_SCAN,	IEEE80211_S_AUTH, lkpi_sta_scan_to_auth },	/* Send AUTH. */
2406d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_AUTH, lkpi_sta_a_to_a },		/* Send ?AUTH. */
2407d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_AUTH, lkpi_sta_assoc_to_auth },	/* Send ?AUTH. */
2408d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_AUTH, lkpi_sta_run_to_auth },	/* Send ?AUTH. */
24096b4cac81SBjoern A. Zeeb 
2410d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_ASSOC, lkpi_sta_auth_to_assoc },	/* Send ASSOCREQ. */
2411d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_ASSOC, lkpi_sta_a_to_a },		/* Send ASSOCREQ. */
2412d9f59799SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_ASSOC, lkpi_sta_run_to_assoc },	/* Send ASSOCREQ/REASSOCREQ. */
24136b4cac81SBjoern A. Zeeb 
24146b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_AUTH,	IEEE80211_S_RUN, lkpi_sta_auth_to_run },
24156b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_ASSOC,	IEEE80211_S_RUN, lkpi_sta_assoc_to_run },
24166b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_RUN,	IEEE80211_S_RUN, lkpi_sta_state_do_nada },
24176b4cac81SBjoern A. Zeeb 
24186b4cac81SBjoern A. Zeeb 	/* Dummy at the end without handler. */
24196b4cac81SBjoern A. Zeeb 	{ IEEE80211_S_INIT,	IEEE80211_S_INIT, NULL },
24206b4cac81SBjoern A. Zeeb };
24216b4cac81SBjoern A. Zeeb 
24226b4cac81SBjoern A. Zeeb static int
24236b4cac81SBjoern A. Zeeb lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
24246b4cac81SBjoern A. Zeeb {
24256b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
24266b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
24276b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
24286b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
24296b4cac81SBjoern A. Zeeb 	struct fsm_state *s;
24306b4cac81SBjoern A. Zeeb 	enum ieee80211_state ostate;
24316b4cac81SBjoern A. Zeeb 	int error;
24326b4cac81SBjoern A. Zeeb 
24336b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
24346b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK_ASSERT(ic);
24356b4cac81SBjoern A. Zeeb 	ostate = vap->iv_state;
24366b4cac81SBjoern A. Zeeb 
24379d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
24389d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
24396b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x\n",
24406b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, vap, nstate, arg);
24419d9ba2b7SBjoern A. Zeeb #endif
24426b4cac81SBjoern A. Zeeb 
24436b4cac81SBjoern A. Zeeb 	if (vap->iv_opmode == IEEE80211_M_STA) {
24446b4cac81SBjoern A. Zeeb 
24456b4cac81SBjoern A. Zeeb 		lhw = ic->ic_softc;
24466b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
24476b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
24486b4cac81SBjoern A. Zeeb 
24496b4cac81SBjoern A. Zeeb 		/* No need to replicate this in most state handlers. */
24506b4cac81SBjoern A. Zeeb 		if (ostate == IEEE80211_S_SCAN && nstate != IEEE80211_S_SCAN)
24516b4cac81SBjoern A. Zeeb 			lkpi_stop_hw_scan(lhw, vif);
24526b4cac81SBjoern A. Zeeb 
24536b4cac81SBjoern A. Zeeb 		s = sta_state_fsm;
24546b4cac81SBjoern A. Zeeb 
24556b4cac81SBjoern A. Zeeb 	} else {
24566b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: only station mode currently supported: "
24576b4cac81SBjoern A. Zeeb 		    "cap %p iv_opmode %d\n", __func__, vap, vap->iv_opmode);
24586b4cac81SBjoern A. Zeeb 		return (ENOSYS);
24596b4cac81SBjoern A. Zeeb 	}
24606b4cac81SBjoern A. Zeeb 
24616b4cac81SBjoern A. Zeeb 	error = 0;
24626b4cac81SBjoern A. Zeeb 	for (; s->handler != NULL; s++) {
24636b4cac81SBjoern A. Zeeb 		if (ostate == s->ostate && nstate == s->nstate) {
24649d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
24659d9ba2b7SBjoern A. Zeeb 			if (linuxkpi_debug_80211 & D80211_TRACE)
2466d9f59799SBjoern A. Zeeb 				ic_printf(vap->iv_ic, "%s: new state %d (%s) ->"
2467d9f59799SBjoern A. Zeeb 				    " %d (%s): arg %d.\n", __func__,
2468d9f59799SBjoern A. Zeeb 				    ostate, ieee80211_state_name[ostate],
2469d9f59799SBjoern A. Zeeb 				    nstate, ieee80211_state_name[nstate], arg);
24709d9ba2b7SBjoern A. Zeeb #endif
24716b4cac81SBjoern A. Zeeb 			error = s->handler(vap, nstate, arg);
24726b4cac81SBjoern A. Zeeb 			break;
24736b4cac81SBjoern A. Zeeb 		}
24746b4cac81SBjoern A. Zeeb 	}
24756b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
24766b4cac81SBjoern A. Zeeb 
24776b4cac81SBjoern A. Zeeb 	if (s->handler == NULL) {
2478d9f59799SBjoern A. Zeeb 		IMPROVE("turn this into a KASSERT\n");
24796b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: unsupported state transition "
24806b4cac81SBjoern A. Zeeb 		    "%d (%s) -> %d (%s)\n", __func__,
24816b4cac81SBjoern A. Zeeb 		    ostate, ieee80211_state_name[ostate],
24826b4cac81SBjoern A. Zeeb 		    nstate, ieee80211_state_name[nstate]);
24836b4cac81SBjoern A. Zeeb 		return (ENOSYS);
24846b4cac81SBjoern A. Zeeb 	}
24856b4cac81SBjoern A. Zeeb 
24866b4cac81SBjoern A. Zeeb 	if (error == EALREADY) {
24879d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
24889d9ba2b7SBjoern A. Zeeb 		if (linuxkpi_debug_80211 & D80211_TRACE)
2489d9f59799SBjoern A. Zeeb 			ic_printf(vap->iv_ic, "%s: state transition %d (%s) -> "
2490d9f59799SBjoern A. Zeeb 			    "%d (%s): iv_newstate already handled: %d.\n",
2491d9f59799SBjoern A. Zeeb 			    __func__, ostate, ieee80211_state_name[ostate],
2492d9f59799SBjoern A. Zeeb 			    nstate, ieee80211_state_name[nstate], error);
24939d9ba2b7SBjoern A. Zeeb #endif
24946b4cac81SBjoern A. Zeeb 		return (0);
24956b4cac81SBjoern A. Zeeb 	}
24966b4cac81SBjoern A. Zeeb 
24976b4cac81SBjoern A. Zeeb 	if (error != 0) {
24986b4cac81SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s: error %d during state transition "
24996b4cac81SBjoern A. Zeeb 		    "%d (%s) -> %d (%s)\n", __func__, error,
25006b4cac81SBjoern A. Zeeb 		    ostate, ieee80211_state_name[ostate],
25016b4cac81SBjoern A. Zeeb 		    nstate, ieee80211_state_name[nstate]);
250245c27ad5SBjoern A. Zeeb 		return (error);
25036b4cac81SBjoern A. Zeeb 	}
25046b4cac81SBjoern A. Zeeb 
25059d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
25069d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE)
2507d9f59799SBjoern A. Zeeb 		ic_printf(vap->iv_ic, "%s:%d: vap %p nstate %#x arg %#x "
2508d9f59799SBjoern A. Zeeb 		    "calling net80211 parent\n",
25096b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, vap, nstate, arg);
25109d9ba2b7SBjoern A. Zeeb #endif
25116b4cac81SBjoern A. Zeeb 
25126b4cac81SBjoern A. Zeeb 	return (lvif->iv_newstate(vap, nstate, arg));
25136b4cac81SBjoern A. Zeeb }
25146b4cac81SBjoern A. Zeeb 
25156b4cac81SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
25166b4cac81SBjoern A. Zeeb 
2517d9f59799SBjoern A. Zeeb /*
2518d9f59799SBjoern A. Zeeb  * We overload (*iv_update_bss) as otherwise we have cases in, e.g.,
2519d9f59799SBjoern A. Zeeb  * net80211::ieee80211_sta_join1() where vap->iv_bss gets replaced by a
2520d9f59799SBjoern A. Zeeb  * new node without us knowing and thus our ni/lsta are out of sync.
2521d9f59799SBjoern A. Zeeb  */
2522d9f59799SBjoern A. Zeeb static struct ieee80211_node *
2523d9f59799SBjoern A. Zeeb lkpi_iv_update_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
2524d9f59799SBjoern A. Zeeb {
2525d9f59799SBjoern A. Zeeb 	struct lkpi_vif *lvif;
25262ac8a218SBjoern A. Zeeb 	struct ieee80211_node *rni;
2527d9f59799SBjoern A. Zeeb 
25282ac8a218SBjoern A. Zeeb 	IEEE80211_LOCK_ASSERT(vap->iv_ic);
2529d9f59799SBjoern A. Zeeb 
2530ed3ef56bSBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
25312ac8a218SBjoern A. Zeeb 
25322ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
25332ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
25342ac8a218SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
25352ac8a218SBjoern A. Zeeb 
25362ac8a218SBjoern A. Zeeb 	rni = lvif->iv_update_bss(vap, ni);
25372ac8a218SBjoern A. Zeeb 	return (rni);
2538d9f59799SBjoern A. Zeeb }
2539d9f59799SBjoern A. Zeeb 
25404a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
25416b4cac81SBjoern A. Zeeb static int
25424a67f1dfSBjoern A. Zeeb lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
25436b4cac81SBjoern A. Zeeb {
25444a67f1dfSBjoern A. Zeeb 	struct ieee80211com *ic;
25456b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
25466b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
25476b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
25486b4cac81SBjoern A. Zeeb 	struct chanAccParams chp;
25496b4cac81SBjoern A. Zeeb 	struct wmeParams wmeparr[WME_NUM_AC];
25506b4cac81SBjoern A. Zeeb 	struct ieee80211_tx_queue_params txqp;
25516b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed changed;
25526b4cac81SBjoern A. Zeeb 	int error;
25536b4cac81SBjoern A. Zeeb 	uint16_t ac;
25546b4cac81SBjoern A. Zeeb 
25556b4cac81SBjoern A. Zeeb 	IMPROVE();
25566b4cac81SBjoern A. Zeeb 	KASSERT(WME_NUM_AC == IEEE80211_NUM_ACS, ("%s: WME_NUM_AC %d != "
25576b4cac81SBjoern A. Zeeb 	    "IEEE80211_NUM_ACS %d\n", __func__, WME_NUM_AC, IEEE80211_NUM_ACS));
25586b4cac81SBjoern A. Zeeb 
25596b4cac81SBjoern A. Zeeb 	if (vap == NULL)
25606b4cac81SBjoern A. Zeeb 		return (0);
25616b4cac81SBjoern A. Zeeb 
25624a67f1dfSBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_WME) == 0)
25634a67f1dfSBjoern A. Zeeb 		return (0);
25644a67f1dfSBjoern A. Zeeb 
25656b4cac81SBjoern A. Zeeb 	if (lhw->ops->conf_tx == NULL)
25666b4cac81SBjoern A. Zeeb 		return (0);
25676b4cac81SBjoern A. Zeeb 
25684a67f1dfSBjoern A. Zeeb 	if (!planned && (vap->iv_state != IEEE80211_S_RUN)) {
25694a67f1dfSBjoern A. Zeeb 		lhw->update_wme = true;
25704a67f1dfSBjoern A. Zeeb 		return (0);
25714a67f1dfSBjoern A. Zeeb 	}
25724a67f1dfSBjoern A. Zeeb 	lhw->update_wme = false;
25736b4cac81SBjoern A. Zeeb 
25744a67f1dfSBjoern A. Zeeb 	ic = lhw->ic;
25756b4cac81SBjoern A. Zeeb 	ieee80211_wme_ic_getparams(ic, &chp);
25766b4cac81SBjoern A. Zeeb 	IEEE80211_LOCK(ic);
25776b4cac81SBjoern A. Zeeb 	for (ac = 0; ac < WME_NUM_AC; ac++)
25786b4cac81SBjoern A. Zeeb 		wmeparr[ac] = chp.cap_wmeParams[ac];
25796b4cac81SBjoern A. Zeeb 	IEEE80211_UNLOCK(ic);
25806b4cac81SBjoern A. Zeeb 
25814a67f1dfSBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
25824a67f1dfSBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
25834a67f1dfSBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
25844a67f1dfSBjoern A. Zeeb 
25856b4cac81SBjoern A. Zeeb 	/* Configure tx queues (conf_tx) & send BSS_CHANGED_QOS. */
25866b4cac81SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
25876b4cac81SBjoern A. Zeeb 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
25886b4cac81SBjoern A. Zeeb 		struct wmeParams *wmep;
25896b4cac81SBjoern A. Zeeb 
25906b4cac81SBjoern A. Zeeb 		wmep = &wmeparr[ac];
25916b4cac81SBjoern A. Zeeb 		bzero(&txqp, sizeof(txqp));
25926b4cac81SBjoern A. Zeeb 		txqp.cw_min = wmep->wmep_logcwmin;
25936b4cac81SBjoern A. Zeeb 		txqp.cw_max = wmep->wmep_logcwmax;
25946b4cac81SBjoern A. Zeeb 		txqp.txop = wmep->wmep_txopLimit;
25956b4cac81SBjoern A. Zeeb 		txqp.aifs = wmep->wmep_aifsn;
259668541546SBjoern A. Zeeb 		error = lkpi_80211_mo_conf_tx(hw, vif, /* link_id */0, ac, &txqp);
25976b4cac81SBjoern A. Zeeb 		if (error != 0)
25983f0083c4SBjoern A. Zeeb 			ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
25996b4cac81SBjoern A. Zeeb 			    __func__, ac, error);
26006b4cac81SBjoern A. Zeeb 	}
26016b4cac81SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
26026b4cac81SBjoern A. Zeeb 	changed = BSS_CHANGED_QOS;
26034a67f1dfSBjoern A. Zeeb 	if (!planned)
26046b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
26054a67f1dfSBjoern A. Zeeb 
26064a67f1dfSBjoern A. Zeeb 	return (changed);
26074a67f1dfSBjoern A. Zeeb }
26086b4cac81SBjoern A. Zeeb #endif
26096b4cac81SBjoern A. Zeeb 
26104a67f1dfSBjoern A. Zeeb static int
26114a67f1dfSBjoern A. Zeeb lkpi_ic_wme_update(struct ieee80211com *ic)
26124a67f1dfSBjoern A. Zeeb {
26134a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
26144a67f1dfSBjoern A. Zeeb 	struct ieee80211vap *vap;
26154a67f1dfSBjoern A. Zeeb 	struct lkpi_hw *lhw;
26164a67f1dfSBjoern A. Zeeb 
26174a67f1dfSBjoern A. Zeeb 	IMPROVE("Use the per-VAP callback in net80211.");
26184a67f1dfSBjoern A. Zeeb 	vap = TAILQ_FIRST(&ic->ic_vaps);
26194a67f1dfSBjoern A. Zeeb 	if (vap == NULL)
26206b4cac81SBjoern A. Zeeb 		return (0);
26214a67f1dfSBjoern A. Zeeb 
26224a67f1dfSBjoern A. Zeeb 	lhw = ic->ic_softc;
26234a67f1dfSBjoern A. Zeeb 
26244a67f1dfSBjoern A. Zeeb 	lkpi_wme_update(lhw, vap, false);
26254a67f1dfSBjoern A. Zeeb #endif
26264a67f1dfSBjoern A. Zeeb 	return (0);	/* unused */
26276b4cac81SBjoern A. Zeeb }
26286b4cac81SBjoern A. Zeeb 
26296b4cac81SBjoern A. Zeeb static struct ieee80211vap *
26306b4cac81SBjoern A. Zeeb lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
26316b4cac81SBjoern A. Zeeb     int unit, enum ieee80211_opmode opmode, int flags,
26326b4cac81SBjoern A. Zeeb     const uint8_t bssid[IEEE80211_ADDR_LEN],
26336b4cac81SBjoern A. Zeeb     const uint8_t mac[IEEE80211_ADDR_LEN])
26346b4cac81SBjoern A. Zeeb {
26356b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
26366b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
26376b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
26386b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
26396b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
2640a6042e17SBjoern A. Zeeb 	struct ieee80211_tx_queue_params txqp;
26416b4cac81SBjoern A. Zeeb 	enum ieee80211_bss_changed changed;
26426b4cac81SBjoern A. Zeeb 	size_t len;
2643e3a0b120SBjoern A. Zeeb 	int error, i;
2644a6042e17SBjoern A. Zeeb 	uint16_t ac;
26456b4cac81SBjoern A. Zeeb 
26466b4cac81SBjoern A. Zeeb 	if (!TAILQ_EMPTY(&ic->ic_vaps))	/* 1 so far. Add <n> once this works. */
26476b4cac81SBjoern A. Zeeb 		return (NULL);
26486b4cac81SBjoern A. Zeeb 
26496b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
26506b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
26516b4cac81SBjoern A. Zeeb 
26526b4cac81SBjoern A. Zeeb 	len = sizeof(*lvif);
26536b4cac81SBjoern A. Zeeb 	len += hw->vif_data_size;	/* vif->drv_priv */
26546b4cac81SBjoern A. Zeeb 
26556b4cac81SBjoern A. Zeeb 	lvif = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO);
26566b4cac81SBjoern A. Zeeb 	mtx_init(&lvif->mtx, "lvif", NULL, MTX_DEF);
26576b4cac81SBjoern A. Zeeb 	TAILQ_INIT(&lvif->lsta_head);
26582ac8a218SBjoern A. Zeeb 	lvif->lvif_bss = NULL;
26592ac8a218SBjoern A. Zeeb 	lvif->lvif_bss_synched = false;
26606b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
26616b4cac81SBjoern A. Zeeb 
26626b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
26636b4cac81SBjoern A. Zeeb 	memcpy(vif->addr, mac, IEEE80211_ADDR_LEN);
26646b4cac81SBjoern A. Zeeb 	vif->p2p = false;
26656b4cac81SBjoern A. Zeeb 	vif->probe_req_reg = false;
26666b4cac81SBjoern A. Zeeb 	vif->type = lkpi_opmode_to_vif_type(opmode);
26676b4cac81SBjoern A. Zeeb 	lvif->wdev.iftype = vif->type;
26686b4cac81SBjoern A. Zeeb 	/* Need to fill in other fields as well. */
26696b4cac81SBjoern A. Zeeb 	IMPROVE();
26706b4cac81SBjoern A. Zeeb 
26716b4cac81SBjoern A. Zeeb 	/* XXX-BZ hardcoded for now! */
26726b4cac81SBjoern A. Zeeb #if 1
26736b4cac81SBjoern A. Zeeb 	vif->chanctx_conf = NULL;
2674adff403fSBjoern A. Zeeb 	vif->bss_conf.vif = vif;
26756ffb7bd4SBjoern A. Zeeb 	/* vap->iv_myaddr is not set until net80211::vap_setup or vap_attach. */
26766ffb7bd4SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(vif->bss_conf.addr, mac);
26776ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.link_id = 0;	/* Non-MLO operation. */
26786b4cac81SBjoern A. Zeeb 	vif->bss_conf.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
26796b4cac81SBjoern A. Zeeb 	vif->bss_conf.use_short_preamble = false;	/* vap->iv_flags IEEE80211_F_SHPREAMBLE */
26806b4cac81SBjoern A. Zeeb 	vif->bss_conf.use_short_slot = false;		/* vap->iv_flags IEEE80211_F_SHSLOT */
26816b4cac81SBjoern A. Zeeb 	vif->bss_conf.qos = false;
26826b4cac81SBjoern A. Zeeb 	vif->bss_conf.use_cts_prot = false;		/* vap->iv_protmode */
26836b4cac81SBjoern A. Zeeb 	vif->bss_conf.ht_operation_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
2684616e1330SBjoern A. Zeeb 	vif->cfg.aid = 0;
2685616e1330SBjoern A. Zeeb 	vif->cfg.assoc = false;
2686616e1330SBjoern A. Zeeb 	vif->cfg.idle = true;
2687616e1330SBjoern A. Zeeb 	vif->cfg.ps = false;
26886ffb7bd4SBjoern A. Zeeb 	IMPROVE("Check other fields and then figure out whats is left elsewhere of them");
2689caaa79c3SBjoern A. Zeeb 	/*
2690caaa79c3SBjoern A. Zeeb 	 * We need to initialize it to something as the bss_info_changed call
2691caaa79c3SBjoern A. Zeeb 	 * will try to copy from it in iwlwifi and NULL is a panic.
2692caaa79c3SBjoern A. Zeeb 	 * We will set the proper one in scan_to_auth() before being assoc.
2693caaa79c3SBjoern A. Zeeb 	 */
26946ffb7bd4SBjoern A. Zeeb 	vif->bss_conf.bssid = ieee80211broadcastaddr;
26956b4cac81SBjoern A. Zeeb #endif
26966b4cac81SBjoern A. Zeeb #if 0
26976b4cac81SBjoern A. Zeeb 	vif->bss_conf.dtim_period = 0; /* IEEE80211_DTIM_DEFAULT ; must stay 0. */
26986b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(vif->bss_conf.bssid, bssid);
26996b4cac81SBjoern A. Zeeb 	vif->bss_conf.beacon_int = ic->ic_bintval;
27006b4cac81SBjoern A. Zeeb 	/* iwlwifi bug. */
27016b4cac81SBjoern A. Zeeb 	if (vif->bss_conf.beacon_int < 16)
27026b4cac81SBjoern A. Zeeb 		vif->bss_conf.beacon_int = 16;
27036b4cac81SBjoern A. Zeeb #endif
2704e3a0b120SBjoern A. Zeeb 
27056ffb7bd4SBjoern A. Zeeb 	/* Link Config */
27066ffb7bd4SBjoern A. Zeeb 	vif->link_conf[0] = &vif->bss_conf;
27076ffb7bd4SBjoern A. Zeeb 	for (i = 0; i < nitems(vif->link_conf); i++) {
27086ffb7bd4SBjoern A. Zeeb 		IMPROVE("more than 1 link one day");
27096ffb7bd4SBjoern A. Zeeb 	}
27106ffb7bd4SBjoern A. Zeeb 
2711e3a0b120SBjoern A. Zeeb 	/* Setup queue defaults; driver may override in (*add_interface). */
2712e3a0b120SBjoern A. Zeeb 	for (i = 0; i < IEEE80211_NUM_ACS; i++) {
2713e3a0b120SBjoern A. Zeeb 		if (ieee80211_hw_check(hw, QUEUE_CONTROL))
2714e3a0b120SBjoern A. Zeeb 			vif->hw_queue[i] = IEEE80211_INVAL_HW_QUEUE;
2715e3a0b120SBjoern A. Zeeb 		else if (hw->queues >= IEEE80211_NUM_ACS)
2716e3a0b120SBjoern A. Zeeb 			vif->hw_queue[i] = i;
2717e3a0b120SBjoern A. Zeeb 		else
2718e3a0b120SBjoern A. Zeeb 			vif->hw_queue[i] = 0;
27190cbcfa19SBjoern A. Zeeb 
27200cbcfa19SBjoern A. Zeeb 		/* Initialize the queue to running. Stopped? */
27210cbcfa19SBjoern A. Zeeb 		lvif->hw_queue_stopped[i] = false;
2722e3a0b120SBjoern A. Zeeb 	}
2723e3a0b120SBjoern A. Zeeb 	vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
2724e3a0b120SBjoern A. Zeeb 
27256b4cac81SBjoern A. Zeeb 	IMPROVE();
27266b4cac81SBjoern A. Zeeb 
27276b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_start(hw);
27286b4cac81SBjoern A. Zeeb 	if (error != 0) {
27293f0083c4SBjoern A. Zeeb 		ic_printf(ic, "%s: failed to start hw: %d\n", __func__, error);
27306b4cac81SBjoern A. Zeeb 		mtx_destroy(&lvif->mtx);
27316b4cac81SBjoern A. Zeeb 		free(lvif, M_80211_VAP);
27326b4cac81SBjoern A. Zeeb 		return (NULL);
27336b4cac81SBjoern A. Zeeb 	}
27346b4cac81SBjoern A. Zeeb 
27356b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_add_interface(hw, vif);
27366b4cac81SBjoern A. Zeeb 	if (error != 0) {
27376b4cac81SBjoern A. Zeeb 		IMPROVE();	/* XXX-BZ mo_stop()? */
27383f0083c4SBjoern A. Zeeb 		ic_printf(ic, "%s: failed to add interface: %d\n", __func__, error);
27396b4cac81SBjoern A. Zeeb 		mtx_destroy(&lvif->mtx);
27406b4cac81SBjoern A. Zeeb 		free(lvif, M_80211_VAP);
27416b4cac81SBjoern A. Zeeb 		return (NULL);
27426b4cac81SBjoern A. Zeeb 	}
27436b4cac81SBjoern A. Zeeb 
27448891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
27456b4cac81SBjoern A. Zeeb 	TAILQ_INSERT_TAIL(&lhw->lvif_head, lvif, lvif_entry);
27468891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
27476b4cac81SBjoern A. Zeeb 
27486b4cac81SBjoern A. Zeeb 	/* Set bss_info. */
27496b4cac81SBjoern A. Zeeb 	changed = 0;
27506b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
27516b4cac81SBjoern A. Zeeb 
2752a6042e17SBjoern A. Zeeb 	/* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */
2753a6042e17SBjoern A. Zeeb 	IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element");
2754a6042e17SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
2755a6042e17SBjoern A. Zeeb 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
2756a6042e17SBjoern A. Zeeb 
2757a6042e17SBjoern A. Zeeb 		bzero(&txqp, sizeof(txqp));
2758a6042e17SBjoern A. Zeeb 		txqp.cw_min = 15;
2759a6042e17SBjoern A. Zeeb 		txqp.cw_max = 1023;
2760a6042e17SBjoern A. Zeeb 		txqp.txop = 0;
2761a6042e17SBjoern A. Zeeb 		txqp.aifs = 2;
2762a6042e17SBjoern A. Zeeb 		error = lkpi_80211_mo_conf_tx(hw, vif, /* link_id */0, ac, &txqp);
2763a6042e17SBjoern A. Zeeb 		if (error != 0)
2764a6042e17SBjoern A. Zeeb 			ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
2765a6042e17SBjoern A. Zeeb 			    __func__, ac, error);
2766a6042e17SBjoern A. Zeeb 	}
2767a6042e17SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
2768a6042e17SBjoern A. Zeeb 	changed = BSS_CHANGED_QOS;
2769a6042e17SBjoern A. Zeeb 	lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
27706b4cac81SBjoern A. Zeeb 
27716b4cac81SBjoern A. Zeeb 	/* Force MC init. */
27726b4cac81SBjoern A. Zeeb 	lkpi_update_mcast_filter(ic, true);
27736b4cac81SBjoern A. Zeeb 
27746b4cac81SBjoern A. Zeeb 	IMPROVE();
27756b4cac81SBjoern A. Zeeb 
27766b4cac81SBjoern A. Zeeb 	ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
27776b4cac81SBjoern A. Zeeb 
27786b4cac81SBjoern A. Zeeb 	/* Override with LinuxKPI method so we can drive mac80211/cfg80211. */
27796b4cac81SBjoern A. Zeeb 	lvif->iv_newstate = vap->iv_newstate;
27806b4cac81SBjoern A. Zeeb 	vap->iv_newstate = lkpi_iv_newstate;
2781d9f59799SBjoern A. Zeeb 	lvif->iv_update_bss = vap->iv_update_bss;
2782d9f59799SBjoern A. Zeeb 	vap->iv_update_bss = lkpi_iv_update_bss;
27836b4cac81SBjoern A. Zeeb 
27846b4cac81SBjoern A. Zeeb 	/* Key management. */
27856b4cac81SBjoern A. Zeeb 	if (lhw->ops->set_key != NULL) {
2786b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
27876b4cac81SBjoern A. Zeeb 		vap->iv_key_set = lkpi_iv_key_set;
27886b4cac81SBjoern A. Zeeb 		vap->iv_key_delete = lkpi_iv_key_delete;
27896b4cac81SBjoern A. Zeeb #endif
27906b4cac81SBjoern A. Zeeb 	}
27916b4cac81SBjoern A. Zeeb 
27929fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
27939fb91463SBjoern A. Zeeb 	/* Stay with the iv_ampdu_rxmax,limit / iv_ampdu_density defaults until later. */
27949fb91463SBjoern A. Zeeb #endif
27959fb91463SBjoern A. Zeeb 
27966b4cac81SBjoern A. Zeeb 	ieee80211_ratectl_init(vap);
27976b4cac81SBjoern A. Zeeb 
27986b4cac81SBjoern A. Zeeb 	/* Complete setup. */
27996b4cac81SBjoern A. Zeeb 	ieee80211_vap_attach(vap, ieee80211_media_change,
28006b4cac81SBjoern A. Zeeb 	    ieee80211_media_status, mac);
28016b4cac81SBjoern A. Zeeb 
28026b4cac81SBjoern A. Zeeb 	if (hw->max_listen_interval == 0)
28036b4cac81SBjoern A. Zeeb 		hw->max_listen_interval = 7 * (ic->ic_lintval / ic->ic_bintval);
28046b4cac81SBjoern A. Zeeb 	hw->conf.listen_interval = hw->max_listen_interval;
28056b4cac81SBjoern A. Zeeb 	ic->ic_set_channel(ic);
28066b4cac81SBjoern A. Zeeb 
28076b4cac81SBjoern A. Zeeb 	/* XXX-BZ do we need to be able to update these? */
28086b4cac81SBjoern A. Zeeb 	hw->wiphy->frag_threshold = vap->iv_fragthreshold;
28096b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_set_frag_threshold(hw, vap->iv_fragthreshold);
28106b4cac81SBjoern A. Zeeb 	hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
28116b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold);
28126b4cac81SBjoern A. Zeeb 	/* any others? */
28136b4cac81SBjoern A. Zeeb 	IMPROVE();
28146b4cac81SBjoern A. Zeeb 
28156b4cac81SBjoern A. Zeeb 	return (vap);
28166b4cac81SBjoern A. Zeeb }
28176b4cac81SBjoern A. Zeeb 
28184b0af114SBjoern A. Zeeb void
28194b0af114SBjoern A. Zeeb linuxkpi_ieee80211_unregister_hw(struct ieee80211_hw *hw)
28204b0af114SBjoern A. Zeeb {
28214b0af114SBjoern A. Zeeb 
28224b0af114SBjoern A. Zeeb 	wiphy_unregister(hw->wiphy);
28234b0af114SBjoern A. Zeeb 	linuxkpi_ieee80211_ifdetach(hw);
28244b0af114SBjoern A. Zeeb 
28254b0af114SBjoern A. Zeeb 	IMPROVE();
28264b0af114SBjoern A. Zeeb }
28274b0af114SBjoern A. Zeeb 
28284b0af114SBjoern A. Zeeb void
28294b0af114SBjoern A. Zeeb linuxkpi_ieee80211_restart_hw(struct ieee80211_hw *hw)
28304b0af114SBjoern A. Zeeb {
28314b0af114SBjoern A. Zeeb 
28324b0af114SBjoern A. Zeeb 	TODO();
28334b0af114SBjoern A. Zeeb }
28344b0af114SBjoern A. Zeeb 
28356b4cac81SBjoern A. Zeeb static void
28366b4cac81SBjoern A. Zeeb lkpi_ic_vap_delete(struct ieee80211vap *vap)
28376b4cac81SBjoern A. Zeeb {
28386b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
28396b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
28406b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
28416b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
28426b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
28436b4cac81SBjoern A. Zeeb 
28446b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
28456b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
28466b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
28476b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
28486b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
28496b4cac81SBjoern A. Zeeb 
28508891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
28516b4cac81SBjoern A. Zeeb 	TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry);
28528891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
28536b4cac81SBjoern A. Zeeb 
28546b4cac81SBjoern A. Zeeb 	ieee80211_ratectl_deinit(vap);
28556b4cac81SBjoern A. Zeeb 	ieee80211_vap_detach(vap);
2856dbf76919SBjoern A. Zeeb 
2857dbf76919SBjoern A. Zeeb 	IMPROVE("clear up other bits in this state");
2858dbf76919SBjoern A. Zeeb 
2859dbf76919SBjoern A. Zeeb 	lkpi_80211_mo_remove_interface(hw, vif);
2860dbf76919SBjoern A. Zeeb 
28616c38c6b1SBjoern A. Zeeb 	/* Single VAP, so we can do this here. */
28626c38c6b1SBjoern A. Zeeb 	lkpi_80211_mo_stop(hw);
28636c38c6b1SBjoern A. Zeeb 
28646b4cac81SBjoern A. Zeeb 	mtx_destroy(&lvif->mtx);
28656b4cac81SBjoern A. Zeeb 	free(lvif, M_80211_VAP);
28666b4cac81SBjoern A. Zeeb }
28676b4cac81SBjoern A. Zeeb 
28686b4cac81SBjoern A. Zeeb static void
28696b4cac81SBjoern A. Zeeb lkpi_ic_update_mcast(struct ieee80211com *ic)
28706b4cac81SBjoern A. Zeeb {
28716b4cac81SBjoern A. Zeeb 
28726b4cac81SBjoern A. Zeeb 	lkpi_update_mcast_filter(ic, false);
28736b4cac81SBjoern A. Zeeb 	TRACEOK();
28746b4cac81SBjoern A. Zeeb }
28756b4cac81SBjoern A. Zeeb 
28766b4cac81SBjoern A. Zeeb static void
28776b4cac81SBjoern A. Zeeb lkpi_ic_update_promisc(struct ieee80211com *ic)
28786b4cac81SBjoern A. Zeeb {
28796b4cac81SBjoern A. Zeeb 
28806b4cac81SBjoern A. Zeeb 	UNIMPLEMENTED;
28816b4cac81SBjoern A. Zeeb }
28826b4cac81SBjoern A. Zeeb 
28836b4cac81SBjoern A. Zeeb static void
28846b4cac81SBjoern A. Zeeb lkpi_ic_update_chw(struct ieee80211com *ic)
28856b4cac81SBjoern A. Zeeb {
28866b4cac81SBjoern A. Zeeb 
28876b4cac81SBjoern A. Zeeb 	UNIMPLEMENTED;
28886b4cac81SBjoern A. Zeeb }
28896b4cac81SBjoern A. Zeeb 
28906b4cac81SBjoern A. Zeeb /* Start / stop device. */
28916b4cac81SBjoern A. Zeeb static void
28926b4cac81SBjoern A. Zeeb lkpi_ic_parent(struct ieee80211com *ic)
28936b4cac81SBjoern A. Zeeb {
28946b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
28958d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
28966b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
28976b4cac81SBjoern A. Zeeb 	int error;
28988d58a057SBjoern A. Zeeb #endif
28996b4cac81SBjoern A. Zeeb 	bool start_all;
29006b4cac81SBjoern A. Zeeb 
29016b4cac81SBjoern A. Zeeb 	IMPROVE();
29026b4cac81SBjoern A. Zeeb 
29036b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
29048d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
29056b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
29068d58a057SBjoern A. Zeeb #endif
29076b4cac81SBjoern A. Zeeb 	start_all = false;
29086b4cac81SBjoern A. Zeeb 
29098ac540d3SBjoern A. Zeeb 	/* IEEE80211_UNLOCK(ic); */
29108ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK(lhw);
29116b4cac81SBjoern A. Zeeb 	if (ic->ic_nrunning > 0) {
29128d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
29136b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_start(hw);
29146b4cac81SBjoern A. Zeeb 		if (error == 0)
29158d58a057SBjoern A. Zeeb #endif
29166b4cac81SBjoern A. Zeeb 			start_all = true;
29176b4cac81SBjoern A. Zeeb 	} else {
29188d58a057SBjoern A. Zeeb #ifdef HW_START_STOP
29196b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_stop(hw);
29208d58a057SBjoern A. Zeeb #endif
29216b4cac81SBjoern A. Zeeb 	}
29228ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_UNLOCK(lhw);
29238ac540d3SBjoern A. Zeeb 	/* IEEE80211_LOCK(ic); */
29246b4cac81SBjoern A. Zeeb 
29256b4cac81SBjoern A. Zeeb 	if (start_all)
29266b4cac81SBjoern A. Zeeb 		ieee80211_start_all(ic);
29276b4cac81SBjoern A. Zeeb }
29286b4cac81SBjoern A. Zeeb 
29296b4cac81SBjoern A. Zeeb bool
29306b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_is_ie_id_in_ie_buf(const u8 ie, const u8 *ie_ids,
29316b4cac81SBjoern A. Zeeb     size_t ie_ids_len)
29326b4cac81SBjoern A. Zeeb {
29336b4cac81SBjoern A. Zeeb 	int i;
29346b4cac81SBjoern A. Zeeb 
29356b4cac81SBjoern A. Zeeb 	for (i = 0; i < ie_ids_len; i++) {
29366b4cac81SBjoern A. Zeeb 		if (ie == *ie_ids)
29376b4cac81SBjoern A. Zeeb 			return (true);
29386b4cac81SBjoern A. Zeeb 	}
29396b4cac81SBjoern A. Zeeb 
29406b4cac81SBjoern A. Zeeb 	return (false);
29416b4cac81SBjoern A. Zeeb }
29426b4cac81SBjoern A. Zeeb 
29436b4cac81SBjoern A. Zeeb /* Return true if skipped; false if error. */
29446b4cac81SBjoern A. Zeeb bool
29456b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_ie_advance(size_t *xp, const u8 *ies, size_t ies_len)
29466b4cac81SBjoern A. Zeeb {
29476b4cac81SBjoern A. Zeeb 	size_t x;
29486b4cac81SBjoern A. Zeeb 	uint8_t l;
29496b4cac81SBjoern A. Zeeb 
29506b4cac81SBjoern A. Zeeb 	x = *xp;
29516b4cac81SBjoern A. Zeeb 
29526b4cac81SBjoern A. Zeeb 	KASSERT(x < ies_len, ("%s: x %zu ies_len %zu ies %p\n",
29536b4cac81SBjoern A. Zeeb 	    __func__, x, ies_len, ies));
29546b4cac81SBjoern A. Zeeb 	l = ies[x + 1];
29556b4cac81SBjoern A. Zeeb 	x += 2 + l;
29566b4cac81SBjoern A. Zeeb 
29576b4cac81SBjoern A. Zeeb 	if (x > ies_len)
29586b4cac81SBjoern A. Zeeb 		return (false);
29596b4cac81SBjoern A. Zeeb 
29606b4cac81SBjoern A. Zeeb 	*xp = x;
29616b4cac81SBjoern A. Zeeb 	return (true);
29626b4cac81SBjoern A. Zeeb }
29636b4cac81SBjoern A. Zeeb 
2964d9945d78SBjoern A. Zeeb static uint8_t *
2965d9945d78SBjoern A. Zeeb lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
2966d9945d78SBjoern A. Zeeb     uint32_t band_mask, struct ieee80211vap *vap, struct ieee80211_hw *hw)
29676b4cac81SBjoern A. Zeeb {
2968d9945d78SBjoern A. Zeeb 	struct ieee80211_supported_band *supband;
2969d9945d78SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *channels;
29703dd98026SBjoern A. Zeeb 	struct ieee80211com *ic;
2971d9945d78SBjoern A. Zeeb 	const struct ieee80211_channel *chan;
2972d9945d78SBjoern A. Zeeb 	const struct ieee80211_rateset *rs;
2973d9945d78SBjoern A. Zeeb 	uint8_t *pb;
2974d9945d78SBjoern A. Zeeb 	int band, i;
29756b4cac81SBjoern A. Zeeb 
29763dd98026SBjoern A. Zeeb 	ic = vap->iv_ic;
2977d9945d78SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
2978d9945d78SBjoern A. Zeeb 		if ((band_mask & (1 << band)) == 0)
2979d9945d78SBjoern A. Zeeb 			continue;
2980d9945d78SBjoern A. Zeeb 
2981d9945d78SBjoern A. Zeeb 		supband = hw->wiphy->bands[band];
2982d9945d78SBjoern A. Zeeb 		/*
2983d9945d78SBjoern A. Zeeb 		 * This should not happen;
2984d9945d78SBjoern A. Zeeb 		 * band_mask is a bitmask of valid bands to scan on.
2985d9945d78SBjoern A. Zeeb 		 */
2986d9945d78SBjoern A. Zeeb 		if (supband == NULL || supband->n_channels == 0)
2987d9945d78SBjoern A. Zeeb 			continue;
2988d9945d78SBjoern A. Zeeb 
2989d9945d78SBjoern A. Zeeb 		/* Find a first channel to get the mode and rates from. */
2990d9945d78SBjoern A. Zeeb 		channels = supband->channels;
2991d9945d78SBjoern A. Zeeb 		chan = NULL;
2992d9945d78SBjoern A. Zeeb 		for (i = 0; i < supband->n_channels; i++) {
2993d9945d78SBjoern A. Zeeb 
2994d9945d78SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED)
2995d9945d78SBjoern A. Zeeb 				continue;
2996d9945d78SBjoern A. Zeeb 
29973dd98026SBjoern A. Zeeb 			chan = ieee80211_find_channel(ic,
2998d9945d78SBjoern A. Zeeb 			    channels[i].center_freq, 0);
2999d9945d78SBjoern A. Zeeb 			if (chan != NULL)
3000d9945d78SBjoern A. Zeeb 				break;
3001d9945d78SBjoern A. Zeeb 		}
3002d9945d78SBjoern A. Zeeb 
3003d9945d78SBjoern A. Zeeb 		/* This really should not happen. */
3004d9945d78SBjoern A. Zeeb 		if (chan == NULL)
3005d9945d78SBjoern A. Zeeb 			continue;
3006d9945d78SBjoern A. Zeeb 
3007d9945d78SBjoern A. Zeeb 		pb = p;
30083dd98026SBjoern A. Zeeb 		rs = ieee80211_get_suprates(ic, chan);	/* calls chan2mode */
3009d9945d78SBjoern A. Zeeb 		p = ieee80211_add_rates(p, rs);
3010d9945d78SBjoern A. Zeeb 		p = ieee80211_add_xrates(p, rs);
3011d9945d78SBjoern A. Zeeb 
30123dd98026SBjoern A. Zeeb #if defined(LKPI_80211_HT)
30133dd98026SBjoern A. Zeeb 		if ((vap->iv_flags_ht & IEEE80211_FHT_HT) != 0) {
30143dd98026SBjoern A. Zeeb 			struct ieee80211_channel *c;
30153dd98026SBjoern A. Zeeb 
30163dd98026SBjoern A. Zeeb 			c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
30173dd98026SBjoern A. Zeeb 			    vap->iv_flags_ht);
30183dd98026SBjoern A. Zeeb 			p = ieee80211_add_htcap_ch(p, vap, c);
30193dd98026SBjoern A. Zeeb 		}
30203dd98026SBjoern A. Zeeb #endif
30213dd98026SBjoern A. Zeeb #if defined(LKPI_80211_VHT)
30223dd98026SBjoern A. Zeeb 		if ((vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
30233dd98026SBjoern A. Zeeb 			struct ieee80211_channel *c;
30243dd98026SBjoern A. Zeeb 
30253dd98026SBjoern A. Zeeb 			c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
30263dd98026SBjoern A. Zeeb 			    vap->iv_flags_ht);
30273dd98026SBjoern A. Zeeb 			c = ieee80211_vht_adjust_channel(ic, c,
30283dd98026SBjoern A. Zeeb 			    vap->iv_vht_flags);
30293dd98026SBjoern A. Zeeb 			p = ieee80211_add_vhtcap_ch(p, vap, c);
30303dd98026SBjoern A. Zeeb 		}
30313dd98026SBjoern A. Zeeb #endif
30323dd98026SBjoern A. Zeeb 
3033d9945d78SBjoern A. Zeeb 		scan_ies->ies[band] = pb;
3034d9945d78SBjoern A. Zeeb 		scan_ies->len[band] = p - pb;
3035d9945d78SBjoern A. Zeeb 	}
3036d9945d78SBjoern A. Zeeb 
3037d9945d78SBjoern A. Zeeb 	/* Add common_ies */
3038d9945d78SBjoern A. Zeeb 	pb = p;
3039d9945d78SBjoern A. Zeeb 	if ((vap->iv_flags & IEEE80211_F_WPA1) != 0 &&
3040d9945d78SBjoern A. Zeeb 	    vap->iv_wpa_ie != NULL) {
3041d9945d78SBjoern A. Zeeb 		memcpy(p, vap->iv_wpa_ie, 2 + vap->iv_wpa_ie[1]);
3042d9945d78SBjoern A. Zeeb 		p += 2 + vap->iv_wpa_ie[1];
3043d9945d78SBjoern A. Zeeb 	}
3044d9945d78SBjoern A. Zeeb 	if (vap->iv_appie_probereq != NULL) {
3045d9945d78SBjoern A. Zeeb 		memcpy(p, vap->iv_appie_probereq->ie_data,
3046d9945d78SBjoern A. Zeeb 		    vap->iv_appie_probereq->ie_len);
3047d9945d78SBjoern A. Zeeb 		p += vap->iv_appie_probereq->ie_len;
3048d9945d78SBjoern A. Zeeb 	}
3049d9945d78SBjoern A. Zeeb 	scan_ies->common_ies = pb;
3050d9945d78SBjoern A. Zeeb 	scan_ies->common_ie_len = p - pb;
3051d9945d78SBjoern A. Zeeb 
3052d9945d78SBjoern A. Zeeb 	return (p);
30536b4cac81SBjoern A. Zeeb }
30546b4cac81SBjoern A. Zeeb 
30556b4cac81SBjoern A. Zeeb static void
30566b4cac81SBjoern A. Zeeb lkpi_ic_scan_start(struct ieee80211com *ic)
30576b4cac81SBjoern A. Zeeb {
30586b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
30596b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
30606b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
30616b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
30626b4cac81SBjoern A. Zeeb 	struct ieee80211_scan_state *ss;
30636b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
30646b4cac81SBjoern A. Zeeb 	int error;
30658ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
30666b4cac81SBjoern A. Zeeb 
30676b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
30688ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
3069a486fbbdSBjoern A. Zeeb 	if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
30706b4cac81SBjoern A. Zeeb 		/* A scan is still running. */
30718ac540d3SBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_UNLOCK(lhw);
30726b4cac81SBjoern A. Zeeb 		return;
30736b4cac81SBjoern A. Zeeb 	}
30748ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
30758ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
30766b4cac81SBjoern A. Zeeb 
30776b4cac81SBjoern A. Zeeb 	ss = ic->ic_scan;
30786b4cac81SBjoern A. Zeeb 	vap = ss->ss_vap;
30796b4cac81SBjoern A. Zeeb 	if (vap->iv_state != IEEE80211_S_SCAN) {
3080d3ef7fb4SBjoern A. Zeeb 		IMPROVE("We need to be able to scan if not in S_SCAN");
30816b4cac81SBjoern A. Zeeb 		return;
30826b4cac81SBjoern A. Zeeb 	}
30836b4cac81SBjoern A. Zeeb 
30846b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
30858ac540d3SBjoern A. Zeeb 	if (!is_hw_scan) {
3086a486fbbdSBjoern A. Zeeb 		/* If hw_scan is cleared clear FEXT_SCAN_OFFLOAD too. */
3087a486fbbdSBjoern A. Zeeb 		vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
3088d3ef7fb4SBjoern A. Zeeb sw_scan:
30896b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
30906b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
3091086be6a8SBjoern A. Zeeb 
3092086be6a8SBjoern A. Zeeb 		if (vap->iv_state == IEEE80211_S_SCAN)
3093086be6a8SBjoern A. Zeeb 			lkpi_hw_conf_idle(hw, false);
3094086be6a8SBjoern A. Zeeb 
30956b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_sw_scan_start(hw, vif, vif->addr);
30966b4cac81SBjoern A. Zeeb 		/* net80211::scan_start() handled PS for us. */
30976b4cac81SBjoern A. Zeeb 		IMPROVE();
30986b4cac81SBjoern A. Zeeb 		/* XXX Also means it is too late to flush queues?
30996b4cac81SBjoern A. Zeeb 		 * need to check iv_sta_ps or overload? */
31006b4cac81SBjoern A. Zeeb 		/* XXX want to adjust ss end time/ maxdwell? */
31016b4cac81SBjoern A. Zeeb 
31026b4cac81SBjoern A. Zeeb 	} else {
31036b4cac81SBjoern A. Zeeb 		struct ieee80211_channel *c;
31046b4cac81SBjoern A. Zeeb 		struct ieee80211_scan_request *hw_req;
31056b4cac81SBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *lc, **cpp;
31066b4cac81SBjoern A. Zeeb 		struct cfg80211_ssid *ssids;
31076b4cac81SBjoern A. Zeeb 		struct cfg80211_scan_6ghz_params *s6gp;
31086b4cac81SBjoern A. Zeeb 		size_t chan_len, nchan, ssids_len, s6ghzlen;
3109d9945d78SBjoern A. Zeeb 		int band, i, ssid_count, common_ie_len;
3110d9945d78SBjoern A. Zeeb 		uint32_t band_mask;
3111d9945d78SBjoern A. Zeeb 		uint8_t *ie, *ieend;
31123206587aSBjoern A. Zeeb 		bool running;
3113d9945d78SBjoern A. Zeeb 
3114d9945d78SBjoern A. Zeeb 		ssid_count = min(ss->ss_nssid, hw->wiphy->max_scan_ssids);
3115d9945d78SBjoern A. Zeeb 		ssids_len = ssid_count * sizeof(*ssids);
31166b4cac81SBjoern A. Zeeb 		s6ghzlen = 0 * (sizeof(*s6gp));			/* XXX-BZ */
31176b4cac81SBjoern A. Zeeb 
3118d9945d78SBjoern A. Zeeb 		band_mask = 0;
31196b4cac81SBjoern A. Zeeb 		nchan = 0;
3120d9945d78SBjoern A. Zeeb 		for (i = ss->ss_next; i < ss->ss_last; i++) {
31216b4cac81SBjoern A. Zeeb 			nchan++;
3122d9945d78SBjoern A. Zeeb 			band = lkpi_net80211_chan_to_nl80211_band(
3123d9945d78SBjoern A. Zeeb 			    ss->ss_chans[ss->ss_next + i]);
3124d9945d78SBjoern A. Zeeb 			band_mask |= (1 << band);
3125d9945d78SBjoern A. Zeeb 		}
31263206587aSBjoern A. Zeeb 
31273206587aSBjoern A. Zeeb 		if (!ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
31283206587aSBjoern A. Zeeb 			IMPROVE("individual band scans not yet supported, only scanning first band");
31293206587aSBjoern A. Zeeb 			/* In theory net80211 should drive this. */
31303206587aSBjoern A. Zeeb 			/* Probably we need to add local logic for now;
31313206587aSBjoern A. Zeeb 			 * need to deal with scan_complete
31323206587aSBjoern A. Zeeb 			 * and cancel_scan and keep local state.
31333206587aSBjoern A. Zeeb 			 * Also cut the nchan down above.
31343206587aSBjoern A. Zeeb 			 */
31353206587aSBjoern A. Zeeb 			/* XXX-BZ ath10k does not set this but still does it? &$%^ */
31363206587aSBjoern A. Zeeb 		}
31373206587aSBjoern A. Zeeb 
31386b4cac81SBjoern A. Zeeb 		chan_len = nchan * (sizeof(lc) + sizeof(*lc));
31396b4cac81SBjoern A. Zeeb 
3140d9945d78SBjoern A. Zeeb 		common_ie_len = 0;
3141d9945d78SBjoern A. Zeeb 		if ((vap->iv_flags & IEEE80211_F_WPA1) != 0 &&
3142d9945d78SBjoern A. Zeeb 		    vap->iv_wpa_ie != NULL)
3143d9945d78SBjoern A. Zeeb 			common_ie_len += vap->iv_wpa_ie[1];
3144d9945d78SBjoern A. Zeeb 		if (vap->iv_appie_probereq != NULL)
3145d9945d78SBjoern A. Zeeb 			common_ie_len += vap->iv_appie_probereq->ie_len;
3146d9945d78SBjoern A. Zeeb 
3147d9945d78SBjoern A. Zeeb 		/* We would love to check this at an earlier stage... */
3148d9945d78SBjoern A. Zeeb 		if (common_ie_len >  hw->wiphy->max_scan_ie_len) {
3149d9945d78SBjoern A. Zeeb 			ic_printf(ic, "WARNING: %s: common_ie_len %d > "
3150d9945d78SBjoern A. Zeeb 			    "wiphy->max_scan_ie_len %d\n", __func__,
3151d9945d78SBjoern A. Zeeb 			    common_ie_len, hw->wiphy->max_scan_ie_len);
3152d9945d78SBjoern A. Zeeb 		}
3153d9945d78SBjoern A. Zeeb 
31543206587aSBjoern A. Zeeb 		hw_req = malloc(sizeof(*hw_req) + ssids_len +
3155d9945d78SBjoern A. Zeeb 		    s6ghzlen + chan_len + lhw->supbands * lhw->scan_ie_len +
3156d9945d78SBjoern A. Zeeb 		    common_ie_len, M_LKPI80211, M_WAITOK | M_ZERO);
31576b4cac81SBjoern A. Zeeb 
31586b4cac81SBjoern A. Zeeb 		hw_req->req.flags = 0;			/* XXX ??? */
31596b4cac81SBjoern A. Zeeb 		/* hw_req->req.wdev */
31606b4cac81SBjoern A. Zeeb 		hw_req->req.wiphy = hw->wiphy;
31616b4cac81SBjoern A. Zeeb 		hw_req->req.no_cck = false;		/* XXX */
31626b4cac81SBjoern A. Zeeb #if 0
31636b4cac81SBjoern A. Zeeb 		/* This seems to pessimise default scanning behaviour. */
31646b4cac81SBjoern A. Zeeb 		hw_req->req.duration_mandatory = TICKS_2_USEC(ss->ss_mindwell);
31656b4cac81SBjoern A. Zeeb 		hw_req->req.duration = TICKS_2_USEC(ss->ss_maxdwell);
31666b4cac81SBjoern A. Zeeb #endif
31676b4cac81SBjoern A. Zeeb #ifdef __notyet__
31686b4cac81SBjoern A. Zeeb 		hw_req->req.flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
31696b4cac81SBjoern A. Zeeb 		memcpy(hw_req->req.mac_addr, xxx, IEEE80211_ADDR_LEN);
31706b4cac81SBjoern A. Zeeb 		memset(hw_req->req.mac_addr_mask, 0xxx, IEEE80211_ADDR_LEN);
31716b4cac81SBjoern A. Zeeb #endif
3172e1e90be0SBjoern A. Zeeb 		eth_broadcast_addr(hw_req->req.bssid);
31736b4cac81SBjoern A. Zeeb 
31746b4cac81SBjoern A. Zeeb 		hw_req->req.n_channels = nchan;
31756b4cac81SBjoern A. Zeeb 		cpp = (struct linuxkpi_ieee80211_channel **)(hw_req + 1);
31766b4cac81SBjoern A. Zeeb 		lc = (struct linuxkpi_ieee80211_channel *)(cpp + nchan);
31776b4cac81SBjoern A. Zeeb 		for (i = 0; i < nchan; i++) {
31786b4cac81SBjoern A. Zeeb 			*(cpp + i) =
31796b4cac81SBjoern A. Zeeb 			    (struct linuxkpi_ieee80211_channel *)(lc + i);
31806b4cac81SBjoern A. Zeeb 		}
31816b4cac81SBjoern A. Zeeb 		for (i = 0; i < nchan; i++) {
31826b4cac81SBjoern A. Zeeb 			c = ss->ss_chans[ss->ss_next + i];
31836b4cac81SBjoern A. Zeeb 
31846b4cac81SBjoern A. Zeeb 			lc->hw_value = c->ic_ieee;
31853206587aSBjoern A. Zeeb 			lc->center_freq = c->ic_freq;	/* XXX */
31866b4cac81SBjoern A. Zeeb 			/* lc->flags */
31876b4cac81SBjoern A. Zeeb 			lc->band = lkpi_net80211_chan_to_nl80211_band(c);
31886b4cac81SBjoern A. Zeeb 			lc->max_power = c->ic_maxpower;
31896b4cac81SBjoern A. Zeeb 			/* lc-> ... */
31906b4cac81SBjoern A. Zeeb 			lc++;
31916b4cac81SBjoern A. Zeeb 		}
31926b4cac81SBjoern A. Zeeb 
3193d9945d78SBjoern A. Zeeb 		hw_req->req.n_ssids = ssid_count;
31946b4cac81SBjoern A. Zeeb 		if (hw_req->req.n_ssids > 0) {
31956b4cac81SBjoern A. Zeeb 			ssids = (struct cfg80211_ssid *)lc;
31966b4cac81SBjoern A. Zeeb 			hw_req->req.ssids = ssids;
3197d9945d78SBjoern A. Zeeb 			for (i = 0; i < ssid_count; i++) {
31986b4cac81SBjoern A. Zeeb 				ssids->ssid_len = ss->ss_ssid[i].len;
31996b4cac81SBjoern A. Zeeb 				memcpy(ssids->ssid, ss->ss_ssid[i].ssid,
32006b4cac81SBjoern A. Zeeb 				    ss->ss_ssid[i].len);
32016b4cac81SBjoern A. Zeeb 				ssids++;
32026b4cac81SBjoern A. Zeeb 			}
32036b4cac81SBjoern A. Zeeb 			s6gp = (struct cfg80211_scan_6ghz_params *)ssids;
32046b4cac81SBjoern A. Zeeb 		} else {
32056b4cac81SBjoern A. Zeeb 			s6gp = (struct cfg80211_scan_6ghz_params *)lc;
32066b4cac81SBjoern A. Zeeb 		}
32076b4cac81SBjoern A. Zeeb 
32086b4cac81SBjoern A. Zeeb 		/* 6GHz one day. */
32096b4cac81SBjoern A. Zeeb 		hw_req->req.n_6ghz_params = 0;
32106b4cac81SBjoern A. Zeeb 		hw_req->req.scan_6ghz_params = NULL;
32116b4cac81SBjoern A. Zeeb 		hw_req->req.scan_6ghz = false;	/* Weird boolean; not what you think. */
32126b4cac81SBjoern A. Zeeb 		/* s6gp->... */
32136b4cac81SBjoern A. Zeeb 
3214d9945d78SBjoern A. Zeeb 		ie = ieend = (uint8_t *)s6gp;
3215d9945d78SBjoern A. Zeeb 		/* Copy per-band IEs, copy common IEs */
3216d9945d78SBjoern A. Zeeb 		ieend = lkpi_scan_ies_add(ie, &hw_req->ies, band_mask, vap, hw);
3217d9945d78SBjoern A. Zeeb 		hw_req->req.ie = ie;
3218d9945d78SBjoern A. Zeeb 		hw_req->req.ie_len = ieend - ie;
3219d9945d78SBjoern A. Zeeb 
32206b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
32216b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
32223206587aSBjoern A. Zeeb 
32233206587aSBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_LOCK(lhw);
32243206587aSBjoern A. Zeeb 		/* Re-check under lock. */
32253206587aSBjoern A. Zeeb 		running = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
32263206587aSBjoern A. Zeeb 		if (!running) {
32273206587aSBjoern A. Zeeb 			KASSERT(lhw->hw_req == NULL, ("%s: ic %p lhw %p hw_req %p "
32283206587aSBjoern A. Zeeb 			    "!= NULL\n", __func__, ic, lhw, lhw->hw_req));
32293206587aSBjoern A. Zeeb 
32303206587aSBjoern A. Zeeb 			lhw->scan_flags |= LKPI_LHW_SCAN_RUNNING;
32313206587aSBjoern A. Zeeb 			lhw->hw_req = hw_req;
32323206587aSBjoern A. Zeeb 		}
32333206587aSBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_UNLOCK(lhw);
32343206587aSBjoern A. Zeeb 		if (running) {
32353206587aSBjoern A. Zeeb 			free(hw_req, M_LKPI80211);
32363206587aSBjoern A. Zeeb 			return;
32373206587aSBjoern A. Zeeb 		}
32383206587aSBjoern A. Zeeb 
32396b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_hw_scan(hw, vif, hw_req);
32406b4cac81SBjoern A. Zeeb 		if (error != 0) {
3241d9945d78SBjoern A. Zeeb 			ieee80211_cancel_scan(vap);
3242d9945d78SBjoern A. Zeeb 
32433206587aSBjoern A. Zeeb 			/*
32443206587aSBjoern A. Zeeb 			 * ieee80211_scan_completed must be called in either
32453206587aSBjoern A. Zeeb 			 * case of error or none.  So let the free happen there
32463206587aSBjoern A. Zeeb 			 * and only there.
32473206587aSBjoern A. Zeeb 			 * That would be fine in theory but in practice drivers
32483206587aSBjoern A. Zeeb 			 * behave differently:
32493206587aSBjoern A. Zeeb 			 * ath10k does not return hw_scan until after scan_complete
32503206587aSBjoern A. Zeeb 			 *        and can then still return an error.
32513206587aSBjoern A. Zeeb 			 * rtw88 can return 1 or -EBUSY without scan_complete
32523206587aSBjoern A. Zeeb 			 * iwlwifi can return various errors before scan starts
32533206587aSBjoern A. Zeeb 			 * ...
32543206587aSBjoern A. Zeeb 			 * So we cannot rely on that behaviour and have to check
32553206587aSBjoern A. Zeeb 			 * and balance between both code paths.
32563206587aSBjoern A. Zeeb 			 */
32573206587aSBjoern A. Zeeb 			LKPI_80211_LHW_SCAN_LOCK(lhw);
32583206587aSBjoern A. Zeeb 			if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
32593206587aSBjoern A. Zeeb 				free(lhw->hw_req, M_LKPI80211);
32606b4cac81SBjoern A. Zeeb 				lhw->hw_req = NULL;
32613206587aSBjoern A. Zeeb 				lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
32623206587aSBjoern A. Zeeb 			}
32633206587aSBjoern A. Zeeb 			LKPI_80211_LHW_SCAN_UNLOCK(lhw);
3264d3ef7fb4SBjoern A. Zeeb 
3265d3ef7fb4SBjoern A. Zeeb 			/*
3266d3ef7fb4SBjoern A. Zeeb 			 * XXX-SIGH magic number.
3267d3ef7fb4SBjoern A. Zeeb 			 * rtw88 has a magic "return 1" if offloading scan is
3268d3ef7fb4SBjoern A. Zeeb 			 * not possible.  Fall back to sw scan in that case.
3269d3ef7fb4SBjoern A. Zeeb 			 */
3270196cfd0bSBjoern A. Zeeb 			if (error == 1) {
32718ac540d3SBjoern A. Zeeb 				LKPI_80211_LHW_SCAN_LOCK(lhw);
3272a486fbbdSBjoern A. Zeeb 				lhw->scan_flags &= ~LKPI_LHW_SCAN_HW;
32738ac540d3SBjoern A. Zeeb 				LKPI_80211_LHW_SCAN_UNLOCK(lhw);
32743206587aSBjoern A. Zeeb 				/*
32753206587aSBjoern A. Zeeb 				 * XXX If we clear this now and later a driver
32763206587aSBjoern A. Zeeb 				 * thinks it * can do a hw_scan again, we will
32773206587aSBjoern A. Zeeb 				 * currently not re-enable it?
32783206587aSBjoern A. Zeeb 				 */
32793206587aSBjoern A. Zeeb 				vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
3280196cfd0bSBjoern A. Zeeb 				ieee80211_start_scan(vap,
3281196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_ACTIVE |
3282196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_NOPICK |
3283196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_ONCE,
3284196cfd0bSBjoern A. Zeeb 				    IEEE80211_SCAN_FOREVER,
3285196cfd0bSBjoern A. Zeeb 				    ss->ss_mindwell ? ss->ss_mindwell : msecs_to_ticks(20),
3286196cfd0bSBjoern A. Zeeb 				    ss->ss_maxdwell ? ss->ss_maxdwell : msecs_to_ticks(200),
3287196cfd0bSBjoern A. Zeeb 				    vap->iv_des_nssid, vap->iv_des_ssid);
3288d3ef7fb4SBjoern A. Zeeb 				goto sw_scan;
3289196cfd0bSBjoern A. Zeeb 			}
3290d3ef7fb4SBjoern A. Zeeb 
3291d3ef7fb4SBjoern A. Zeeb 			ic_printf(ic, "ERROR: %s: hw_scan returned %d\n",
3292d3ef7fb4SBjoern A. Zeeb 			    __func__, error);
32936b4cac81SBjoern A. Zeeb 		}
32946b4cac81SBjoern A. Zeeb 	}
32956b4cac81SBjoern A. Zeeb }
32966b4cac81SBjoern A. Zeeb 
32976b4cac81SBjoern A. Zeeb static void
32986b4cac81SBjoern A. Zeeb lkpi_ic_scan_end(struct ieee80211com *ic)
32996b4cac81SBjoern A. Zeeb {
33006b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
33018ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
33026b4cac81SBjoern A. Zeeb 
33036b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
33048ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
3305a486fbbdSBjoern A. Zeeb 	if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) == 0) {
33068ac540d3SBjoern A. Zeeb 		LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33076b4cac81SBjoern A. Zeeb 		return;
33086b4cac81SBjoern A. Zeeb 	}
33098ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
33108ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33116b4cac81SBjoern A. Zeeb 
33128ac540d3SBjoern A. Zeeb 	if (!is_hw_scan) {
3313a486fbbdSBjoern A. Zeeb 		struct ieee80211_scan_state *ss;
3314a486fbbdSBjoern A. Zeeb 		struct ieee80211vap *vap;
33156b4cac81SBjoern A. Zeeb 		struct ieee80211_hw *hw;
33166b4cac81SBjoern A. Zeeb 		struct lkpi_vif *lvif;
33176b4cac81SBjoern A. Zeeb 		struct ieee80211_vif *vif;
33186b4cac81SBjoern A. Zeeb 
3319a486fbbdSBjoern A. Zeeb 		ss = ic->ic_scan;
3320a486fbbdSBjoern A. Zeeb 		vap = ss->ss_vap;
33216b4cac81SBjoern A. Zeeb 		hw = LHW_TO_HW(lhw);
33226b4cac81SBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
33236b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
3324a486fbbdSBjoern A. Zeeb 
33256b4cac81SBjoern A. Zeeb 		lkpi_80211_mo_sw_scan_complete(hw, vif);
33266b4cac81SBjoern A. Zeeb 
33276b4cac81SBjoern A. Zeeb 		/* Send PS to stop buffering if n80211 does not for us? */
3328086be6a8SBjoern A. Zeeb 
3329086be6a8SBjoern A. Zeeb 		if (vap->iv_state == IEEE80211_S_SCAN)
3330086be6a8SBjoern A. Zeeb 			lkpi_hw_conf_idle(hw, true);
33316b4cac81SBjoern A. Zeeb 	}
33326b4cac81SBjoern A. Zeeb }
33336b4cac81SBjoern A. Zeeb 
33346b4cac81SBjoern A. Zeeb static void
3335a486fbbdSBjoern A. Zeeb lkpi_ic_scan_curchan(struct ieee80211_scan_state *ss,
3336a486fbbdSBjoern A. Zeeb     unsigned long maxdwell)
3337a486fbbdSBjoern A. Zeeb {
3338a486fbbdSBjoern A. Zeeb 	struct lkpi_hw *lhw;
33398ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
3340a486fbbdSBjoern A. Zeeb 
3341a486fbbdSBjoern A. Zeeb 	lhw = ss->ss_ic->ic_softc;
33428ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
33438ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
33448ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33458ac540d3SBjoern A. Zeeb 	if (!is_hw_scan)
3346a486fbbdSBjoern A. Zeeb 		lhw->ic_scan_curchan(ss, maxdwell);
3347a486fbbdSBjoern A. Zeeb }
3348a486fbbdSBjoern A. Zeeb 
3349a486fbbdSBjoern A. Zeeb static void
3350a486fbbdSBjoern A. Zeeb lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss)
3351a486fbbdSBjoern A. Zeeb {
3352a486fbbdSBjoern A. Zeeb 	struct lkpi_hw *lhw;
33538ac540d3SBjoern A. Zeeb 	bool is_hw_scan;
3354a486fbbdSBjoern A. Zeeb 
3355a486fbbdSBjoern A. Zeeb 	lhw = ss->ss_ic->ic_softc;
33568ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
33578ac540d3SBjoern A. Zeeb 	is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
33588ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33598ac540d3SBjoern A. Zeeb 	if (!is_hw_scan)
3360a486fbbdSBjoern A. Zeeb 		lhw->ic_scan_mindwell(ss);
3361a486fbbdSBjoern A. Zeeb }
3362a486fbbdSBjoern A. Zeeb 
3363a486fbbdSBjoern A. Zeeb static void
33646b4cac81SBjoern A. Zeeb lkpi_ic_set_channel(struct ieee80211com *ic)
33656b4cac81SBjoern A. Zeeb {
33666b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
33676b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
3368b2cf3c21SBjoern A. Zeeb 	struct ieee80211_channel *c;
3369b2cf3c21SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
33706b4cac81SBjoern A. Zeeb 	int error;
33718ac540d3SBjoern A. Zeeb 	bool hw_scan_running;
33726b4cac81SBjoern A. Zeeb 
33736b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
3374b2cf3c21SBjoern A. Zeeb 
3375b2cf3c21SBjoern A. Zeeb 	/* If we do not support (*config)() save us the work. */
3376b2cf3c21SBjoern A. Zeeb 	if (lhw->ops->config == NULL)
33776b4cac81SBjoern A. Zeeb 		return;
33786b4cac81SBjoern A. Zeeb 
3379a486fbbdSBjoern A. Zeeb 	/* If we have a hw_scan running do not switch channels. */
33808ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
33818ac540d3SBjoern A. Zeeb 	hw_scan_running =
33828ac540d3SBjoern A. Zeeb 	    (lhw->scan_flags & (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW)) ==
33838ac540d3SBjoern A. Zeeb 		(LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW);
33848ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
33858ac540d3SBjoern A. Zeeb 	if (hw_scan_running)
3386a486fbbdSBjoern A. Zeeb 		return;
3387a486fbbdSBjoern A. Zeeb 
3388b2cf3c21SBjoern A. Zeeb 	c = ic->ic_curchan;
3389b2cf3c21SBjoern A. Zeeb 	if (c == NULL || c == IEEE80211_CHAN_ANYC) {
33906b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: c %p ops->config %p\n", __func__,
33916b4cac81SBjoern A. Zeeb 		    c, lhw->ops->config);
33926b4cac81SBjoern A. Zeeb 		return;
33936b4cac81SBjoern A. Zeeb 	}
33946b4cac81SBjoern A. Zeeb 
33956b4cac81SBjoern A. Zeeb 	chan = lkpi_find_lkpi80211_chan(lhw, c);
33966b4cac81SBjoern A. Zeeb 	if (chan == NULL) {
33976b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: c %p chan %p\n", __func__,
33986b4cac81SBjoern A. Zeeb 		    c, chan);
33996b4cac81SBjoern A. Zeeb 		return;
34006b4cac81SBjoern A. Zeeb 	}
34016b4cac81SBjoern A. Zeeb 
34026b4cac81SBjoern A. Zeeb 	/* XXX max power for scanning? */
34036b4cac81SBjoern A. Zeeb 	IMPROVE();
34046b4cac81SBjoern A. Zeeb 
34056b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
34064a07abdeSBjoern A. Zeeb 	cfg80211_chandef_create(&hw->conf.chandef, chan,
34079fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
34089fb91463SBjoern A. Zeeb 	    (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
34099fb91463SBjoern A. Zeeb #endif
34104a07abdeSBjoern A. Zeeb 	    NL80211_CHAN_NO_HT);
34116b4cac81SBjoern A. Zeeb 
34126b4cac81SBjoern A. Zeeb 	error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_CHANNEL);
34136b4cac81SBjoern A. Zeeb 	if (error != 0 && error != EOPNOTSUPP) {
34146b4cac81SBjoern A. Zeeb 		ic_printf(ic, "ERROR: %s: config %#0x returned %d\n",
34156b4cac81SBjoern A. Zeeb 		    __func__, IEEE80211_CONF_CHANGE_CHANNEL, error);
34166b4cac81SBjoern A. Zeeb 		/* XXX should we unroll to the previous chandef? */
34176b4cac81SBjoern A. Zeeb 		IMPROVE();
34186b4cac81SBjoern A. Zeeb 	} else {
34196b4cac81SBjoern A. Zeeb 		/* Update radiotap channels as well. */
34206b4cac81SBjoern A. Zeeb 		lhw->rtap_tx.wt_chan_freq = htole16(c->ic_freq);
34216b4cac81SBjoern A. Zeeb 		lhw->rtap_tx.wt_chan_flags = htole16(c->ic_flags);
34226b4cac81SBjoern A. Zeeb 		lhw->rtap_rx.wr_chan_freq = htole16(c->ic_freq);
34236b4cac81SBjoern A. Zeeb 		lhw->rtap_rx.wr_chan_flags = htole16(c->ic_flags);
34246b4cac81SBjoern A. Zeeb 	}
34256b4cac81SBjoern A. Zeeb 
34266b4cac81SBjoern A. Zeeb 	/* Currently PS is hard coded off! Not sure it belongs here. */
34276b4cac81SBjoern A. Zeeb 	IMPROVE();
34286b4cac81SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, SUPPORTS_PS) &&
34296b4cac81SBjoern A. Zeeb 	    (hw->conf.flags & IEEE80211_CONF_PS) != 0) {
34306b4cac81SBjoern A. Zeeb 		hw->conf.flags &= ~IEEE80211_CONF_PS;
34316b4cac81SBjoern A. Zeeb 		error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_PS);
34326b4cac81SBjoern A. Zeeb 		if (error != 0 && error != EOPNOTSUPP)
34336b4cac81SBjoern A. Zeeb 			ic_printf(ic, "ERROR: %s: config %#0x returned "
34346b4cac81SBjoern A. Zeeb 			    "%d\n", __func__, IEEE80211_CONF_CHANGE_PS,
34356b4cac81SBjoern A. Zeeb 			    error);
34366b4cac81SBjoern A. Zeeb 	}
34376b4cac81SBjoern A. Zeeb }
34386b4cac81SBjoern A. Zeeb 
34396b4cac81SBjoern A. Zeeb static struct ieee80211_node *
34406b4cac81SBjoern A. Zeeb lkpi_ic_node_alloc(struct ieee80211vap *vap,
34416b4cac81SBjoern A. Zeeb     const uint8_t mac[IEEE80211_ADDR_LEN])
34426b4cac81SBjoern A. Zeeb {
34436b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
34446b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
34454f61ef8bSBjoern A. Zeeb 	struct ieee80211_node *ni;
34466b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
34476b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
34486b4cac81SBjoern A. Zeeb 
34496b4cac81SBjoern A. Zeeb 	ic = vap->iv_ic;
34506b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
34516b4cac81SBjoern A. Zeeb 
34526b4cac81SBjoern A. Zeeb 	/* We keep allocations de-coupled so we can deal with the two worlds. */
34534f61ef8bSBjoern A. Zeeb 	if (lhw->ic_node_alloc == NULL)
34544f61ef8bSBjoern A. Zeeb 		return (NULL);
34554f61ef8bSBjoern A. Zeeb 
34566b4cac81SBjoern A. Zeeb 	ni = lhw->ic_node_alloc(vap, mac);
34576b4cac81SBjoern A. Zeeb 	if (ni == NULL)
34586b4cac81SBjoern A. Zeeb 		return (NULL);
34596b4cac81SBjoern A. Zeeb 
34606b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
34614f61ef8bSBjoern A. Zeeb 	lsta = lkpi_lsta_alloc(vap, mac, hw, ni);
34626b4cac81SBjoern A. Zeeb 	if (lsta == NULL) {
34636b4cac81SBjoern A. Zeeb 		if (lhw->ic_node_free != NULL)
34646b4cac81SBjoern A. Zeeb 			lhw->ic_node_free(ni);
34656b4cac81SBjoern A. Zeeb 		return (NULL);
34666b4cac81SBjoern A. Zeeb 	}
34676b4cac81SBjoern A. Zeeb 
34686b4cac81SBjoern A. Zeeb 	return (ni);
34696b4cac81SBjoern A. Zeeb }
34706b4cac81SBjoern A. Zeeb 
34716b4cac81SBjoern A. Zeeb static int
34726b4cac81SBjoern A. Zeeb lkpi_ic_node_init(struct ieee80211_node *ni)
34736b4cac81SBjoern A. Zeeb {
34746b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
34756b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
34766b4cac81SBjoern A. Zeeb 	int error;
34776b4cac81SBjoern A. Zeeb 
34786b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
34796b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
34806b4cac81SBjoern A. Zeeb 
34816b4cac81SBjoern A. Zeeb 	if (lhw->ic_node_init != NULL) {
34826b4cac81SBjoern A. Zeeb 		error = lhw->ic_node_init(ni);
34836b4cac81SBjoern A. Zeeb 		if (error != 0)
34846b4cac81SBjoern A. Zeeb 			return (error);
34856b4cac81SBjoern A. Zeeb 	}
34866b4cac81SBjoern A. Zeeb 
34876b4cac81SBjoern A. Zeeb 	/* XXX-BZ Sync other state over. */
34886b4cac81SBjoern A. Zeeb 	IMPROVE();
34896b4cac81SBjoern A. Zeeb 
34906b4cac81SBjoern A. Zeeb 	return (0);
34916b4cac81SBjoern A. Zeeb }
34926b4cac81SBjoern A. Zeeb 
34936b4cac81SBjoern A. Zeeb static void
34946b4cac81SBjoern A. Zeeb lkpi_ic_node_cleanup(struct ieee80211_node *ni)
34956b4cac81SBjoern A. Zeeb {
34966b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
34976b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
34986b4cac81SBjoern A. Zeeb 
34996b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
35006b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
35016b4cac81SBjoern A. Zeeb 
35026b4cac81SBjoern A. Zeeb 	/* XXX-BZ remove from driver, ... */
35036b4cac81SBjoern A. Zeeb 	IMPROVE();
35046b4cac81SBjoern A. Zeeb 
35056b4cac81SBjoern A. Zeeb 	if (lhw->ic_node_cleanup != NULL)
35066b4cac81SBjoern A. Zeeb 		lhw->ic_node_cleanup(ni);
35076b4cac81SBjoern A. Zeeb }
35086b4cac81SBjoern A. Zeeb 
35096b4cac81SBjoern A. Zeeb static void
35106b4cac81SBjoern A. Zeeb lkpi_ic_node_free(struct ieee80211_node *ni)
35116b4cac81SBjoern A. Zeeb {
35126b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
35136b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
35146b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
35156b4cac81SBjoern A. Zeeb 
35166b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
35176b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
35186b4cac81SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
35196b4cac81SBjoern A. Zeeb 
3520*0936c648SBjoern A. Zeeb 	/* KASSERT lsta is not NULL here. Print ni/ni__refcnt. */
35216b4cac81SBjoern A. Zeeb 
3522*0936c648SBjoern A. Zeeb 	/*
3523*0936c648SBjoern A. Zeeb 	 * Pass in the original ni just in case of error we could check that
3524*0936c648SBjoern A. Zeeb 	 * it is the same as lsta->ni.
3525*0936c648SBjoern A. Zeeb 	 */
3526*0936c648SBjoern A. Zeeb 	lkpi_lsta_free(lsta, ni);
35276b4cac81SBjoern A. Zeeb 
35286b4cac81SBjoern A. Zeeb 	if (lhw->ic_node_free != NULL)
35296b4cac81SBjoern A. Zeeb 		lhw->ic_node_free(ni);
35306b4cac81SBjoern A. Zeeb }
35316b4cac81SBjoern A. Zeeb 
35326b4cac81SBjoern A. Zeeb static int
35336b4cac81SBjoern A. Zeeb lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
35346b4cac81SBjoern A. Zeeb         const struct ieee80211_bpf_params *params __unused)
35356b4cac81SBjoern A. Zeeb {
35366b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
35376b4cac81SBjoern A. Zeeb 
35386b4cac81SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
3539*0936c648SBjoern A. Zeeb 	/* XXX locking */
3540*0936c648SBjoern A. Zeeb 	if (!lsta->txq_ready) {
3541*0936c648SBjoern A. Zeeb 		m_free(m);
3542*0936c648SBjoern A. Zeeb 		return (ENETDOWN);
3543*0936c648SBjoern A. Zeeb 	}
35446b4cac81SBjoern A. Zeeb 
35456b4cac81SBjoern A. Zeeb 	/* Queue the packet and enqueue the task to handle it. */
35466b4cac81SBjoern A. Zeeb 	LKPI_80211_LSTA_LOCK(lsta);
35476b4cac81SBjoern A. Zeeb 	mbufq_enqueue(&lsta->txq, m);
35486b4cac81SBjoern A. Zeeb 	LKPI_80211_LSTA_UNLOCK(lsta);
35496b4cac81SBjoern A. Zeeb 
35509d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
35519d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
35526b4cac81SBjoern A. Zeeb 		printf("%s:%d lsta %p ni %p %6D mbuf_qlen %d\n",
35536b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, lsta, ni, ni->ni_macaddr, ":",
35546b4cac81SBjoern A. Zeeb 		    mbufq_len(&lsta->txq));
35559d9ba2b7SBjoern A. Zeeb #endif
35566b4cac81SBjoern A. Zeeb 
35576b4cac81SBjoern A. Zeeb 	taskqueue_enqueue(taskqueue_thread, &lsta->txq_task);
35586b4cac81SBjoern A. Zeeb 	return (0);
35596b4cac81SBjoern A. Zeeb }
35606b4cac81SBjoern A. Zeeb 
35616b4cac81SBjoern A. Zeeb static void
35626b4cac81SBjoern A. Zeeb lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
35636b4cac81SBjoern A. Zeeb {
35646b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
3565b35f6cd0SBjoern A. Zeeb #ifndef LKPI_80211_HW_CRYPTO
35666b4cac81SBjoern A. Zeeb 	struct ieee80211_frame *wh;
3567b35f6cd0SBjoern A. Zeeb #endif
35686b4cac81SBjoern A. Zeeb 	struct ieee80211_key *k;
35696b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
35706b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
35716b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
35726b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
35736b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
35746b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
35756b4cac81SBjoern A. Zeeb 	struct ieee80211_channel *c;
35766b4cac81SBjoern A. Zeeb 	struct ieee80211_tx_control control;
35776b4cac81SBjoern A. Zeeb 	struct ieee80211_tx_info *info;
35786b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
3579e3a0b120SBjoern A. Zeeb 	struct ieee80211_hdr *hdr;
35806b4cac81SBjoern A. Zeeb 	void *buf;
3581e3a0b120SBjoern A. Zeeb 	uint8_t ac, tid;
35826b4cac81SBjoern A. Zeeb 
35836b4cac81SBjoern A. Zeeb 	M_ASSERTPKTHDR(m);
35846b4cac81SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
35859d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX_DUMP)
35866b4cac81SBjoern A. Zeeb 		hexdump(mtod(m, const void *), m->m_len, "RAW TX (plain) ", 0);
35876b4cac81SBjoern A. Zeeb #endif
35886b4cac81SBjoern A. Zeeb 
35896b4cac81SBjoern A. Zeeb 	ni = lsta->ni;
3590b35f6cd0SBjoern A. Zeeb 	k = NULL;
3591b35f6cd0SBjoern A. Zeeb #ifndef LKPI_80211_HW_CRYPTO
35926b4cac81SBjoern A. Zeeb 	/* Encrypt the frame if need be; XXX-BZ info->control.hw_key. */
35936b4cac81SBjoern A. Zeeb 	wh = mtod(m, struct ieee80211_frame *);
35946b4cac81SBjoern A. Zeeb 	if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) {
35956b4cac81SBjoern A. Zeeb 		/* Retrieve key for TX && do software encryption. */
35966b4cac81SBjoern A. Zeeb 		k = ieee80211_crypto_encap(ni, m);
35976b4cac81SBjoern A. Zeeb 		if (k == NULL) {
35986b4cac81SBjoern A. Zeeb 			ieee80211_free_node(ni);
35996b4cac81SBjoern A. Zeeb 			m_freem(m);
36006b4cac81SBjoern A. Zeeb 			return;
36016b4cac81SBjoern A. Zeeb 		}
36026b4cac81SBjoern A. Zeeb 	}
36036b4cac81SBjoern A. Zeeb #endif
36046b4cac81SBjoern A. Zeeb 
36056b4cac81SBjoern A. Zeeb 	ic = ni->ni_ic;
36066b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
36076b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
36086b4cac81SBjoern A. Zeeb 	c = ni->ni_chan;
36096b4cac81SBjoern A. Zeeb 
36106b4cac81SBjoern A. Zeeb 	if (ieee80211_radiotap_active_vap(ni->ni_vap)) {
36116b4cac81SBjoern A. Zeeb 		struct lkpi_radiotap_tx_hdr *rtap;
36126b4cac81SBjoern A. Zeeb 
36136b4cac81SBjoern A. Zeeb 		rtap = &lhw->rtap_tx;
36146b4cac81SBjoern A. Zeeb 		rtap->wt_flags = 0;
36156b4cac81SBjoern A. Zeeb 		if (k != NULL)
36166b4cac81SBjoern A. Zeeb 			rtap->wt_flags |= IEEE80211_RADIOTAP_F_WEP;
36176b4cac81SBjoern A. Zeeb 		if (m->m_flags & M_FRAG)
36186b4cac81SBjoern A. Zeeb 			rtap->wt_flags |= IEEE80211_RADIOTAP_F_FRAG;
36196b4cac81SBjoern A. Zeeb 		IMPROVE();
36206b4cac81SBjoern A. Zeeb 		rtap->wt_rate = 0;
36216b4cac81SBjoern A. Zeeb 		if (c != NULL && c != IEEE80211_CHAN_ANYC) {
36226b4cac81SBjoern A. Zeeb 			rtap->wt_chan_freq = htole16(c->ic_freq);
36236b4cac81SBjoern A. Zeeb 			rtap->wt_chan_flags = htole16(c->ic_flags);
36246b4cac81SBjoern A. Zeeb 		}
36256b4cac81SBjoern A. Zeeb 
36266b4cac81SBjoern A. Zeeb 		ieee80211_radiotap_tx(ni->ni_vap, m);
36276b4cac81SBjoern A. Zeeb 	}
36286b4cac81SBjoern A. Zeeb 
36296b4cac81SBjoern A. Zeeb 	/*
36306b4cac81SBjoern A. Zeeb 	 * net80211 should handle hw->extra_tx_headroom.
36316b4cac81SBjoern A. Zeeb 	 * Though for as long as we are copying we don't mind.
36323d09d310SBjoern A. Zeeb 	 * XXX-BZ rtw88 asks for too much headroom for ipv6+tcp:
36333d09d310SBjoern A. Zeeb 	 * https://lists.freebsd.org/archives/freebsd-transport/2022-February/000012.html
36346b4cac81SBjoern A. Zeeb 	 */
36356b4cac81SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + m->m_pkthdr.len);
36366b4cac81SBjoern A. Zeeb 	if (skb == NULL) {
36373f0083c4SBjoern A. Zeeb 		ic_printf(ic, "ERROR %s: skb alloc failed\n", __func__);
36386b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
36396b4cac81SBjoern A. Zeeb 		m_freem(m);
36406b4cac81SBjoern A. Zeeb 		return;
36416b4cac81SBjoern A. Zeeb 	}
36426b4cac81SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
36436b4cac81SBjoern A. Zeeb 
36446b4cac81SBjoern A. Zeeb 	/* XXX-BZ we need a SKB version understanding mbuf. */
36456b4cac81SBjoern A. Zeeb 	/* Save the mbuf for ieee80211_tx_complete(). */
36466b4cac81SBjoern A. Zeeb 	skb->m_free_func = lkpi_ieee80211_free_skb_mbuf;
36476b4cac81SBjoern A. Zeeb 	skb->m = m;
36486b4cac81SBjoern A. Zeeb #if 0
36496b4cac81SBjoern A. Zeeb 	skb_put_data(skb, m->m_data, m->m_pkthdr.len);
36506b4cac81SBjoern A. Zeeb #else
36516b4cac81SBjoern A. Zeeb 	buf = skb_put(skb, m->m_pkthdr.len);
36526b4cac81SBjoern A. Zeeb 	m_copydata(m, 0, m->m_pkthdr.len, buf);
36536b4cac81SBjoern A. Zeeb #endif
36546b4cac81SBjoern A. Zeeb 	/* Save the ni. */
36556b4cac81SBjoern A. Zeeb 	m->m_pkthdr.PH_loc.ptr = ni;
36566b4cac81SBjoern A. Zeeb 
36576b4cac81SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(ni->ni_vap);
36586b4cac81SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
36596b4cac81SBjoern A. Zeeb 
3660e3a0b120SBjoern A. Zeeb 	hdr = (void *)skb->data;
3661e3a0b120SBjoern A. Zeeb 	tid = linuxkpi_ieee80211_get_tid(hdr, true);
3662e3a0b120SBjoern A. Zeeb 	if (tid == IEEE80211_NONQOS_TID) { /* == IEEE80211_NUM_TIDS */
3663e3a0b120SBjoern A. Zeeb 		skb->priority = 0;
3664e3a0b120SBjoern A. Zeeb 		ac = IEEE80211_AC_BE;
3665e3a0b120SBjoern A. Zeeb 	} else {
3666e3a0b120SBjoern A. Zeeb 		skb->priority = tid & IEEE80211_QOS_CTL_TID_MASK;
3667fb3c249eSBjoern A. Zeeb 		ac = ieee80211e_up_to_ac[tid & 7];
3668e3a0b120SBjoern A. Zeeb 	}
36696b4cac81SBjoern A. Zeeb 	skb_set_queue_mapping(skb, ac);
36706b4cac81SBjoern A. Zeeb 
36716b4cac81SBjoern A. Zeeb 	info = IEEE80211_SKB_CB(skb);
36726b4cac81SBjoern A. Zeeb 	info->flags |= IEEE80211_TX_CTL_REQ_TX_STATUS;
36736b4cac81SBjoern A. Zeeb 	/* Slight delay; probably only happens on scanning so fine? */
36746b4cac81SBjoern A. Zeeb 	if (c == NULL || c == IEEE80211_CHAN_ANYC)
36756b4cac81SBjoern A. Zeeb 		c = ic->ic_curchan;
36766b4cac81SBjoern A. Zeeb 	info->band = lkpi_net80211_chan_to_nl80211_band(c);
3677e3a0b120SBjoern A. Zeeb 	info->hw_queue = vif->hw_queue[ac];
36786b4cac81SBjoern A. Zeeb 	if (m->m_flags & M_EAPOL)
36796b4cac81SBjoern A. Zeeb 		info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
36806b4cac81SBjoern A. Zeeb 	info->control.vif = vif;
36816b4cac81SBjoern A. Zeeb 	/* XXX-BZ info->control.rates */
36829fb91463SBjoern A. Zeeb #ifdef __notyet__
36839fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
36849fb91463SBjoern A. Zeeb 	info->control.rts_cts_rate_idx=
36859fb91463SBjoern A. Zeeb 	info->control.use_rts= /* RTS */
36869fb91463SBjoern A. Zeeb 	info->control.use_cts_prot= /* RTS/CTS*/
36879fb91463SBjoern A. Zeeb #endif
36889fb91463SBjoern A. Zeeb #endif
36896b4cac81SBjoern A. Zeeb 
36906b4cac81SBjoern A. Zeeb 	lsta = lkpi_find_lsta_by_ni(lvif, ni);
36916b4cac81SBjoern A. Zeeb 	if (lsta != NULL) {
36926b4cac81SBjoern A. Zeeb 		sta = LSTA_TO_STA(lsta);
3693b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
36946b4cac81SBjoern A. Zeeb 		info->control.hw_key = lsta->kc;
36956b4cac81SBjoern A. Zeeb #endif
36966b4cac81SBjoern A. Zeeb 	} else {
36976b4cac81SBjoern A. Zeeb 		sta = NULL;
36986b4cac81SBjoern A. Zeeb 	}
36996b4cac81SBjoern A. Zeeb 
37006b4cac81SBjoern A. Zeeb 	IMPROVE();
37016b4cac81SBjoern A. Zeeb 
37026b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
37036b4cac81SBjoern A. Zeeb 		struct lkpi_txq *ltxq;
37046b4cac81SBjoern A. Zeeb 
3705e3a0b120SBjoern A. Zeeb 		ltxq = NULL;
3706e3a0b120SBjoern A. Zeeb 		if (!ieee80211_is_data_present(hdr->frame_control)) {
3707e3a0b120SBjoern A. Zeeb 			if (vif->type == NL80211_IFTYPE_STATION &&
3708e3a0b120SBjoern A. Zeeb 			    lsta->added_to_drv &&
3709e3a0b120SBjoern A. Zeeb 			    sta->txq[IEEE80211_NUM_TIDS] != NULL)
3710d0d29110SBjoern A. Zeeb 				ltxq = TXQ_TO_LTXQ(sta->txq[IEEE80211_NUM_TIDS]);
3711e3a0b120SBjoern A. Zeeb 		} else if (lsta->added_to_drv &&
3712e3a0b120SBjoern A. Zeeb 		    sta->txq[skb->priority] != NULL) {
3713e3a0b120SBjoern A. Zeeb 			ltxq = TXQ_TO_LTXQ(sta->txq[skb->priority]);
3714e3a0b120SBjoern A. Zeeb 		}
3715e3a0b120SBjoern A. Zeeb 		if (ltxq == NULL)
3716d0d29110SBjoern A. Zeeb 			goto ops_tx;
3717e3a0b120SBjoern A. Zeeb 
3718d0d29110SBjoern A. Zeeb 		KASSERT(ltxq != NULL, ("%s: lsta %p sta %p m %p skb %p "
3719d0d29110SBjoern A. Zeeb 		    "ltxq %p != NULL\n", __func__, lsta, sta, m, skb, ltxq));
3720d0d29110SBjoern A. Zeeb 
3721eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_LOCK(ltxq);
37226b4cac81SBjoern A. Zeeb 		skb_queue_tail(&ltxq->skbq, skb);
37239d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
37249d9ba2b7SBjoern A. Zeeb 		if (linuxkpi_debug_80211 & D80211_TRACE_TX)
3725d0d29110SBjoern A. Zeeb 			printf("%s:%d mo_wake_tx_queue :: %d %u lsta %p sta %p "
3726d0d29110SBjoern A. Zeeb 			    "ni %p %6D skb %p lxtq %p { qlen %u, ac %d tid %u } "
3727d0d29110SBjoern A. Zeeb 			    "WAKE_TX_Q ac %d prio %u qmap %u\n",
3728fb6eaf74SBjoern A. Zeeb 			    __func__, __LINE__,
3729d0d29110SBjoern A. Zeeb 			    curthread->td_tid, (unsigned int)ticks,
3730d0d29110SBjoern A. Zeeb 			    lsta, sta, ni, ni->ni_macaddr, ":", skb, ltxq,
3731d0d29110SBjoern A. Zeeb 			    skb_queue_len(&ltxq->skbq), ltxq->txq.ac,
3732d0d29110SBjoern A. Zeeb 			    ltxq->txq.tid, ac, skb->priority, skb->qmap);
37339d9ba2b7SBjoern A. Zeeb #endif
3734eac3646fSBjoern A. Zeeb 		LKPI_80211_LTXQ_UNLOCK(ltxq);
3735d0d29110SBjoern A. Zeeb 		lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
37366b4cac81SBjoern A. Zeeb 		return;
37376b4cac81SBjoern A. Zeeb 	}
37386b4cac81SBjoern A. Zeeb 
37396b4cac81SBjoern A. Zeeb ops_tx:
37409d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
37419d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
3742d0d29110SBjoern A. Zeeb 		printf("%s:%d mo_tx :: lsta %p sta %p ni %p %6D skb %p "
3743d0d29110SBjoern A. Zeeb 		    "TX ac %d prio %u qmap %u\n",
37446b4cac81SBjoern A. Zeeb 		    __func__, __LINE__, lsta, sta, ni, ni->ni_macaddr, ":",
37456b4cac81SBjoern A. Zeeb 		    skb, ac, skb->priority, skb->qmap);
37469d9ba2b7SBjoern A. Zeeb #endif
37476b4cac81SBjoern A. Zeeb 	memset(&control, 0, sizeof(control));
37486b4cac81SBjoern A. Zeeb 	control.sta = sta;
37496b4cac81SBjoern A. Zeeb 
37506b4cac81SBjoern A. Zeeb 	lkpi_80211_mo_tx(hw, &control, skb);
37516b4cac81SBjoern A. Zeeb 	return;
37526b4cac81SBjoern A. Zeeb }
37536b4cac81SBjoern A. Zeeb 
37546b4cac81SBjoern A. Zeeb static void
37556b4cac81SBjoern A. Zeeb lkpi_80211_txq_task(void *ctx, int pending)
37566b4cac81SBjoern A. Zeeb {
37576b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
37586b4cac81SBjoern A. Zeeb 	struct mbufq mq;
37596b4cac81SBjoern A. Zeeb 	struct mbuf *m;
37606b4cac81SBjoern A. Zeeb 
37616b4cac81SBjoern A. Zeeb 	lsta = ctx;
37626b4cac81SBjoern A. Zeeb 
37639d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
37649d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
37656b4cac81SBjoern A. Zeeb 		printf("%s:%d lsta %p ni %p %6D pending %d mbuf_qlen %d\n",
37669d9ba2b7SBjoern A. Zeeb 		    __func__, __LINE__, lsta, lsta->ni, lsta->ni->ni_macaddr, ":",
37676b4cac81SBjoern A. Zeeb 		    pending, mbufq_len(&lsta->txq));
37689d9ba2b7SBjoern A. Zeeb #endif
37696b4cac81SBjoern A. Zeeb 
37706b4cac81SBjoern A. Zeeb 	mbufq_init(&mq, IFQ_MAXLEN);
37716b4cac81SBjoern A. Zeeb 
37726b4cac81SBjoern A. Zeeb 	LKPI_80211_LSTA_LOCK(lsta);
37736b4cac81SBjoern A. Zeeb 	mbufq_concat(&mq, &lsta->txq);
37746b4cac81SBjoern A. Zeeb 	LKPI_80211_LSTA_UNLOCK(lsta);
37756b4cac81SBjoern A. Zeeb 
37766b4cac81SBjoern A. Zeeb 	m = mbufq_dequeue(&mq);
37776b4cac81SBjoern A. Zeeb 	while (m != NULL) {
37786b4cac81SBjoern A. Zeeb 		lkpi_80211_txq_tx_one(lsta, m);
37796b4cac81SBjoern A. Zeeb 		m = mbufq_dequeue(&mq);
37806b4cac81SBjoern A. Zeeb 	}
37816b4cac81SBjoern A. Zeeb }
37826b4cac81SBjoern A. Zeeb 
37836b4cac81SBjoern A. Zeeb static int
37846b4cac81SBjoern A. Zeeb lkpi_ic_transmit(struct ieee80211com *ic, struct mbuf *m)
37856b4cac81SBjoern A. Zeeb {
37866b4cac81SBjoern A. Zeeb 
37876b4cac81SBjoern A. Zeeb 	/* XXX TODO */
37886b4cac81SBjoern A. Zeeb 	IMPROVE();
37896b4cac81SBjoern A. Zeeb 
37906b4cac81SBjoern A. Zeeb 	/* Quick and dirty cheating hack. */
37916b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
37926b4cac81SBjoern A. Zeeb 
37936b4cac81SBjoern A. Zeeb 	ni = (struct ieee80211_node *)m->m_pkthdr.rcvif;
37946b4cac81SBjoern A. Zeeb 	return (lkpi_ic_raw_xmit(ni, m, NULL));
37956b4cac81SBjoern A. Zeeb }
37966b4cac81SBjoern A. Zeeb 
37979fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
37989fb91463SBjoern A. Zeeb static int
37999fb91463SBjoern A. Zeeb lkpi_ic_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
38009fb91463SBjoern A. Zeeb     const uint8_t *frm, const uint8_t *efrm)
38019fb91463SBjoern A. Zeeb {
38029fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38039fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38049fb91463SBjoern A. Zeeb 
38059fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38069fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38079fb91463SBjoern A. Zeeb 
38089fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38099fb91463SBjoern A. Zeeb 
38109fb91463SBjoern A. Zeeb 	return (lhw->ic_recv_action(ni, wh, frm, efrm));
38119fb91463SBjoern A. Zeeb }
38129fb91463SBjoern A. Zeeb 
38139fb91463SBjoern A. Zeeb static int
38149fb91463SBjoern A. Zeeb lkpi_ic_send_action(struct ieee80211_node *ni, int category, int action, void *sa)
38159fb91463SBjoern A. Zeeb {
38169fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38179fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38189fb91463SBjoern A. Zeeb 
38199fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38209fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38219fb91463SBjoern A. Zeeb 
38229fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38239fb91463SBjoern A. Zeeb 
38249fb91463SBjoern A. Zeeb 	return (lhw->ic_send_action(ni, category, action, sa));
38259fb91463SBjoern A. Zeeb }
38269fb91463SBjoern A. Zeeb 
38279fb91463SBjoern A. Zeeb 
38289fb91463SBjoern A. Zeeb static int
38299fb91463SBjoern A. Zeeb lkpi_ic_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
38309fb91463SBjoern A. Zeeb {
38319fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38329fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38339fb91463SBjoern A. Zeeb 
38349fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38359fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38369fb91463SBjoern A. Zeeb 
38379fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38389fb91463SBjoern A. Zeeb 
38399fb91463SBjoern A. Zeeb 	return (lhw->ic_ampdu_enable(ni, tap));
38409fb91463SBjoern A. Zeeb }
38419fb91463SBjoern A. Zeeb 
38429fb91463SBjoern A. Zeeb static int
38439fb91463SBjoern A. Zeeb lkpi_ic_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
38449fb91463SBjoern A. Zeeb     int dialogtoken, int baparamset, int batimeout)
38459fb91463SBjoern A. Zeeb {
38469fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38479fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38489fb91463SBjoern A. Zeeb 
38499fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38509fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38519fb91463SBjoern A. Zeeb 
38529fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38539fb91463SBjoern A. Zeeb 
38549fb91463SBjoern A. Zeeb 	return (lhw->ic_addba_request(ni, tap, dialogtoken, baparamset, batimeout));
38559fb91463SBjoern A. Zeeb }
38569fb91463SBjoern A. Zeeb 
38579fb91463SBjoern A. Zeeb static int
38589fb91463SBjoern A. Zeeb lkpi_ic_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
38599fb91463SBjoern A. Zeeb     int status, int baparamset, int batimeout)
38609fb91463SBjoern A. Zeeb {
38619fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38629fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38639fb91463SBjoern A. Zeeb 
38649fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38659fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38669fb91463SBjoern A. Zeeb 
38679fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38689fb91463SBjoern A. Zeeb 
38699fb91463SBjoern A. Zeeb 	return (lhw->ic_addba_response(ni, tap, status, baparamset, batimeout));
38709fb91463SBjoern A. Zeeb }
38719fb91463SBjoern A. Zeeb 
38729fb91463SBjoern A. Zeeb static void
38739fb91463SBjoern A. Zeeb lkpi_ic_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
38749fb91463SBjoern A. Zeeb {
38759fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38769fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38779fb91463SBjoern A. Zeeb 
38789fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38799fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38809fb91463SBjoern A. Zeeb 
38819fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38829fb91463SBjoern A. Zeeb 
38839fb91463SBjoern A. Zeeb 	lhw->ic_addba_stop(ni, tap);
38849fb91463SBjoern A. Zeeb }
38859fb91463SBjoern A. Zeeb 
38869fb91463SBjoern A. Zeeb static void
38879fb91463SBjoern A. Zeeb lkpi_ic_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
38889fb91463SBjoern A. Zeeb {
38899fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
38909fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
38919fb91463SBjoern A. Zeeb 
38929fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
38939fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
38949fb91463SBjoern A. Zeeb 
38959fb91463SBjoern A. Zeeb 	IMPROVE_HT();
38969fb91463SBjoern A. Zeeb 
38979fb91463SBjoern A. Zeeb 	lhw->ic_addba_response_timeout(ni, tap);
38989fb91463SBjoern A. Zeeb }
38999fb91463SBjoern A. Zeeb 
39009fb91463SBjoern A. Zeeb static void
39019fb91463SBjoern A. Zeeb lkpi_ic_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
39029fb91463SBjoern A. Zeeb     int status)
39039fb91463SBjoern A. Zeeb {
39049fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
39059fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39069fb91463SBjoern A. Zeeb 
39079fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39089fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39099fb91463SBjoern A. Zeeb 
39109fb91463SBjoern A. Zeeb 	IMPROVE_HT();
39119fb91463SBjoern A. Zeeb 
39129fb91463SBjoern A. Zeeb 	lhw->ic_bar_response(ni, tap, status);
39139fb91463SBjoern A. Zeeb }
39149fb91463SBjoern A. Zeeb 
39159fb91463SBjoern A. Zeeb static int
39169fb91463SBjoern A. Zeeb lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
39179fb91463SBjoern A. Zeeb     int baparamset, int batimeout, int baseqctl)
39189fb91463SBjoern A. Zeeb {
39199fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
39209fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39219fb91463SBjoern A. Zeeb 	struct ieee80211_hw *hw;
39229fb91463SBjoern A. Zeeb 	struct ieee80211vap *vap;
39239fb91463SBjoern A. Zeeb 	struct lkpi_vif *lvif;
39249fb91463SBjoern A. Zeeb 	struct ieee80211_vif *vif;
39259fb91463SBjoern A. Zeeb 	struct lkpi_sta *lsta;
39269fb91463SBjoern A. Zeeb         struct ieee80211_sta *sta;
39279fb91463SBjoern A. Zeeb 	struct ieee80211_ampdu_params params;
39289fb91463SBjoern A. Zeeb 	int error;
39299fb91463SBjoern A. Zeeb 
39309fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39319fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39329fb91463SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
39339fb91463SBjoern A. Zeeb 	vap = ni->ni_vap;
39349fb91463SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
39359fb91463SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
39369fb91463SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
39379fb91463SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
39389fb91463SBjoern A. Zeeb 
39399fb91463SBjoern A. Zeeb 	params.sta = sta;
39409fb91463SBjoern A. Zeeb 	params.action = IEEE80211_AMPDU_RX_START;
39419fb91463SBjoern A. Zeeb 	params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
39429fb91463SBjoern A. Zeeb 	if (params.buf_size == 0)
39439fb91463SBjoern A. Zeeb 		params.buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
39449fb91463SBjoern A. Zeeb 	else
39459fb91463SBjoern A. Zeeb 		params.buf_size = min(params.buf_size, IEEE80211_MAX_AMPDU_BUF_HT);
39469fb91463SBjoern A. Zeeb 	if (params.buf_size > hw->max_rx_aggregation_subframes)
39479fb91463SBjoern A. Zeeb 		params.buf_size = hw->max_rx_aggregation_subframes;
39489fb91463SBjoern A. Zeeb 	params.timeout = le16toh(batimeout);
39499fb91463SBjoern A. Zeeb 	params.ssn = _IEEE80211_MASKSHIFT(le16toh(baseqctl), IEEE80211_BASEQ_START);
39509fb91463SBjoern A. Zeeb 	params.tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
39519fb91463SBjoern A. Zeeb 	params.amsdu = false;
39529fb91463SBjoern A. Zeeb 
39539fb91463SBjoern A. Zeeb 	IMPROVE_HT("Do we need to distinguish based on SUPPORTS_REORDERING_BUFFER?");
39549fb91463SBjoern A. Zeeb 
39559fb91463SBjoern A. Zeeb 	/* This may call kalloc.  Make sure we can sleep. */
39569fb91463SBjoern A. Zeeb 	error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
39579fb91463SBjoern A. Zeeb 	if (error != 0) {
39589fb91463SBjoern A. Zeeb 		ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
39599fb91463SBjoern A. Zeeb 		    __func__, error, ni, rap);
39609fb91463SBjoern A. Zeeb 		return (error);
39619fb91463SBjoern A. Zeeb 	}
39629fb91463SBjoern A. Zeeb 	IMPROVE_HT("net80211 is missing the error check on return and assumes success");
39639fb91463SBjoern A. Zeeb 
39649fb91463SBjoern A. Zeeb 	error = lhw->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
39659fb91463SBjoern A. Zeeb 	return (error);
39669fb91463SBjoern A. Zeeb }
39679fb91463SBjoern A. Zeeb 
39689fb91463SBjoern A. Zeeb static void
39699fb91463SBjoern A. Zeeb lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
39709fb91463SBjoern A. Zeeb {
39719fb91463SBjoern A. Zeeb 	struct ieee80211com *ic;
39729fb91463SBjoern A. Zeeb 	struct lkpi_hw *lhw;
39739fb91463SBjoern A. Zeeb 	struct ieee80211_hw *hw;
39749fb91463SBjoern A. Zeeb 	struct ieee80211vap *vap;
39759fb91463SBjoern A. Zeeb 	struct lkpi_vif *lvif;
39769fb91463SBjoern A. Zeeb 	struct ieee80211_vif *vif;
39779fb91463SBjoern A. Zeeb 	struct lkpi_sta *lsta;
39789fb91463SBjoern A. Zeeb         struct ieee80211_sta *sta;
39799fb91463SBjoern A. Zeeb 	struct ieee80211_ampdu_params params;
39809fb91463SBjoern A. Zeeb 	int error;
39819fb91463SBjoern A. Zeeb 	uint8_t tid;
39829fb91463SBjoern A. Zeeb 
39839fb91463SBjoern A. Zeeb 	ic = ni->ni_ic;
39849fb91463SBjoern A. Zeeb 	lhw = ic->ic_softc;
39859fb91463SBjoern A. Zeeb 
39869fb91463SBjoern A. Zeeb 	/*
39879fb91463SBjoern A. Zeeb 	 * We should not (cannot) call into mac80211 ops with AMPDU_RX_STOP if
39889fb91463SBjoern A. Zeeb 	 * we did not START.  Some drivers pass it down to firmware which will
39899fb91463SBjoern A. Zeeb 	 * simply barf and net80211 calls ieee80211_ht_node_cleanup() from
39909fb91463SBjoern A. Zeeb 	 * ieee80211_ht_node_init() amongst others which will iterate over all
39919fb91463SBjoern A. Zeeb 	 * tid and call ic_ampdu_rx_stop() unconditionally.
39929fb91463SBjoern A. Zeeb 	 * XXX net80211 should probably be more "gentle" in these cases and
39939fb91463SBjoern A. Zeeb 	 * track some state itself.
39949fb91463SBjoern A. Zeeb 	 */
39959fb91463SBjoern A. Zeeb 	if ((rap->rxa_flags & IEEE80211_AGGR_RUNNING) == 0)
39969fb91463SBjoern A. Zeeb 		goto net80211_only;
39979fb91463SBjoern A. Zeeb 
39989fb91463SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
39999fb91463SBjoern A. Zeeb 	vap = ni->ni_vap;
40009fb91463SBjoern A. Zeeb 	lvif = VAP_TO_LVIF(vap);
40019fb91463SBjoern A. Zeeb 	vif = LVIF_TO_VIF(lvif);
40029fb91463SBjoern A. Zeeb 	lsta = ni->ni_drv_data;
40039fb91463SBjoern A. Zeeb 	sta = LSTA_TO_STA(lsta);
40049fb91463SBjoern A. Zeeb 
40059fb91463SBjoern A. Zeeb 	IMPROVE_HT("This really should be passed from ht_recv_action_ba_delba.");
40069fb91463SBjoern A. Zeeb 	for (tid = 0; tid < WME_NUM_TID; tid++) {
40079fb91463SBjoern A. Zeeb 		if (&ni->ni_rx_ampdu[tid] == rap)
40089fb91463SBjoern A. Zeeb 			break;
40099fb91463SBjoern A. Zeeb 	}
40109fb91463SBjoern A. Zeeb 
40119fb91463SBjoern A. Zeeb 	params.sta = sta;
40129fb91463SBjoern A. Zeeb 	params.action = IEEE80211_AMPDU_RX_STOP;
40139fb91463SBjoern A. Zeeb 	params.buf_size = 0;
40149fb91463SBjoern A. Zeeb 	params.timeout = 0;
40159fb91463SBjoern A. Zeeb 	params.ssn = 0;
40169fb91463SBjoern A. Zeeb 	params.tid = tid;
40179fb91463SBjoern A. Zeeb 	params.amsdu = false;
40189fb91463SBjoern A. Zeeb 
40199fb91463SBjoern A. Zeeb 	error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
40209fb91463SBjoern A. Zeeb 	if (error != 0)
40219fb91463SBjoern A. Zeeb 		ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
40229fb91463SBjoern A. Zeeb 		    __func__, error, ni, rap);
40239fb91463SBjoern A. Zeeb 
40249fb91463SBjoern A. Zeeb net80211_only:
40259fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_rx_stop(ni, rap);
40269fb91463SBjoern A. Zeeb }
40279fb91463SBjoern A. Zeeb #endif
40289fb91463SBjoern A. Zeeb 
40299fb91463SBjoern A. Zeeb static void
40309fb91463SBjoern A. Zeeb lkpi_ic_getradiocaps_ht(struct ieee80211com *ic, struct ieee80211_hw *hw,
40319fb91463SBjoern A. Zeeb     uint8_t *bands, int *chan_flags, enum nl80211_band band)
40329fb91463SBjoern A. Zeeb {
40339fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
40349fb91463SBjoern A. Zeeb 	struct ieee80211_sta_ht_cap *ht_cap;
40359fb91463SBjoern A. Zeeb 
40369fb91463SBjoern A. Zeeb 	ht_cap = &hw->wiphy->bands[band]->ht_cap;
40379fb91463SBjoern A. Zeeb 	if (!ht_cap->ht_supported)
40389fb91463SBjoern A. Zeeb 		return;
40399fb91463SBjoern A. Zeeb 
40409fb91463SBjoern A. Zeeb 	switch (band) {
40419fb91463SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
40429fb91463SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11NG);
40439fb91463SBjoern A. Zeeb 		break;
40449fb91463SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
40459fb91463SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11NA);
40469fb91463SBjoern A. Zeeb 		break;
40479fb91463SBjoern A. Zeeb 	default:
40489fb91463SBjoern A. Zeeb 		IMPROVE("Unsupported band %d", band);
40499fb91463SBjoern A. Zeeb 		return;
40509fb91463SBjoern A. Zeeb 	}
40519fb91463SBjoern A. Zeeb 
40529fb91463SBjoern A. Zeeb 	ic->ic_htcaps = IEEE80211_HTC_HT;	/* HT operation */
40539fb91463SBjoern A. Zeeb 
40549fb91463SBjoern A. Zeeb 	/*
40559fb91463SBjoern A. Zeeb 	 * Rather than manually checking each flag and
40569fb91463SBjoern A. Zeeb 	 * translating IEEE80211_HT_CAP_ to IEEE80211_HTCAP_,
40579fb91463SBjoern A. Zeeb 	 * simply copy the 16bits.
40589fb91463SBjoern A. Zeeb 	 */
40599fb91463SBjoern A. Zeeb 	ic->ic_htcaps |= ht_cap->cap;
40609fb91463SBjoern A. Zeeb 
40619fb91463SBjoern A. Zeeb 	/* Then deal with the other flags. */
40629fb91463SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, AMPDU_AGGREGATION))
40639fb91463SBjoern A. Zeeb 		ic->ic_htcaps |= IEEE80211_HTC_AMPDU;
40649fb91463SBjoern A. Zeeb #ifdef __notyet__
40659fb91463SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, TX_AMSDU))
40669fb91463SBjoern A. Zeeb 		ic->ic_htcaps |= IEEE80211_HTC_AMSDU;
40679fb91463SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU))
40689fb91463SBjoern A. Zeeb 		ic->ic_htcaps |= (IEEE80211_HTC_RX_AMSDU_AMPDU |
40699fb91463SBjoern A. Zeeb 		    IEEE80211_HTC_TX_AMSDU_AMPDU);
40709fb91463SBjoern A. Zeeb #endif
40719fb91463SBjoern A. Zeeb 
40729fb91463SBjoern A. Zeeb 	IMPROVE("PS, ampdu_*, ht_cap.mcs.tx_params, ...");
40739fb91463SBjoern A. Zeeb 	ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_OFF;
40749fb91463SBjoern A. Zeeb 
40759fb91463SBjoern A. Zeeb 	/* Only add HT40 channels if supported. */
40769fb91463SBjoern A. Zeeb 	if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0 &&
40779fb91463SBjoern A. Zeeb 	    chan_flags != NULL)
40789fb91463SBjoern A. Zeeb 		*chan_flags |= NET80211_CBW_FLAG_HT40;
40799fb91463SBjoern A. Zeeb #endif
40809fb91463SBjoern A. Zeeb }
40819fb91463SBjoern A. Zeeb 
40826b4cac81SBjoern A. Zeeb static void
40836b4cac81SBjoern A. Zeeb lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
40846b4cac81SBjoern A. Zeeb     int *n, struct ieee80211_channel *c)
40856b4cac81SBjoern A. Zeeb {
40866b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
40876b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
40886b4cac81SBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *channels;
40896b4cac81SBjoern A. Zeeb 	uint8_t bands[IEEE80211_MODE_BYTES];
40906b4cac81SBjoern A. Zeeb 	int chan_flags, error, i, nchans;
40916b4cac81SBjoern A. Zeeb 
40926b4cac81SBjoern A. Zeeb 	/* Channels */
40936b4cac81SBjoern A. Zeeb 	lhw = ic->ic_softc;
40946b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
40956b4cac81SBjoern A. Zeeb 
40966b4cac81SBjoern A. Zeeb 	/* NL80211_BAND_2GHZ */
40976b4cac81SBjoern A. Zeeb 	nchans = 0;
40986b4cac81SBjoern A. Zeeb 	if (hw->wiphy->bands[NL80211_BAND_2GHZ] != NULL)
40996b4cac81SBjoern A. Zeeb 		nchans = hw->wiphy->bands[NL80211_BAND_2GHZ]->n_channels;
41006b4cac81SBjoern A. Zeeb 	if (nchans > 0) {
41016b4cac81SBjoern A. Zeeb 		memset(bands, 0, sizeof(bands));
41026b4cac81SBjoern A. Zeeb 		chan_flags = 0;
41036b4cac81SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11B);
41046b4cac81SBjoern A. Zeeb 		/* XXX-BZ unclear how to check for 11g. */
41059fb91463SBjoern A. Zeeb 
41069fb91463SBjoern A. Zeeb 		IMPROVE("the bitrates may have flags?");
41076b4cac81SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11G);
41089fb91463SBjoern A. Zeeb 
41099fb91463SBjoern A. Zeeb 		lkpi_ic_getradiocaps_ht(ic, hw, bands, &chan_flags,
41109fb91463SBjoern A. Zeeb 		    NL80211_BAND_2GHZ);
41116b4cac81SBjoern A. Zeeb 
41126b4cac81SBjoern A. Zeeb 		channels = hw->wiphy->bands[NL80211_BAND_2GHZ]->channels;
4113cee56e77SBjoern A. Zeeb 		for (i = 0; i < nchans && *n < maxchan; i++) {
41146b4cac81SBjoern A. Zeeb 			uint32_t nflags = 0;
41156b4cac81SBjoern A. Zeeb 			int cflags = chan_flags;
41166b4cac81SBjoern A. Zeeb 
41176b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED) {
41183f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Skipping disabled chan "
41193f0083c4SBjoern A. Zeeb 				    "[%u/%u/%#x]\n", __func__,
41206b4cac81SBjoern A. Zeeb 				    channels[i].hw_value,
41216b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags);
41226b4cac81SBjoern A. Zeeb 				continue;
41236b4cac81SBjoern A. Zeeb 			}
41246b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_IR)
41256b4cac81SBjoern A. Zeeb 				nflags |= (IEEE80211_CHAN_NOADHOC|IEEE80211_CHAN_PASSIVE);
41266b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_RADAR)
41276b4cac81SBjoern A. Zeeb 				nflags |= IEEE80211_CHAN_DFS;
41286b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_160MHZ)
41296b4cac81SBjoern A. Zeeb 				cflags &= ~(NET80211_CBW_FLAG_VHT160|NET80211_CBW_FLAG_VHT80P80);
41306b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_80MHZ)
41316b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_VHT80;
41326b4cac81SBjoern A. Zeeb 			/* XXX how to map the remaining enum ieee80211_channel_flags? */
41336b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_HT40)
41346b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_HT40;
41356b4cac81SBjoern A. Zeeb 
41366b4cac81SBjoern A. Zeeb 			error = ieee80211_add_channel_cbw(c, maxchan, n,
41376b4cac81SBjoern A. Zeeb 			    channels[i].hw_value, channels[i].center_freq,
41386b4cac81SBjoern A. Zeeb 			    channels[i].max_power,
41395856761fSBjoern A. Zeeb 			    nflags, bands, cflags);
4140cee56e77SBjoern A. Zeeb 			/* net80211::ENOBUFS: *n >= maxchans */
4141cee56e77SBjoern A. Zeeb 			if (error != 0 && error != ENOBUFS)
41423f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
41433f0083c4SBjoern A. Zeeb 				    "returned error %d\n",
41446b4cac81SBjoern A. Zeeb 				    __func__, channels[i].hw_value,
41456b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags,
41466b4cac81SBjoern A. Zeeb 				    nflags, chan_flags, cflags, error);
4147cee56e77SBjoern A. Zeeb 			if (error != 0)
41486b4cac81SBjoern A. Zeeb 				break;
41496b4cac81SBjoern A. Zeeb 		}
41506b4cac81SBjoern A. Zeeb 	}
41516b4cac81SBjoern A. Zeeb 
41526b4cac81SBjoern A. Zeeb 	/* NL80211_BAND_5GHZ */
41536b4cac81SBjoern A. Zeeb 	nchans = 0;
41546b4cac81SBjoern A. Zeeb 	if (hw->wiphy->bands[NL80211_BAND_5GHZ] != NULL)
41556b4cac81SBjoern A. Zeeb 		nchans = hw->wiphy->bands[NL80211_BAND_5GHZ]->n_channels;
41566b4cac81SBjoern A. Zeeb 	if (nchans > 0) {
41576b4cac81SBjoern A. Zeeb 		memset(bands, 0, sizeof(bands));
41586b4cac81SBjoern A. Zeeb 		chan_flags = 0;
41596b4cac81SBjoern A. Zeeb 		setbit(bands, IEEE80211_MODE_11A);
41609fb91463SBjoern A. Zeeb 
41619fb91463SBjoern A. Zeeb 		lkpi_ic_getradiocaps_ht(ic, hw, bands, &chan_flags,
41629fb91463SBjoern A. Zeeb 		    NL80211_BAND_5GHZ);
41639fb91463SBjoern A. Zeeb 
41649fb91463SBjoern A. Zeeb #ifdef LKPI_80211_VHT
41656b4cac81SBjoern A. Zeeb 		if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported){
41666b4cac81SBjoern A. Zeeb 
41676b4cac81SBjoern A. Zeeb 			ic->ic_flags_ext |= IEEE80211_FEXT_VHT;
4168562adbe1SBjoern A. Zeeb 			ic->ic_vht_cap.vht_cap_info =
41696b4cac81SBjoern A. Zeeb 			    hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap;
41706b4cac81SBjoern A. Zeeb 
41716b4cac81SBjoern A. Zeeb 			setbit(bands, IEEE80211_MODE_VHT_5GHZ);
41726b4cac81SBjoern A. Zeeb 			chan_flags |= NET80211_CBW_FLAG_VHT80;
41736b4cac81SBjoern A. Zeeb 			if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(
4174562adbe1SBjoern A. Zeeb 			    ic->ic_vht_cap.vht_cap_info))
41756b4cac81SBjoern A. Zeeb 				chan_flags |= NET80211_CBW_FLAG_VHT160;
41766b4cac81SBjoern A. Zeeb 			if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(
4177562adbe1SBjoern A. Zeeb 			    ic->ic_vht_cap.vht_cap_info))
41786b4cac81SBjoern A. Zeeb 				chan_flags |= NET80211_CBW_FLAG_VHT80P80;
41796b4cac81SBjoern A. Zeeb 		}
41806b4cac81SBjoern A. Zeeb #endif
41816b4cac81SBjoern A. Zeeb 
41826b4cac81SBjoern A. Zeeb 		channels = hw->wiphy->bands[NL80211_BAND_5GHZ]->channels;
4183cee56e77SBjoern A. Zeeb 		for (i = 0; i < nchans && *n < maxchan; i++) {
41846b4cac81SBjoern A. Zeeb 			uint32_t nflags = 0;
41856b4cac81SBjoern A. Zeeb 			int cflags = chan_flags;
41866b4cac81SBjoern A. Zeeb 
41876b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED) {
41883f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Skipping disabled chan "
41893f0083c4SBjoern A. Zeeb 				    "[%u/%u/%#x]\n", __func__,
41906b4cac81SBjoern A. Zeeb 				    channels[i].hw_value,
41916b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags);
41926b4cac81SBjoern A. Zeeb 				continue;
41936b4cac81SBjoern A. Zeeb 			}
41946b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_IR)
41956b4cac81SBjoern A. Zeeb 				nflags |= (IEEE80211_CHAN_NOADHOC|IEEE80211_CHAN_PASSIVE);
41966b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_RADAR)
41976b4cac81SBjoern A. Zeeb 				nflags |= IEEE80211_CHAN_DFS;
41986b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_160MHZ)
41996b4cac81SBjoern A. Zeeb 				cflags &= ~(NET80211_CBW_FLAG_VHT160|NET80211_CBW_FLAG_VHT80P80);
42006b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_80MHZ)
42016b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_VHT80;
42026b4cac81SBjoern A. Zeeb 			/* XXX hwo to map the remaining enum ieee80211_channel_flags? */
42036b4cac81SBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_NO_HT40)
42046b4cac81SBjoern A. Zeeb 				cflags &= ~NET80211_CBW_FLAG_HT40;
42056b4cac81SBjoern A. Zeeb 
42066b4cac81SBjoern A. Zeeb 			error = ieee80211_add_channel_cbw(c, maxchan, n,
42076b4cac81SBjoern A. Zeeb 			    channels[i].hw_value, channels[i].center_freq,
42086b4cac81SBjoern A. Zeeb 			    channels[i].max_power,
42095856761fSBjoern A. Zeeb 			    nflags, bands, cflags);
4210cee56e77SBjoern A. Zeeb 			/* net80211::ENOBUFS: *n >= maxchans */
4211cee56e77SBjoern A. Zeeb 			if (error != 0 && error != ENOBUFS)
42123f0083c4SBjoern A. Zeeb 				ic_printf(ic, "%s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
42133f0083c4SBjoern A. Zeeb 				    "returned error %d\n",
42146b4cac81SBjoern A. Zeeb 				    __func__, channels[i].hw_value,
42156b4cac81SBjoern A. Zeeb 				    channels[i].center_freq, channels[i].flags,
42166b4cac81SBjoern A. Zeeb 				    nflags, chan_flags, cflags, error);
4217cee56e77SBjoern A. Zeeb 			if (error != 0)
42186b4cac81SBjoern A. Zeeb 				break;
42196b4cac81SBjoern A. Zeeb 		}
42206b4cac81SBjoern A. Zeeb 	}
42216b4cac81SBjoern A. Zeeb }
42226b4cac81SBjoern A. Zeeb 
42236b4cac81SBjoern A. Zeeb static void *
42246b4cac81SBjoern A. Zeeb lkpi_ieee80211_ifalloc(void)
42256b4cac81SBjoern A. Zeeb {
42266b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
42276b4cac81SBjoern A. Zeeb 
42286b4cac81SBjoern A. Zeeb 	ic = malloc(sizeof(*ic), M_LKPI80211, M_WAITOK | M_ZERO);
42296b4cac81SBjoern A. Zeeb 	if (ic == NULL)
42306b4cac81SBjoern A. Zeeb 		return (NULL);
42316b4cac81SBjoern A. Zeeb 
42326b4cac81SBjoern A. Zeeb 	/* Setting these happens later when we have device information. */
42336b4cac81SBjoern A. Zeeb 	ic->ic_softc = NULL;
42346b4cac81SBjoern A. Zeeb 	ic->ic_name = "linuxkpi";
42356b4cac81SBjoern A. Zeeb 
42366b4cac81SBjoern A. Zeeb 	return (ic);
42376b4cac81SBjoern A. Zeeb }
42386b4cac81SBjoern A. Zeeb 
42396b4cac81SBjoern A. Zeeb struct ieee80211_hw *
42406b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
42416b4cac81SBjoern A. Zeeb {
42426b4cac81SBjoern A. Zeeb 	struct ieee80211_hw *hw;
42436b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
42446b4cac81SBjoern A. Zeeb 	struct wiphy *wiphy;
4245a5ae63edSBjoern A. Zeeb 	int ac;
42466b4cac81SBjoern A. Zeeb 
42476b4cac81SBjoern A. Zeeb 	/* Get us and the driver data also allocated. */
42486b4cac81SBjoern A. Zeeb 	wiphy = wiphy_new(&linuxkpi_mac80211cfgops, sizeof(*lhw) + priv_len);
42496b4cac81SBjoern A. Zeeb 	if (wiphy == NULL)
42506b4cac81SBjoern A. Zeeb 		return (NULL);
42516b4cac81SBjoern A. Zeeb 
42526b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
42536b4cac81SBjoern A. Zeeb 	lhw->ops = ops;
4254652e22d3SBjoern A. Zeeb 
42558ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_LOCK_INIT(lhw);
42568ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK_INIT(lhw);
4257eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_LOCK_INIT(lhw);
42588891c455SBjoern A. Zeeb 	sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
42596b4cac81SBjoern A. Zeeb 	TAILQ_INIT(&lhw->lvif_head);
4260a5ae63edSBjoern A. Zeeb 	for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
4261a5ae63edSBjoern A. Zeeb 		lhw->txq_generation[ac] = 1;
4262a5ae63edSBjoern A. Zeeb 		TAILQ_INIT(&lhw->scheduled_txqs[ac]);
4263a5ae63edSBjoern A. Zeeb 	}
42646b4cac81SBjoern A. Zeeb 
42656b4cac81SBjoern A. Zeeb 	/*
42666b4cac81SBjoern A. Zeeb 	 * XXX-BZ TODO make sure there is a "_null" function to all ops
42676b4cac81SBjoern A. Zeeb 	 * not initialized.
42686b4cac81SBjoern A. Zeeb 	 */
42696b4cac81SBjoern A. Zeeb 	hw = LHW_TO_HW(lhw);
42706b4cac81SBjoern A. Zeeb 	hw->wiphy = wiphy;
4271086be6a8SBjoern A. Zeeb 	hw->conf.flags |= IEEE80211_CONF_IDLE;
42726b4cac81SBjoern A. Zeeb 	hw->priv = (void *)(lhw + 1);
42736b4cac81SBjoern A. Zeeb 
42746b4cac81SBjoern A. Zeeb 	/* BSD Specific. */
42756b4cac81SBjoern A. Zeeb 	lhw->ic = lkpi_ieee80211_ifalloc();
42766b4cac81SBjoern A. Zeeb 	if (lhw->ic == NULL) {
42776b4cac81SBjoern A. Zeeb 		ieee80211_free_hw(hw);
42786b4cac81SBjoern A. Zeeb 		return (NULL);
42796b4cac81SBjoern A. Zeeb 	}
42806b4cac81SBjoern A. Zeeb 
42816b4cac81SBjoern A. Zeeb 	IMPROVE();
42826b4cac81SBjoern A. Zeeb 
42836b4cac81SBjoern A. Zeeb 	return (hw);
42846b4cac81SBjoern A. Zeeb }
42856b4cac81SBjoern A. Zeeb 
42866b4cac81SBjoern A. Zeeb void
42876b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
42886b4cac81SBjoern A. Zeeb {
42896b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
42906b4cac81SBjoern A. Zeeb 
42916b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
42926b4cac81SBjoern A. Zeeb 	free(lhw->ic, M_LKPI80211);
42936b4cac81SBjoern A. Zeeb 	lhw->ic = NULL;
42946b4cac81SBjoern A. Zeeb 
42956b4cac81SBjoern A. Zeeb 	/* Cleanup more of lhw here or in wiphy_free()? */
4296eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
42978ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw);
4298eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_LOCK_DESTROY(lhw);
4299eac3646fSBjoern A. Zeeb 	sx_destroy(&lhw->lvif_sx);
43006b4cac81SBjoern A. Zeeb 	IMPROVE();
43016b4cac81SBjoern A. Zeeb }
43026b4cac81SBjoern A. Zeeb 
43036b4cac81SBjoern A. Zeeb void
43046b4cac81SBjoern A. Zeeb linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw, char *name)
43056b4cac81SBjoern A. Zeeb {
43066b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
43076b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
43086b4cac81SBjoern A. Zeeb 
43096b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
43106b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
43116b4cac81SBjoern A. Zeeb 
43126b4cac81SBjoern A. Zeeb 	/* Now set a proper name before ieee80211_ifattach(). */
43136b4cac81SBjoern A. Zeeb 	ic->ic_softc = lhw;
43146b4cac81SBjoern A. Zeeb 	ic->ic_name = name;
43156b4cac81SBjoern A. Zeeb 
43166b4cac81SBjoern A. Zeeb 	/* XXX-BZ do we also need to set wiphy name? */
43176b4cac81SBjoern A. Zeeb }
43186b4cac81SBjoern A. Zeeb 
43196b4cac81SBjoern A. Zeeb struct ieee80211_hw *
43206b4cac81SBjoern A. Zeeb linuxkpi_wiphy_to_ieee80211_hw(struct wiphy *wiphy)
43216b4cac81SBjoern A. Zeeb {
43226b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
43236b4cac81SBjoern A. Zeeb 
43246b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
43256b4cac81SBjoern A. Zeeb 	return (LHW_TO_HW(lhw));
43266b4cac81SBjoern A. Zeeb }
43276b4cac81SBjoern A. Zeeb 
43286b4cac81SBjoern A. Zeeb static void
43296b4cac81SBjoern A. Zeeb lkpi_radiotap_attach(struct lkpi_hw *lhw)
43306b4cac81SBjoern A. Zeeb {
43316b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
43326b4cac81SBjoern A. Zeeb 
43336b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
43346b4cac81SBjoern A. Zeeb 	ieee80211_radiotap_attach(ic,
43356b4cac81SBjoern A. Zeeb 	    &lhw->rtap_tx.wt_ihdr, sizeof(lhw->rtap_tx),
43366b4cac81SBjoern A. Zeeb 	    LKPI_RTAP_TX_FLAGS_PRESENT,
43376b4cac81SBjoern A. Zeeb 	    &lhw->rtap_rx.wr_ihdr, sizeof(lhw->rtap_rx),
43386b4cac81SBjoern A. Zeeb 	    LKPI_RTAP_RX_FLAGS_PRESENT);
43396b4cac81SBjoern A. Zeeb }
43406b4cac81SBjoern A. Zeeb 
43412e183d99SBjoern A. Zeeb int
43426b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
43436b4cac81SBjoern A. Zeeb {
43446b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
43456b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
4346c5b96b3eSBjoern A. Zeeb 	int band, i;
43476b4cac81SBjoern A. Zeeb 
43486b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
43496b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
43506b4cac81SBjoern A. Zeeb 
4351652e22d3SBjoern A. Zeeb 	/* We do it this late as wiphy->dev should be set for the name. */
4352652e22d3SBjoern A. Zeeb 	lhw->workq = alloc_ordered_workqueue(wiphy_name(hw->wiphy), 0);
4353652e22d3SBjoern A. Zeeb 	if (lhw->workq == NULL)
4354652e22d3SBjoern A. Zeeb 		return (-EAGAIN);
4355652e22d3SBjoern A. Zeeb 
43566b4cac81SBjoern A. Zeeb 	/* XXX-BZ figure this out how they count his... */
43576b4cac81SBjoern A. Zeeb 	if (!is_zero_ether_addr(hw->wiphy->perm_addr)) {
43586b4cac81SBjoern A. Zeeb 		IEEE80211_ADDR_COPY(ic->ic_macaddr,
43596b4cac81SBjoern A. Zeeb 		    hw->wiphy->perm_addr);
43606b4cac81SBjoern A. Zeeb 	} else if (hw->wiphy->n_addresses > 0) {
43616b4cac81SBjoern A. Zeeb 		/* We take the first one. */
43626b4cac81SBjoern A. Zeeb 		IEEE80211_ADDR_COPY(ic->ic_macaddr,
43636b4cac81SBjoern A. Zeeb 		    hw->wiphy->addresses[0].addr);
43646b4cac81SBjoern A. Zeeb 	} else {
43656b4cac81SBjoern A. Zeeb 		ic_printf(ic, "%s: warning, no hardware address!\n", __func__);
43666b4cac81SBjoern A. Zeeb 	}
43676b4cac81SBjoern A. Zeeb 
43683d09d310SBjoern A. Zeeb #ifdef __not_yet__
43693d09d310SBjoern A. Zeeb 	/* See comment in lkpi_80211_txq_tx_one(). */
43706b4cac81SBjoern A. Zeeb 	ic->ic_headroom = hw->extra_tx_headroom;
43713d09d310SBjoern A. Zeeb #endif
43726b4cac81SBjoern A. Zeeb 
43736b4cac81SBjoern A. Zeeb 	ic->ic_phytype = IEEE80211_T_OFDM;	/* not only, but not used */
43746b4cac81SBjoern A. Zeeb 	ic->ic_opmode = IEEE80211_M_STA;
43756b4cac81SBjoern A. Zeeb 
43766b4cac81SBjoern A. Zeeb 	/* Set device capabilities. */
43776b4cac81SBjoern A. Zeeb 	/* XXX-BZ we need to get these from linux80211/drivers and convert. */
43786b4cac81SBjoern A. Zeeb 	ic->ic_caps =
43796b4cac81SBjoern A. Zeeb 	    IEEE80211_C_STA |
43806b4cac81SBjoern A. Zeeb 	    IEEE80211_C_MONITOR |
43816b4cac81SBjoern A. Zeeb 	    IEEE80211_C_WPA |		/* WPA/RSN */
43824a67f1dfSBjoern A. Zeeb #ifdef LKPI_80211_WME
43836b4cac81SBjoern A. Zeeb 	    IEEE80211_C_WME |
43844a67f1dfSBjoern A. Zeeb #endif
43856b4cac81SBjoern A. Zeeb #if 0
43866b4cac81SBjoern A. Zeeb 	    IEEE80211_C_PMGT |
43876b4cac81SBjoern A. Zeeb #endif
43886b4cac81SBjoern A. Zeeb 	    IEEE80211_C_SHSLOT |	/* short slot time supported */
43896b4cac81SBjoern A. Zeeb 	    IEEE80211_C_SHPREAMBLE	/* short preamble supported */
43906b4cac81SBjoern A. Zeeb 	    ;
43916b4cac81SBjoern A. Zeeb #if 0
43926b4cac81SBjoern A. Zeeb 	/* Scanning is a different kind of beast to re-work. */
43936b4cac81SBjoern A. Zeeb 	ic->ic_caps |= IEEE80211_C_BGSCAN;
43946b4cac81SBjoern A. Zeeb #endif
4395cc4e78d5SBjoern A. Zeeb 	if (lhw->ops->hw_scan) {
4396cc4e78d5SBjoern A. Zeeb 		/*
4397cc4e78d5SBjoern A. Zeeb 		 * Advertise full-offload scanning.
4398cc4e78d5SBjoern A. Zeeb 		 *
4399cc4e78d5SBjoern A. Zeeb 		 * Not limiting to SINGLE_SCAN_ON_ALL_BANDS here as otherwise
4400cc4e78d5SBjoern A. Zeeb 		 * we essentially disable hw_scan for all drivers not setting
4401cc4e78d5SBjoern A. Zeeb 		 * the flag.
4402cc4e78d5SBjoern A. Zeeb 		 */
44036b4cac81SBjoern A. Zeeb 		ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
4404a486fbbdSBjoern A. Zeeb 		lhw->scan_flags |= LKPI_LHW_SCAN_HW;
44056b4cac81SBjoern A. Zeeb 	}
44066b4cac81SBjoern A. Zeeb 
4407527687a9SBjoern A. Zeeb 	/*
4408527687a9SBjoern A. Zeeb 	 * The wiphy variables report bitmasks of avail antennas.
4409527687a9SBjoern A. Zeeb 	 * (*get_antenna) get the current bitmask sets which can be
4410527687a9SBjoern A. Zeeb 	 * altered by (*set_antenna) for some drivers.
4411527687a9SBjoern A. Zeeb 	 * XXX-BZ will the count alone do us much good long-term in net80211?
4412527687a9SBjoern A. Zeeb 	 */
4413527687a9SBjoern A. Zeeb 	if (hw->wiphy->available_antennas_rx ||
4414527687a9SBjoern A. Zeeb 	    hw->wiphy->available_antennas_tx) {
4415527687a9SBjoern A. Zeeb 		uint32_t rxs, txs;
4416527687a9SBjoern A. Zeeb 
4417527687a9SBjoern A. Zeeb 		if (lkpi_80211_mo_get_antenna(hw, &txs, &rxs) == 0) {
4418527687a9SBjoern A. Zeeb 			ic->ic_rxstream = bitcount32(rxs);
4419527687a9SBjoern A. Zeeb 			ic->ic_txstream = bitcount32(txs);
4420527687a9SBjoern A. Zeeb 		}
4421527687a9SBjoern A. Zeeb 	}
4422527687a9SBjoern A. Zeeb 
44236b4cac81SBjoern A. Zeeb 	ic->ic_cryptocaps = 0;
4424b35f6cd0SBjoern A. Zeeb #ifdef LKPI_80211_HW_CRYPTO
44256b4cac81SBjoern A. Zeeb 	if (hw->wiphy->n_cipher_suites > 0) {
44266b4cac81SBjoern A. Zeeb 		for (i = 0; i < hw->wiphy->n_cipher_suites; i++)
44276b4cac81SBjoern A. Zeeb 			ic->ic_cryptocaps |= lkpi_l80211_to_net80211_cyphers(
44286b4cac81SBjoern A. Zeeb 			    hw->wiphy->cipher_suites[i]);
44296b4cac81SBjoern A. Zeeb 	}
44306b4cac81SBjoern A. Zeeb #endif
44316b4cac81SBjoern A. Zeeb 
44326b4cac81SBjoern A. Zeeb 	lkpi_ic_getradiocaps(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans,
44336b4cac81SBjoern A. Zeeb 	    ic->ic_channels);
44346b4cac81SBjoern A. Zeeb 
44356b4cac81SBjoern A. Zeeb 	ieee80211_ifattach(ic);
44366b4cac81SBjoern A. Zeeb 
44376b4cac81SBjoern A. Zeeb 	ic->ic_update_mcast = lkpi_ic_update_mcast;
44386b4cac81SBjoern A. Zeeb 	ic->ic_update_promisc = lkpi_ic_update_promisc;
44396b4cac81SBjoern A. Zeeb 	ic->ic_update_chw = lkpi_ic_update_chw;
44406b4cac81SBjoern A. Zeeb 	ic->ic_parent = lkpi_ic_parent;
44416b4cac81SBjoern A. Zeeb 	ic->ic_scan_start = lkpi_ic_scan_start;
44426b4cac81SBjoern A. Zeeb 	ic->ic_scan_end = lkpi_ic_scan_end;
44436b4cac81SBjoern A. Zeeb 	ic->ic_set_channel = lkpi_ic_set_channel;
44446b4cac81SBjoern A. Zeeb 	ic->ic_transmit = lkpi_ic_transmit;
44456b4cac81SBjoern A. Zeeb 	ic->ic_raw_xmit = lkpi_ic_raw_xmit;
44466b4cac81SBjoern A. Zeeb 	ic->ic_vap_create = lkpi_ic_vap_create;
44476b4cac81SBjoern A. Zeeb 	ic->ic_vap_delete = lkpi_ic_vap_delete;
44486b4cac81SBjoern A. Zeeb 	ic->ic_getradiocaps = lkpi_ic_getradiocaps;
44496b4cac81SBjoern A. Zeeb 	ic->ic_wme.wme_update = lkpi_ic_wme_update;
44506b4cac81SBjoern A. Zeeb 
4451a486fbbdSBjoern A. Zeeb 	lhw->ic_scan_curchan = ic->ic_scan_curchan;
4452a486fbbdSBjoern A. Zeeb 	ic->ic_scan_curchan = lkpi_ic_scan_curchan;
4453a486fbbdSBjoern A. Zeeb 	lhw->ic_scan_mindwell = ic->ic_scan_mindwell;
4454a486fbbdSBjoern A. Zeeb 	ic->ic_scan_mindwell = lkpi_ic_scan_mindwell;
4455a486fbbdSBjoern A. Zeeb 
44566b4cac81SBjoern A. Zeeb 	lhw->ic_node_alloc = ic->ic_node_alloc;
44576b4cac81SBjoern A. Zeeb 	ic->ic_node_alloc = lkpi_ic_node_alloc;
44586b4cac81SBjoern A. Zeeb 	lhw->ic_node_init = ic->ic_node_init;
44596b4cac81SBjoern A. Zeeb 	ic->ic_node_init = lkpi_ic_node_init;
44606b4cac81SBjoern A. Zeeb 	lhw->ic_node_cleanup = ic->ic_node_cleanup;
44616b4cac81SBjoern A. Zeeb 	ic->ic_node_cleanup = lkpi_ic_node_cleanup;
44626b4cac81SBjoern A. Zeeb 	lhw->ic_node_free = ic->ic_node_free;
44636b4cac81SBjoern A. Zeeb 	ic->ic_node_free = lkpi_ic_node_free;
44646b4cac81SBjoern A. Zeeb 
44659fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
44669fb91463SBjoern A. Zeeb 	lhw->ic_recv_action = ic->ic_recv_action;
44679fb91463SBjoern A. Zeeb 	ic->ic_recv_action = lkpi_ic_recv_action;
44689fb91463SBjoern A. Zeeb 	lhw->ic_send_action = ic->ic_send_action;
44699fb91463SBjoern A. Zeeb 	ic->ic_send_action = lkpi_ic_send_action;
44709fb91463SBjoern A. Zeeb 
44719fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_enable = ic->ic_ampdu_enable;
44729fb91463SBjoern A. Zeeb 	ic->ic_ampdu_enable = lkpi_ic_ampdu_enable;
44739fb91463SBjoern A. Zeeb 
44749fb91463SBjoern A. Zeeb 	lhw->ic_addba_request = ic->ic_addba_request;
44759fb91463SBjoern A. Zeeb 	ic->ic_addba_request = lkpi_ic_addba_request;
44769fb91463SBjoern A. Zeeb 	lhw->ic_addba_response = ic->ic_addba_response;
44779fb91463SBjoern A. Zeeb 	ic->ic_addba_response = lkpi_ic_addba_response;
44789fb91463SBjoern A. Zeeb 	lhw->ic_addba_stop = ic->ic_addba_stop;
44799fb91463SBjoern A. Zeeb 	ic->ic_addba_stop = lkpi_ic_addba_stop;
44809fb91463SBjoern A. Zeeb 	lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout;
44819fb91463SBjoern A. Zeeb 	ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout;
44829fb91463SBjoern A. Zeeb 
44839fb91463SBjoern A. Zeeb 	lhw->ic_bar_response = ic->ic_bar_response;
44849fb91463SBjoern A. Zeeb 	ic->ic_bar_response = lkpi_ic_bar_response;
44859fb91463SBjoern A. Zeeb 
44869fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start;
44879fb91463SBjoern A. Zeeb 	ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start;
44889fb91463SBjoern A. Zeeb 	lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
44899fb91463SBjoern A. Zeeb 	ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop;
44909fb91463SBjoern A. Zeeb #endif
44919fb91463SBjoern A. Zeeb 
44926b4cac81SBjoern A. Zeeb 	lkpi_radiotap_attach(lhw);
44936b4cac81SBjoern A. Zeeb 
4494c5b96b3eSBjoern A. Zeeb 	/*
4495c5b96b3eSBjoern A. Zeeb 	 * Assign the first possible channel for now;  seems Realtek drivers
4496c5b96b3eSBjoern A. Zeeb 	 * expect one.
4497d9945d78SBjoern A. Zeeb 	 * Also remember the amount of bands we support and the most rates
4498d9945d78SBjoern A. Zeeb 	 * in any band so we can scale [(ext) sup rates] IE(s) accordingly.
4499c5b96b3eSBjoern A. Zeeb 	 */
4500d9945d78SBjoern A. Zeeb 	lhw->supbands = lhw->max_rates = 0;
4501f454a4a1SBjoern A. Zeeb 	for (band = 0; band < NUM_NL80211_BANDS; band++) {
4502c5b96b3eSBjoern A. Zeeb 		struct ieee80211_supported_band *supband;
4503c5b96b3eSBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *channels;
4504c5b96b3eSBjoern A. Zeeb 
4505c5b96b3eSBjoern A. Zeeb 		supband = hw->wiphy->bands[band];
4506c5b96b3eSBjoern A. Zeeb 		if (supband == NULL || supband->n_channels == 0)
4507c5b96b3eSBjoern A. Zeeb 			continue;
4508c5b96b3eSBjoern A. Zeeb 
4509d9945d78SBjoern A. Zeeb 		lhw->supbands++;
4510d9945d78SBjoern A. Zeeb 		lhw->max_rates = max(lhw->max_rates, supband->n_bitrates);
4511d9945d78SBjoern A. Zeeb 
4512f454a4a1SBjoern A. Zeeb 		/* If we have a channel, we need to keep counting supbands. */
4513f454a4a1SBjoern A. Zeeb 		if (hw->conf.chandef.chan != NULL)
4514f454a4a1SBjoern A. Zeeb 			continue;
4515f454a4a1SBjoern A. Zeeb 
4516c5b96b3eSBjoern A. Zeeb 		channels = supband->channels;
4517c5b96b3eSBjoern A. Zeeb 		for (i = 0; i < supband->n_channels; i++) {
4518c5b96b3eSBjoern A. Zeeb 
4519c5b96b3eSBjoern A. Zeeb 			if (channels[i].flags & IEEE80211_CHAN_DISABLED)
4520c5b96b3eSBjoern A. Zeeb 				continue;
4521c5b96b3eSBjoern A. Zeeb 
45224a07abdeSBjoern A. Zeeb 			cfg80211_chandef_create(&hw->conf.chandef, &channels[i],
45239fb91463SBjoern A. Zeeb #ifdef LKPI_80211_HT
45249fb91463SBjoern A. Zeeb 			    (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
45259fb91463SBjoern A. Zeeb #endif
4526c5b96b3eSBjoern A. Zeeb 			    NL80211_CHAN_NO_HT);
4527c5b96b3eSBjoern A. Zeeb 			break;
4528c5b96b3eSBjoern A. Zeeb 		}
4529c5b96b3eSBjoern A. Zeeb 	}
4530c5b96b3eSBjoern A. Zeeb 
4531d9945d78SBjoern A. Zeeb 	IMPROVE("see net80211::ieee80211_chan_init vs. wiphy->bands[].bitrates possibly in lkpi_ic_getradiocaps?");
4532d9945d78SBjoern A. Zeeb 
4533d9945d78SBjoern A. Zeeb 	/* Make sure we do not support more than net80211 is willing to take. */
4534d9945d78SBjoern A. Zeeb 	if (lhw->max_rates > IEEE80211_RATE_MAXSIZE) {
4535d9945d78SBjoern A. Zeeb 		ic_printf(ic, "%s: limiting max_rates %d to %d!\n", __func__,
4536d9945d78SBjoern A. Zeeb 		    lhw->max_rates, IEEE80211_RATE_MAXSIZE);
4537d9945d78SBjoern A. Zeeb 		lhw->max_rates = IEEE80211_RATE_MAXSIZE;
4538d9945d78SBjoern A. Zeeb 	}
4539d9945d78SBjoern A. Zeeb 
4540d9945d78SBjoern A. Zeeb 	/*
4541d9945d78SBjoern A. Zeeb 	 * The maximum supported bitrates on any band + size for
4542d9945d78SBjoern A. Zeeb 	 * DSSS Parameter Set give our per-band IE size.
4543d9945d78SBjoern A. Zeeb 	 * SSID is the responsibility of the driver and goes on the side.
4544d9945d78SBjoern A. Zeeb 	 * The user specified bits coming from the vap go into the
4545d9945d78SBjoern A. Zeeb 	 * "common ies" fields.
4546d9945d78SBjoern A. Zeeb 	 */
4547d9945d78SBjoern A. Zeeb 	lhw->scan_ie_len = 2 + IEEE80211_RATE_SIZE;
4548d9945d78SBjoern A. Zeeb 	if (lhw->max_rates > IEEE80211_RATE_SIZE)
4549d9945d78SBjoern A. Zeeb 		lhw->scan_ie_len += 2 + (lhw->max_rates - IEEE80211_RATE_SIZE);
455078ca45dfSBjoern A. Zeeb 
455178ca45dfSBjoern A. Zeeb 	if (hw->wiphy->features & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) {
4552d9945d78SBjoern A. Zeeb 		/*
455378ca45dfSBjoern A. Zeeb 		 * net80211 does not seem to support the DSSS Parameter Set but
455478ca45dfSBjoern A. Zeeb 		 * some of the drivers insert it so calculate the extra fixed
455578ca45dfSBjoern A. Zeeb 		 * space in.
4556d9945d78SBjoern A. Zeeb 		 */
4557d9945d78SBjoern A. Zeeb 		lhw->scan_ie_len += 2 + 1;
455878ca45dfSBjoern A. Zeeb 	}
4559d9945d78SBjoern A. Zeeb 
45609fb91463SBjoern A. Zeeb #if defined(LKPI_80211_HT)
45619fb91463SBjoern A. Zeeb 	if ((ic->ic_htcaps & IEEE80211_HTC_HT) != 0)
45629fb91463SBjoern A. Zeeb 		lhw->scan_ie_len += sizeof(struct ieee80211_ie_htcap);
45639fb91463SBjoern A. Zeeb #endif
45649fb91463SBjoern A. Zeeb #if defined(LKPI_80211_VHT)
45659fb91463SBjoern A. Zeeb 	if ((ic->ic_flags_ext & IEEE80211_FEXT_VHT) != 0)
45669fb91463SBjoern A. Zeeb 		lhw->scan_ie_len += 2 + sizeof(struct ieee80211_vht_cap);
45679fb91463SBjoern A. Zeeb #endif
45689fb91463SBjoern A. Zeeb 
4569d9945d78SBjoern A. Zeeb 	/* Reduce the max_scan_ie_len "left" by the amount we consume already. */
457078ca45dfSBjoern A. Zeeb 	if (hw->wiphy->max_scan_ie_len > 0) {
457178ca45dfSBjoern A. Zeeb 		if (lhw->scan_ie_len > hw->wiphy->max_scan_ie_len)
457278ca45dfSBjoern A. Zeeb 			goto err;
4573d9945d78SBjoern A. Zeeb 		hw->wiphy->max_scan_ie_len -= lhw->scan_ie_len;
457478ca45dfSBjoern A. Zeeb 	}
4575d9945d78SBjoern A. Zeeb 
45766b4cac81SBjoern A. Zeeb 	if (bootverbose)
45776b4cac81SBjoern A. Zeeb 		ieee80211_announce(ic);
45782e183d99SBjoern A. Zeeb 
45792e183d99SBjoern A. Zeeb 	return (0);
458078ca45dfSBjoern A. Zeeb err:
458178ca45dfSBjoern A. Zeeb 	IMPROVE("TODO FIXME CLEANUP");
458278ca45dfSBjoern A. Zeeb 	return (-EAGAIN);
45836b4cac81SBjoern A. Zeeb }
45846b4cac81SBjoern A. Zeeb 
45856b4cac81SBjoern A. Zeeb void
45866b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_ifdetach(struct ieee80211_hw *hw)
45876b4cac81SBjoern A. Zeeb {
45886b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
45896b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
45906b4cac81SBjoern A. Zeeb 
45916b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
45926b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
45936b4cac81SBjoern A. Zeeb 	ieee80211_ifdetach(ic);
45946b4cac81SBjoern A. Zeeb }
45956b4cac81SBjoern A. Zeeb 
45966b4cac81SBjoern A. Zeeb void
45976b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
45986b4cac81SBjoern A. Zeeb     enum ieee80211_iface_iter flags,
45996b4cac81SBjoern A. Zeeb     void(*iterfunc)(void *, uint8_t *, struct ieee80211_vif *),
46006b4cac81SBjoern A. Zeeb     void *arg)
46016b4cac81SBjoern A. Zeeb {
46026b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
46036b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
46046b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
460561a68e50SBjoern A. Zeeb 	bool active, atomic, nin_drv;
46066b4cac81SBjoern A. Zeeb 
46076b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
46086b4cac81SBjoern A. Zeeb 
46096b4cac81SBjoern A. Zeeb 	if (flags & ~(IEEE80211_IFACE_ITER_NORMAL|
46106b4cac81SBjoern A. Zeeb 	    IEEE80211_IFACE_ITER_RESUME_ALL|
461161a68e50SBjoern A. Zeeb 	    IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER|
4612adff403fSBjoern A. Zeeb 	    IEEE80211_IFACE_ITER_ACTIVE|IEEE80211_IFACE_ITER__ATOMIC)) {
46136b4cac81SBjoern A. Zeeb 		ic_printf(lhw->ic, "XXX TODO %s flags(%#x) not yet supported.\n",
46146b4cac81SBjoern A. Zeeb 		    __func__, flags);
46156b4cac81SBjoern A. Zeeb 	}
46166b4cac81SBjoern A. Zeeb 
4617adff403fSBjoern A. Zeeb 	active = (flags & IEEE80211_IFACE_ITER_ACTIVE) != 0;
46186b4cac81SBjoern A. Zeeb 	atomic = (flags & IEEE80211_IFACE_ITER__ATOMIC) != 0;
461961a68e50SBjoern A. Zeeb 	nin_drv = (flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) != 0;
46206b4cac81SBjoern A. Zeeb 
46216b4cac81SBjoern A. Zeeb 	if (atomic)
46228891c455SBjoern A. Zeeb 		LKPI_80211_LHW_LVIF_LOCK(lhw);
46236b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
46246b4cac81SBjoern A. Zeeb 		struct ieee80211vap *vap;
46256b4cac81SBjoern A. Zeeb 
46266b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
46276b4cac81SBjoern A. Zeeb 
46286b4cac81SBjoern A. Zeeb 		/*
46296b4cac81SBjoern A. Zeeb 		 * If we want "active" interfaces, we need to distinguish on
46306b4cac81SBjoern A. Zeeb 		 * whether the driver knows about them or not to be able to
46316b4cac81SBjoern A. Zeeb 		 * handle the "resume" case correctly.  Skip the ones the
46326b4cac81SBjoern A. Zeeb 		 * driver does not know about.
46336b4cac81SBjoern A. Zeeb 		 */
46346b4cac81SBjoern A. Zeeb 		if (active && !lvif->added_to_drv &&
46356b4cac81SBjoern A. Zeeb 		    (flags & IEEE80211_IFACE_ITER_RESUME_ALL) != 0)
46366b4cac81SBjoern A. Zeeb 			continue;
46376b4cac81SBjoern A. Zeeb 
46386b4cac81SBjoern A. Zeeb 		/*
463961a68e50SBjoern A. Zeeb 		 * If we shall skip interfaces not added to the driver do so
464061a68e50SBjoern A. Zeeb 		 * if we haven't yet.
464161a68e50SBjoern A. Zeeb 		 */
464261a68e50SBjoern A. Zeeb 		if (nin_drv && !lvif->added_to_drv)
464361a68e50SBjoern A. Zeeb 			continue;
464461a68e50SBjoern A. Zeeb 
464561a68e50SBjoern A. Zeeb 		/*
46466b4cac81SBjoern A. Zeeb 		 * Run the iterator function if we are either not asking
46476b4cac81SBjoern A. Zeeb 		 * asking for active only or if the VAP is "running".
46486b4cac81SBjoern A. Zeeb 		 */
46496b4cac81SBjoern A. Zeeb 		/* XXX-BZ probably should have state in the lvif as well. */
46506b4cac81SBjoern A. Zeeb 		vap = LVIF_TO_VAP(lvif);
46516b4cac81SBjoern A. Zeeb 		if (!active || (vap->iv_state != IEEE80211_S_INIT))
46526b4cac81SBjoern A. Zeeb 			iterfunc(arg, vif->addr, vif);
46536b4cac81SBjoern A. Zeeb 	}
46546b4cac81SBjoern A. Zeeb 	if (atomic)
46558891c455SBjoern A. Zeeb 		LKPI_80211_LHW_LVIF_UNLOCK(lhw);
46566b4cac81SBjoern A. Zeeb }
46576b4cac81SBjoern A. Zeeb 
46586b4cac81SBjoern A. Zeeb void
46596b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_keys(struct ieee80211_hw *hw,
46606b4cac81SBjoern A. Zeeb     struct ieee80211_vif *vif,
46616b4cac81SBjoern A. Zeeb     void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_vif *,
46626b4cac81SBjoern A. Zeeb         struct ieee80211_sta *, struct ieee80211_key_conf *, void *),
46636b4cac81SBjoern A. Zeeb     void *arg)
46646b4cac81SBjoern A. Zeeb {
46656b4cac81SBjoern A. Zeeb 
46666b4cac81SBjoern A. Zeeb 	UNIMPLEMENTED;
46676b4cac81SBjoern A. Zeeb }
46686b4cac81SBjoern A. Zeeb 
46696b4cac81SBjoern A. Zeeb void
46706b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
46716b4cac81SBjoern A. Zeeb     void(*iterfunc)(struct ieee80211_hw *, struct ieee80211_chanctx_conf *,
46726b4cac81SBjoern A. Zeeb 	void *),
46736b4cac81SBjoern A. Zeeb     void *arg)
46746b4cac81SBjoern A. Zeeb {
4675c5e25798SBjoern A. Zeeb 	struct lkpi_hw *lhw;
4676c5e25798SBjoern A. Zeeb 	struct lkpi_vif *lvif;
4677c5e25798SBjoern A. Zeeb 	struct ieee80211_vif *vif;
4678c5e25798SBjoern A. Zeeb 	struct lkpi_chanctx *lchanctx;
46796b4cac81SBjoern A. Zeeb 
4680c5e25798SBjoern A. Zeeb 	KASSERT(hw != NULL && iterfunc != NULL,
4681c5e25798SBjoern A. Zeeb 	    ("%s: hw %p iterfunc %p arg %p\n", __func__, hw, iterfunc, arg));
4682c5e25798SBjoern A. Zeeb 
4683c5e25798SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
4684c5e25798SBjoern A. Zeeb 
4685c5e25798SBjoern A. Zeeb 	IMPROVE("lchanctx should be its own list somewhere");
4686c5e25798SBjoern A. Zeeb 
4687c5e25798SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
4688c5e25798SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
4689c5e25798SBjoern A. Zeeb 
4690c5e25798SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
4691c5e25798SBjoern A. Zeeb 		if (vif->chanctx_conf == NULL)
4692c5e25798SBjoern A. Zeeb 			continue;
4693c5e25798SBjoern A. Zeeb 
4694c5e25798SBjoern A. Zeeb 		lchanctx = CHANCTX_CONF_TO_LCHANCTX(vif->chanctx_conf);
4695c5e25798SBjoern A. Zeeb 		if (!lchanctx->added_to_drv)
4696c5e25798SBjoern A. Zeeb 			continue;
4697c5e25798SBjoern A. Zeeb 
4698c5e25798SBjoern A. Zeeb 		iterfunc(hw, &lchanctx->conf, arg);
4699c5e25798SBjoern A. Zeeb 	}
4700c5e25798SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
47016b4cac81SBjoern A. Zeeb }
47026b4cac81SBjoern A. Zeeb 
47036b4cac81SBjoern A. Zeeb void
47046b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
47056b4cac81SBjoern A. Zeeb    void (*iterfunc)(void *, struct ieee80211_sta *), void *arg)
47066b4cac81SBjoern A. Zeeb {
47076b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47086b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
47096b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
47106b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
47116b4cac81SBjoern A. Zeeb 
47126b4cac81SBjoern A. Zeeb 	KASSERT(hw != NULL && iterfunc != NULL,
47136b4cac81SBjoern A. Zeeb 	    ("%s: hw %p iterfunc %p arg %p\n", __func__, hw, iterfunc, arg));
47146b4cac81SBjoern A. Zeeb 
47156b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
47166b4cac81SBjoern A. Zeeb 
47178891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
47186b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
47196b4cac81SBjoern A. Zeeb 
47206b4cac81SBjoern A. Zeeb 		LKPI_80211_LVIF_LOCK(lvif);
47216b4cac81SBjoern A. Zeeb 		TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
47226b4cac81SBjoern A. Zeeb 			if (!lsta->added_to_drv)
47236b4cac81SBjoern A. Zeeb 				continue;
47246b4cac81SBjoern A. Zeeb 			sta = LSTA_TO_STA(lsta);
47256b4cac81SBjoern A. Zeeb 			iterfunc(arg, sta);
47266b4cac81SBjoern A. Zeeb 		}
47276b4cac81SBjoern A. Zeeb 		LKPI_80211_LVIF_UNLOCK(lvif);
47286b4cac81SBjoern A. Zeeb 	}
47298891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
47306b4cac81SBjoern A. Zeeb }
47316b4cac81SBjoern A. Zeeb 
4732673d62fcSBjoern A. Zeeb struct linuxkpi_ieee80211_regdomain *
4733673d62fcSBjoern A. Zeeb lkpi_get_linuxkpi_ieee80211_regdomain(size_t n)
4734673d62fcSBjoern A. Zeeb {
4735673d62fcSBjoern A. Zeeb 	struct linuxkpi_ieee80211_regdomain *regd;
4736673d62fcSBjoern A. Zeeb 
4737673d62fcSBjoern A. Zeeb 	regd = kzalloc(sizeof(*regd) + n * sizeof(struct ieee80211_reg_rule),
4738673d62fcSBjoern A. Zeeb 	    GFP_KERNEL);
4739673d62fcSBjoern A. Zeeb 	return (regd);
4740673d62fcSBjoern A. Zeeb }
4741673d62fcSBjoern A. Zeeb 
47426b4cac81SBjoern A. Zeeb int
47436b4cac81SBjoern A. Zeeb linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
47446b4cac81SBjoern A. Zeeb     struct linuxkpi_ieee80211_regdomain *regd)
47456b4cac81SBjoern A. Zeeb {
47466b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47476b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
47486b4cac81SBjoern A. Zeeb 	struct ieee80211_regdomain *rd;
47496b4cac81SBjoern A. Zeeb 
47506b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
47516b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
47526b4cac81SBjoern A. Zeeb 
47536b4cac81SBjoern A. Zeeb 	rd = &ic->ic_regdomain;
47546b4cac81SBjoern A. Zeeb 	if (rd->isocc[0] == '\0') {
47556b4cac81SBjoern A. Zeeb 		rd->isocc[0] = regd->alpha2[0];
47566b4cac81SBjoern A. Zeeb 		rd->isocc[1] = regd->alpha2[1];
47576b4cac81SBjoern A. Zeeb 	}
47586b4cac81SBjoern A. Zeeb 
47596b4cac81SBjoern A. Zeeb 	TODO();
47606b4cac81SBjoern A. Zeeb 	/* XXX-BZ finish the rest. */
47616b4cac81SBjoern A. Zeeb 
47626b4cac81SBjoern A. Zeeb 	return (0);
47636b4cac81SBjoern A. Zeeb }
47646b4cac81SBjoern A. Zeeb 
47656b4cac81SBjoern A. Zeeb void
47666b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw,
47676b4cac81SBjoern A. Zeeb     struct cfg80211_scan_info *info)
47686b4cac81SBjoern A. Zeeb {
47696b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47706b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
47716b4cac81SBjoern A. Zeeb 	struct ieee80211_scan_state *ss;
47726b4cac81SBjoern A. Zeeb 
47736b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
47746b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
47756b4cac81SBjoern A. Zeeb 	ss = ic->ic_scan;
47766b4cac81SBjoern A. Zeeb 
47776b4cac81SBjoern A. Zeeb 	ieee80211_scan_done(ss->ss_vap);
47786b4cac81SBjoern A. Zeeb 
47798ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_LOCK(lhw);
47806b4cac81SBjoern A. Zeeb 	free(lhw->hw_req, M_LKPI80211);
47816b4cac81SBjoern A. Zeeb 	lhw->hw_req = NULL;
4782a486fbbdSBjoern A. Zeeb 	lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
47836b4cac81SBjoern A. Zeeb 	wakeup(lhw);
47848ac540d3SBjoern A. Zeeb 	LKPI_80211_LHW_SCAN_UNLOCK(lhw);
47856b4cac81SBjoern A. Zeeb 
47866b4cac81SBjoern A. Zeeb 	return;
47876b4cac81SBjoern A. Zeeb }
47886b4cac81SBjoern A. Zeeb 
4789e30e05d3SBjoern A. Zeeb /* For %list see comment towards the end of the function. */
47906b4cac81SBjoern A. Zeeb void
47916b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
4792e30e05d3SBjoern A. Zeeb     struct ieee80211_sta *sta, struct napi_struct *napi __unused,
4793e30e05d3SBjoern A. Zeeb     struct list_head *list __unused)
47946b4cac81SBjoern A. Zeeb {
47956b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
47966b4cac81SBjoern A. Zeeb 	struct ieee80211com *ic;
47976b4cac81SBjoern A. Zeeb 	struct mbuf *m;
47986b4cac81SBjoern A. Zeeb 	struct skb_shared_info *shinfo;
47996b4cac81SBjoern A. Zeeb 	struct ieee80211_rx_status *rx_status;
48006b4cac81SBjoern A. Zeeb 	struct ieee80211_rx_stats rx_stats;
48016b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
48026b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
48036b4cac81SBjoern A. Zeeb 	struct ieee80211_hdr *hdr;
48046b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
48059d9ba2b7SBjoern A. Zeeb 	int i, offset, ok;
4806170acccfSBjoern A. Zeeb 	int8_t rssi;
4807c0cadd99SBjoern A. Zeeb 	bool is_beacon;
48086b4cac81SBjoern A. Zeeb 
48096b4cac81SBjoern A. Zeeb 	if (skb->len < 2) {
48106b4cac81SBjoern A. Zeeb 		/* Need 80211 stats here. */
48116b4cac81SBjoern A. Zeeb 		IMPROVE();
48126b4cac81SBjoern A. Zeeb 		goto err;
48136b4cac81SBjoern A. Zeeb 	}
48146b4cac81SBjoern A. Zeeb 
48156b4cac81SBjoern A. Zeeb 	/*
48166b4cac81SBjoern A. Zeeb 	 * For now do the data copy; we can later improve things. Might even
48176b4cac81SBjoern A. Zeeb 	 * have an mbuf backing the skb data then?
48186b4cac81SBjoern A. Zeeb 	 */
48196b4cac81SBjoern A. Zeeb 	m = m_get2(skb->len, M_NOWAIT, MT_DATA, M_PKTHDR);
48206b4cac81SBjoern A. Zeeb 	if (m == NULL)
48216b4cac81SBjoern A. Zeeb 		goto err;
48226b4cac81SBjoern A. Zeeb 	m_copyback(m, 0, skb->tail - skb->data, skb->data);
48236b4cac81SBjoern A. Zeeb 
48246b4cac81SBjoern A. Zeeb 	shinfo = skb_shinfo(skb);
48256b4cac81SBjoern A. Zeeb 	offset = m->m_len;
48266b4cac81SBjoern A. Zeeb 	for (i = 0; i < shinfo->nr_frags; i++) {
48276b4cac81SBjoern A. Zeeb 		m_copyback(m, offset, shinfo->frags[i].size,
48286b4cac81SBjoern A. Zeeb 		    (uint8_t *)linux_page_address(shinfo->frags[i].page) +
48296b4cac81SBjoern A. Zeeb 		    shinfo->frags[i].offset);
48306b4cac81SBjoern A. Zeeb 		offset += shinfo->frags[i].size;
48316b4cac81SBjoern A. Zeeb 	}
48326b4cac81SBjoern A. Zeeb 
48336b4cac81SBjoern A. Zeeb 	rx_status = IEEE80211_SKB_RXCB(skb);
48346b4cac81SBjoern A. Zeeb 
48356b4cac81SBjoern A. Zeeb 	hdr = (void *)skb->data;
4836c0cadd99SBjoern A. Zeeb 	is_beacon = ieee80211_is_beacon(hdr->frame_control);
4837c0cadd99SBjoern A. Zeeb 
4838c0cadd99SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
48399d9ba2b7SBjoern A. Zeeb 	if (is_beacon && (linuxkpi_debug_80211 & D80211_TRACE_RX_BEACONS) == 0)
48406b4cac81SBjoern A. Zeeb 		goto no_trace_beacons;
48416b4cac81SBjoern A. Zeeb 
48429d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
48436b4cac81SBjoern A. Zeeb 		printf("TRACE-RX: %s: skb %p a/l/d/t-len (%u/%u/%u/%u) "
4844c0cadd99SBjoern A. Zeeb 		    "h %p d %p t %p e %p sh %p (%u) m %p plen %u len %u%s\n",
48456b4cac81SBjoern A. Zeeb 		    __func__, skb, skb->_alloc_len, skb->len, skb->data_len,
48466b4cac81SBjoern A. Zeeb 		    skb->truesize, skb->head, skb->data, skb->tail, skb->end,
48476b4cac81SBjoern A. Zeeb 		    shinfo, shinfo->nr_frags,
4848c0cadd99SBjoern A. Zeeb 		    m, m->m_pkthdr.len, m->m_len, is_beacon ? " beacon" : "");
48496b4cac81SBjoern A. Zeeb 
48509d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX_DUMP)
48516b4cac81SBjoern A. Zeeb 		hexdump(mtod(m, const void *), m->m_len, "RX (raw) ", 0);
48526b4cac81SBjoern A. Zeeb 
48536b4cac81SBjoern A. Zeeb 	/* Implement a dump_rxcb() !!! */
48549d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
4855c8dafefaSBjoern A. Zeeb 		printf("TRACE %s: RXCB: %ju %ju %u, %#0x, %u, %#0x, %#0x, "
485660970a32SBjoern A. Zeeb 		    "%u band %u, %u { %d %d %d %d }, %d, %#x %#x %#x %#x %u %u %u\n",
48576b4cac81SBjoern A. Zeeb 			__func__,
4858c8dafefaSBjoern A. Zeeb 			(uintmax_t)rx_status->boottime_ns,
4859c8dafefaSBjoern A. Zeeb 			(uintmax_t)rx_status->mactime,
48606b4cac81SBjoern A. Zeeb 			rx_status->device_timestamp,
48616b4cac81SBjoern A. Zeeb 			rx_status->flag,
48626b4cac81SBjoern A. Zeeb 			rx_status->freq,
48636b4cac81SBjoern A. Zeeb 			rx_status->bw,
48646b4cac81SBjoern A. Zeeb 			rx_status->encoding,
48656b4cac81SBjoern A. Zeeb 			rx_status->ampdu_reference,
48666b4cac81SBjoern A. Zeeb 			rx_status->band,
48676b4cac81SBjoern A. Zeeb 			rx_status->chains,
48686b4cac81SBjoern A. Zeeb 			rx_status->chain_signal[0],
48696b4cac81SBjoern A. Zeeb 			rx_status->chain_signal[1],
48706b4cac81SBjoern A. Zeeb 			rx_status->chain_signal[2],
487160970a32SBjoern A. Zeeb 			rx_status->chain_signal[3],
48726b4cac81SBjoern A. Zeeb 			rx_status->signal,
48736b4cac81SBjoern A. Zeeb 			rx_status->enc_flags,
48746b4cac81SBjoern A. Zeeb 			rx_status->he_dcm,
48756b4cac81SBjoern A. Zeeb 			rx_status->he_gi,
48766b4cac81SBjoern A. Zeeb 			rx_status->he_ru,
48776b4cac81SBjoern A. Zeeb 			rx_status->zero_length_psdu_type,
48786b4cac81SBjoern A. Zeeb 			rx_status->nss,
48796b4cac81SBjoern A. Zeeb 			rx_status->rate_idx);
48806b4cac81SBjoern A. Zeeb no_trace_beacons:
48816b4cac81SBjoern A. Zeeb #endif
48826b4cac81SBjoern A. Zeeb 
48836b4cac81SBjoern A. Zeeb 	memset(&rx_stats, 0, sizeof(rx_stats));
48846b4cac81SBjoern A. Zeeb 	rx_stats.r_flags = IEEE80211_R_NF | IEEE80211_R_RSSI;
488560970a32SBjoern A. Zeeb 	/* XXX-BZ correct hardcoded rssi and noise floor, how? survey? */
488660970a32SBjoern A. Zeeb 	rx_stats.c_nf = -96;
48876b4cac81SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, SIGNAL_DBM) &&
48886b4cac81SBjoern A. Zeeb 	    !(rx_status->flag & RX_FLAG_NO_SIGNAL_VAL))
4889170acccfSBjoern A. Zeeb 		rssi = rx_status->signal;
48906b4cac81SBjoern A. Zeeb 	else
4891170acccfSBjoern A. Zeeb 		rssi = rx_stats.c_nf;
4892170acccfSBjoern A. Zeeb 	/*
4893170acccfSBjoern A. Zeeb 	 * net80211 signal strength data are in .5 dBm units relative to
4894170acccfSBjoern A. Zeeb 	 * the current noise floor (see comment in ieee80211_node.h).
4895170acccfSBjoern A. Zeeb 	 */
4896170acccfSBjoern A. Zeeb 	rssi -= rx_stats.c_nf;
4897170acccfSBjoern A. Zeeb 	rx_stats.c_rssi = rssi * 2;
48986b4cac81SBjoern A. Zeeb 	rx_stats.r_flags |= IEEE80211_R_BAND;
48996b4cac81SBjoern A. Zeeb 	rx_stats.c_band =
49006b4cac81SBjoern A. Zeeb 	    lkpi_nl80211_band_to_net80211_band(rx_status->band);
49016b4cac81SBjoern A. Zeeb 	rx_stats.r_flags |= IEEE80211_R_FREQ | IEEE80211_R_IEEE;
49026b4cac81SBjoern A. Zeeb 	rx_stats.c_freq = rx_status->freq;
49036b4cac81SBjoern A. Zeeb 	rx_stats.c_ieee = ieee80211_mhz2ieee(rx_stats.c_freq, rx_stats.c_band);
49046b4cac81SBjoern A. Zeeb 
49056b4cac81SBjoern A. Zeeb 	/* XXX (*sta_statistics)() to get to some of that? */
49066b4cac81SBjoern A. Zeeb 	/* XXX-BZ dump the FreeBSD version of rx_stats as well! */
49076b4cac81SBjoern A. Zeeb 
49086b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
49096b4cac81SBjoern A. Zeeb 	ic = lhw->ic;
49106b4cac81SBjoern A. Zeeb 
49116b4cac81SBjoern A. Zeeb 	ok = ieee80211_add_rx_params(m, &rx_stats);
49126b4cac81SBjoern A. Zeeb 	if (ok == 0) {
4913dbc06dd9SBjoern A. Zeeb 		m_freem(m);
49146b4cac81SBjoern A. Zeeb 		counter_u64_add(ic->ic_ierrors, 1);
49156b4cac81SBjoern A. Zeeb 		goto err;
49166b4cac81SBjoern A. Zeeb 	}
49176b4cac81SBjoern A. Zeeb 
49186b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
49196b4cac81SBjoern A. Zeeb 		lsta = STA_TO_LSTA(sta);
49206b4cac81SBjoern A. Zeeb 		ni = ieee80211_ref_node(lsta->ni);
49216b4cac81SBjoern A. Zeeb 	} else {
4922c8dafefaSBjoern A. Zeeb 		struct ieee80211_frame_min *wh;
4923c8dafefaSBjoern A. Zeeb 
49246b4cac81SBjoern A. Zeeb 		wh = mtod(m, struct ieee80211_frame_min *);
49256b4cac81SBjoern A. Zeeb 		ni = ieee80211_find_rxnode(ic, wh);
49266b4cac81SBjoern A. Zeeb 		if (ni != NULL)
49276b4cac81SBjoern A. Zeeb 			lsta = ni->ni_drv_data;
49286b4cac81SBjoern A. Zeeb 	}
49296b4cac81SBjoern A. Zeeb 
49306b4cac81SBjoern A. Zeeb 	if (ni != NULL)
49316b4cac81SBjoern A. Zeeb 		vap = ni->ni_vap;
49326b4cac81SBjoern A. Zeeb 	else
49336b4cac81SBjoern A. Zeeb 		/*
49346b4cac81SBjoern A. Zeeb 		 * XXX-BZ can we improve this by looking at the frame hdr
49356b4cac81SBjoern A. Zeeb 		 * or other meta-data passed up?
49366b4cac81SBjoern A. Zeeb 		 */
49376b4cac81SBjoern A. Zeeb 		vap = TAILQ_FIRST(&ic->ic_vaps);
49386b4cac81SBjoern A. Zeeb 
49399d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
49409d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
4941c0cadd99SBjoern A. Zeeb 		printf("TRACE %s: sta %p lsta %p state %d ni %p vap %p%s\n",
4942c0cadd99SBjoern A. Zeeb 		    __func__, sta, lsta, (lsta != NULL) ? lsta->state : -1,
4943c0cadd99SBjoern A. Zeeb 		    ni, vap, is_beacon ? " beacon" : "");
49449d9ba2b7SBjoern A. Zeeb #endif
49456b4cac81SBjoern A. Zeeb 
4946c0cadd99SBjoern A. Zeeb 	if (ni != NULL && vap != NULL && is_beacon &&
4947c8dafefaSBjoern A. Zeeb 	    rx_status->device_timestamp > 0 &&
4948c8dafefaSBjoern A. Zeeb 	    m->m_pkthdr.len >= sizeof(struct ieee80211_frame)) {
4949c8dafefaSBjoern A. Zeeb 		struct lkpi_vif *lvif;
4950c8dafefaSBjoern A. Zeeb 		struct ieee80211_vif *vif;
4951c8dafefaSBjoern A. Zeeb 		struct ieee80211_frame *wh;
4952c8dafefaSBjoern A. Zeeb 
4953c8dafefaSBjoern A. Zeeb 		wh = mtod(m, struct ieee80211_frame *);
4954c8dafefaSBjoern A. Zeeb 		if (!IEEE80211_ADDR_EQ(wh->i_addr2, ni->ni_bssid))
4955c8dafefaSBjoern A. Zeeb 			goto skip_device_ts;
4956c8dafefaSBjoern A. Zeeb 
4957c8dafefaSBjoern A. Zeeb 		lvif = VAP_TO_LVIF(vap);
4958c8dafefaSBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
4959c8dafefaSBjoern A. Zeeb 
4960c8dafefaSBjoern A. Zeeb 		IMPROVE("TIMING_BEACON_ONLY?");
4961c8dafefaSBjoern A. Zeeb 		/* mac80211 specific (not net80211) so keep it here. */
4962c8dafefaSBjoern A. Zeeb 		vif->bss_conf.sync_device_ts = rx_status->device_timestamp;
4963c8dafefaSBjoern A. Zeeb 		/*
4964c8dafefaSBjoern A. Zeeb 		 * net80211 should take care of the other information (sync_tsf,
4965c8dafefaSBjoern A. Zeeb 		 * sync_dtim_count) as otherwise we need to parse the beacon.
4966c8dafefaSBjoern A. Zeeb 		 */
4967c8dafefaSBjoern A. Zeeb skip_device_ts:
49689fb91463SBjoern A. Zeeb 		;
49699fb91463SBjoern A. Zeeb 	}
4970c8dafefaSBjoern A. Zeeb 
49716b4cac81SBjoern A. Zeeb 	if (vap != NULL && vap->iv_state > IEEE80211_S_INIT &&
49726b4cac81SBjoern A. Zeeb 	    ieee80211_radiotap_active_vap(vap)) {
49736b4cac81SBjoern A. Zeeb 		struct lkpi_radiotap_rx_hdr *rtap;
49746b4cac81SBjoern A. Zeeb 
49756b4cac81SBjoern A. Zeeb 		rtap = &lhw->rtap_rx;
49766b4cac81SBjoern A. Zeeb 		rtap->wr_tsft = rx_status->device_timestamp;
49776b4cac81SBjoern A. Zeeb 		rtap->wr_flags = 0;
49786b4cac81SBjoern A. Zeeb 		if (rx_status->enc_flags & RX_ENC_FLAG_SHORTPRE)
49796b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE;
49806b4cac81SBjoern A. Zeeb 		if (rx_status->enc_flags & RX_ENC_FLAG_SHORT_GI)
49816b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTGI;
49826b4cac81SBjoern A. Zeeb #if 0	/* .. or it does not given we strip it below. */
49836b4cac81SBjoern A. Zeeb 		if (ieee80211_hw_check(hw, RX_INCLUDES_FCS))
49846b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_FCS;
49856b4cac81SBjoern A. Zeeb #endif
49866b4cac81SBjoern A. Zeeb 		if (rx_status->flag & RX_FLAG_FAILED_FCS_CRC)
49876b4cac81SBjoern A. Zeeb 			rtap->wr_flags |= IEEE80211_RADIOTAP_F_BADFCS;
49886b4cac81SBjoern A. Zeeb 		rtap->wr_rate = 0;
49896b4cac81SBjoern A. Zeeb 		IMPROVE();
49906b4cac81SBjoern A. Zeeb 		/* XXX TODO status->encoding / rate_index / bw */
49916b4cac81SBjoern A. Zeeb 		rtap->wr_chan_freq = htole16(rx_stats.c_freq);
49926b4cac81SBjoern A. Zeeb 		if (ic->ic_curchan->ic_ieee == rx_stats.c_ieee)
49936b4cac81SBjoern A. Zeeb 			rtap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags);
4994170acccfSBjoern A. Zeeb 		rtap->wr_dbm_antsignal = rssi;
49956b4cac81SBjoern A. Zeeb 		rtap->wr_dbm_antnoise = rx_stats.c_nf;
49966b4cac81SBjoern A. Zeeb 	}
49976b4cac81SBjoern A. Zeeb 
49986b4cac81SBjoern A. Zeeb 	if (ieee80211_hw_check(hw, RX_INCLUDES_FCS))
49996b4cac81SBjoern A. Zeeb 		m_adj(m, -IEEE80211_CRC_LEN);
50006b4cac81SBjoern A. Zeeb 
5001e30e05d3SBjoern A. Zeeb #if 0
5002e30e05d3SBjoern A. Zeeb 	if (list != NULL) {
5003e30e05d3SBjoern A. Zeeb 		/*
5004e30e05d3SBjoern A. Zeeb 		* Normally this would be queued up and delivered by
5005e30e05d3SBjoern A. Zeeb 		* netif_receive_skb_list(), napi_gro_receive(), or the like.
5006e30e05d3SBjoern A. Zeeb 		* See mt76::mac80211.c as only current possible consumer.
5007e30e05d3SBjoern A. Zeeb 		*/
5008e30e05d3SBjoern A. Zeeb 		IMPROVE("we simply pass the packet to net80211 to deal with.");
5009e30e05d3SBjoern A. Zeeb 	}
5010e30e05d3SBjoern A. Zeeb #endif
5011e30e05d3SBjoern A. Zeeb 
50126b4cac81SBjoern A. Zeeb 	if (ni != NULL) {
50139d9ba2b7SBjoern A. Zeeb 		ok = ieee80211_input_mimo(ni, m);
50146b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
5015dbc06dd9SBjoern A. Zeeb 		if (ok < 0)
5016dbc06dd9SBjoern A. Zeeb 			m_freem(m);
50176b4cac81SBjoern A. Zeeb 	} else {
50189d9ba2b7SBjoern A. Zeeb 		ok = ieee80211_input_mimo_all(ic, m);
5019dbc06dd9SBjoern A. Zeeb 		/* mbuf got consumed. */
50206b4cac81SBjoern A. Zeeb 	}
50216b4cac81SBjoern A. Zeeb 
50229d9ba2b7SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
50239d9ba2b7SBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_RX)
50249d9ba2b7SBjoern A. Zeeb 		printf("TRACE %s: handled frame type %#0x\n", __func__, ok);
50259d9ba2b7SBjoern A. Zeeb #endif
50266b4cac81SBjoern A. Zeeb 
50276b4cac81SBjoern A. Zeeb 	IMPROVE();
50286b4cac81SBjoern A. Zeeb 
50296b4cac81SBjoern A. Zeeb err:
50306b4cac81SBjoern A. Zeeb 	/* The skb is ours so we can free it :-) */
50316b4cac81SBjoern A. Zeeb 	kfree_skb(skb);
50326b4cac81SBjoern A. Zeeb }
50336b4cac81SBjoern A. Zeeb 
50346b4cac81SBjoern A. Zeeb uint8_t
5035ec190d91SBjoern A. Zeeb linuxkpi_ieee80211_get_tid(struct ieee80211_hdr *hdr, bool nonqos_ok)
50366b4cac81SBjoern A. Zeeb {
50376b4cac81SBjoern A. Zeeb 	const struct ieee80211_frame *wh;
5038ec190d91SBjoern A. Zeeb 	uint8_t tid;
5039ec190d91SBjoern A. Zeeb 
5040ec190d91SBjoern A. Zeeb 	/* Linux seems to assume this is a QOS-Data-Frame */
5041ec190d91SBjoern A. Zeeb 	KASSERT(nonqos_ok || ieee80211_is_data_qos(hdr->frame_control),
5042ec190d91SBjoern A. Zeeb 	   ("%s: hdr %p fc %#06x not qos_data\n", __func__, hdr,
5043ec190d91SBjoern A. Zeeb 	   hdr->frame_control));
50446b4cac81SBjoern A. Zeeb 
50456b4cac81SBjoern A. Zeeb 	wh = (const struct ieee80211_frame *)hdr;
5046ec190d91SBjoern A. Zeeb 	tid = ieee80211_gettid(wh);
5047ec190d91SBjoern A. Zeeb 	KASSERT(nonqos_ok || tid == (tid & IEEE80211_QOS_TID), ("%s: tid %u "
5048ec190d91SBjoern A. Zeeb 	   "not expected (%u?)\n", __func__, tid, IEEE80211_NONQOS_TID));
5049ec190d91SBjoern A. Zeeb 
5050ec190d91SBjoern A. Zeeb 	return (tid);
50516b4cac81SBjoern A. Zeeb }
50526b4cac81SBjoern A. Zeeb 
50536b4cac81SBjoern A. Zeeb struct wiphy *
50546b4cac81SBjoern A. Zeeb linuxkpi_wiphy_new(const struct cfg80211_ops *ops, size_t priv_len)
50556b4cac81SBjoern A. Zeeb {
50566b4cac81SBjoern A. Zeeb 	struct lkpi_wiphy *lwiphy;
50576b4cac81SBjoern A. Zeeb 
50586b4cac81SBjoern A. Zeeb 	lwiphy = kzalloc(sizeof(*lwiphy) + priv_len, GFP_KERNEL);
50596b4cac81SBjoern A. Zeeb 	if (lwiphy == NULL)
50606b4cac81SBjoern A. Zeeb 		return (NULL);
50616b4cac81SBjoern A. Zeeb 	lwiphy->ops = ops;
50626b4cac81SBjoern A. Zeeb 
50636b4cac81SBjoern A. Zeeb 	/* XXX TODO */
50646b4cac81SBjoern A. Zeeb 	return (LWIPHY_TO_WIPHY(lwiphy));
50656b4cac81SBjoern A. Zeeb }
50666b4cac81SBjoern A. Zeeb 
50676b4cac81SBjoern A. Zeeb void
50686b4cac81SBjoern A. Zeeb linuxkpi_wiphy_free(struct wiphy *wiphy)
50696b4cac81SBjoern A. Zeeb {
50706b4cac81SBjoern A. Zeeb 	struct lkpi_wiphy *lwiphy;
50716b4cac81SBjoern A. Zeeb 
50726b4cac81SBjoern A. Zeeb 	if (wiphy == NULL)
50736b4cac81SBjoern A. Zeeb 		return;
50746b4cac81SBjoern A. Zeeb 
50756b4cac81SBjoern A. Zeeb 	lwiphy = WIPHY_TO_LWIPHY(wiphy);
50766b4cac81SBjoern A. Zeeb 	kfree(lwiphy);
50776b4cac81SBjoern A. Zeeb }
50786b4cac81SBjoern A. Zeeb 
50796b4cac81SBjoern A. Zeeb uint32_t
50806b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_channel_to_frequency(uint32_t channel,
50816b4cac81SBjoern A. Zeeb     enum nl80211_band band)
50826b4cac81SBjoern A. Zeeb {
50836b4cac81SBjoern A. Zeeb 
50846b4cac81SBjoern A. Zeeb 	switch (band) {
50856b4cac81SBjoern A. Zeeb 	case NL80211_BAND_2GHZ:
50866b4cac81SBjoern A. Zeeb 		return (ieee80211_ieee2mhz(channel, IEEE80211_CHAN_2GHZ));
50876b4cac81SBjoern A. Zeeb 		break;
50886b4cac81SBjoern A. Zeeb 	case NL80211_BAND_5GHZ:
50896b4cac81SBjoern A. Zeeb 		return (ieee80211_ieee2mhz(channel, IEEE80211_CHAN_5GHZ));
50906b4cac81SBjoern A. Zeeb 		break;
50916b4cac81SBjoern A. Zeeb 	default:
50926b4cac81SBjoern A. Zeeb 		/* XXX abort, retry, error, panic? */
50936b4cac81SBjoern A. Zeeb 		break;
50946b4cac81SBjoern A. Zeeb 	}
50956b4cac81SBjoern A. Zeeb 
50966b4cac81SBjoern A. Zeeb 	return (0);
50976b4cac81SBjoern A. Zeeb }
50986b4cac81SBjoern A. Zeeb 
50996b4cac81SBjoern A. Zeeb uint32_t
51006b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_frequency_to_channel(uint32_t freq, uint32_t flags __unused)
51016b4cac81SBjoern A. Zeeb {
51026b4cac81SBjoern A. Zeeb 
51036b4cac81SBjoern A. Zeeb 	return (ieee80211_mhz2ieee(freq, 0));
51046b4cac81SBjoern A. Zeeb }
51056b4cac81SBjoern A. Zeeb 
51066b4cac81SBjoern A. Zeeb static struct lkpi_sta *
51076b4cac81SBjoern A. Zeeb lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni)
51086b4cac81SBjoern A. Zeeb {
51096b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta, *temp;
51106b4cac81SBjoern A. Zeeb 
51116b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
51126b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) {
51136b4cac81SBjoern A. Zeeb 		if (lsta->ni == ni) {
51146b4cac81SBjoern A. Zeeb 			LKPI_80211_LVIF_UNLOCK(lvif);
51156b4cac81SBjoern A. Zeeb 			return (lsta);
51166b4cac81SBjoern A. Zeeb 		}
51176b4cac81SBjoern A. Zeeb 	}
51186b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
51196b4cac81SBjoern A. Zeeb 
51206b4cac81SBjoern A. Zeeb 	return (NULL);
51216b4cac81SBjoern A. Zeeb }
51226b4cac81SBjoern A. Zeeb 
51236b4cac81SBjoern A. Zeeb struct ieee80211_sta *
51246b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer)
51256b4cac81SBjoern A. Zeeb {
51266b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
51276b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta, *temp;
51286b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
51296b4cac81SBjoern A. Zeeb 
51306b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
51316b4cac81SBjoern A. Zeeb 
51326b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_LOCK(lvif);
51336b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH_SAFE(lsta, &lvif->lsta_head, lsta_entry, temp) {
51346b4cac81SBjoern A. Zeeb 		sta = LSTA_TO_STA(lsta);
51356b4cac81SBjoern A. Zeeb 		if (IEEE80211_ADDR_EQ(sta->addr, peer)) {
51366b4cac81SBjoern A. Zeeb 			LKPI_80211_LVIF_UNLOCK(lvif);
51376b4cac81SBjoern A. Zeeb 			return (sta);
51386b4cac81SBjoern A. Zeeb 		}
51396b4cac81SBjoern A. Zeeb 	}
51406b4cac81SBjoern A. Zeeb 	LKPI_80211_LVIF_UNLOCK(lvif);
51416b4cac81SBjoern A. Zeeb 	return (NULL);
51426b4cac81SBjoern A. Zeeb }
51436b4cac81SBjoern A. Zeeb 
51446b4cac81SBjoern A. Zeeb struct ieee80211_sta *
51452e183d99SBjoern A. Zeeb linuxkpi_ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
51462e183d99SBjoern A. Zeeb     const uint8_t *addr, const uint8_t *ourvifaddr)
51476b4cac81SBjoern A. Zeeb {
51486b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
51496b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
51506b4cac81SBjoern A. Zeeb 	struct lkpi_sta *lsta;
51516b4cac81SBjoern A. Zeeb 	struct ieee80211_vif *vif;
51526b4cac81SBjoern A. Zeeb 	struct ieee80211_sta *sta;
51536b4cac81SBjoern A. Zeeb 
51546b4cac81SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
51556b4cac81SBjoern A. Zeeb 	sta = NULL;
51566b4cac81SBjoern A. Zeeb 
51578891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
51586b4cac81SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
51596b4cac81SBjoern A. Zeeb 
51606b4cac81SBjoern A. Zeeb 		/* XXX-BZ check our address from the vif. */
51616b4cac81SBjoern A. Zeeb 
51626b4cac81SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
51636b4cac81SBjoern A. Zeeb 		if (ourvifaddr != NULL &&
51646b4cac81SBjoern A. Zeeb 		    !IEEE80211_ADDR_EQ(vif->addr, ourvifaddr))
51656b4cac81SBjoern A. Zeeb 			continue;
51666b4cac81SBjoern A. Zeeb 		sta = linuxkpi_ieee80211_find_sta(vif, addr);
51676b4cac81SBjoern A. Zeeb 		if (sta != NULL)
51686b4cac81SBjoern A. Zeeb 			break;
51696b4cac81SBjoern A. Zeeb 	}
51708891c455SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
51716b4cac81SBjoern A. Zeeb 
51726b4cac81SBjoern A. Zeeb 	if (sta != NULL) {
51736b4cac81SBjoern A. Zeeb 		lsta = STA_TO_LSTA(sta);
51746b4cac81SBjoern A. Zeeb 		if (!lsta->added_to_drv)
51756b4cac81SBjoern A. Zeeb 			return (NULL);
51766b4cac81SBjoern A. Zeeb 	}
51776b4cac81SBjoern A. Zeeb 
51786b4cac81SBjoern A. Zeeb 	return (sta);
51796b4cac81SBjoern A. Zeeb }
51806b4cac81SBjoern A. Zeeb 
51816b4cac81SBjoern A. Zeeb struct sk_buff *
51826b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
51836b4cac81SBjoern A. Zeeb     struct ieee80211_txq *txq)
51846b4cac81SBjoern A. Zeeb {
51856b4cac81SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
51860cbcfa19SBjoern A. Zeeb 	struct lkpi_vif *lvif;
51876b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
51886b4cac81SBjoern A. Zeeb 
51890cbcfa19SBjoern A. Zeeb 	skb = NULL;
51906b4cac81SBjoern A. Zeeb 	ltxq = TXQ_TO_LTXQ(txq);
51916b4cac81SBjoern A. Zeeb 	ltxq->seen_dequeue = true;
51926b4cac81SBjoern A. Zeeb 
51930cbcfa19SBjoern A. Zeeb 	if (ltxq->stopped)
51940cbcfa19SBjoern A. Zeeb 		goto stopped;
51950cbcfa19SBjoern A. Zeeb 
51960cbcfa19SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(ltxq->txq.vif);
51970cbcfa19SBjoern A. Zeeb 	if (lvif->hw_queue_stopped[ltxq->txq.ac]) {
51980cbcfa19SBjoern A. Zeeb 		ltxq->stopped = true;
51990cbcfa19SBjoern A. Zeeb 		goto stopped;
52000cbcfa19SBjoern A. Zeeb 	}
52010cbcfa19SBjoern A. Zeeb 
5202eac3646fSBjoern A. Zeeb 	IMPROVE("hw(TX_FRAG_LIST)");
5203eac3646fSBjoern A. Zeeb 
5204eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_LOCK(ltxq);
52056b4cac81SBjoern A. Zeeb 	skb = skb_dequeue(&ltxq->skbq);
5206eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_UNLOCK(ltxq);
52076b4cac81SBjoern A. Zeeb 
52080cbcfa19SBjoern A. Zeeb stopped:
52096b4cac81SBjoern A. Zeeb 	return (skb);
52106b4cac81SBjoern A. Zeeb }
52116b4cac81SBjoern A. Zeeb 
52126b4cac81SBjoern A. Zeeb void
52136b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *txq,
521486220d3cSBjoern A. Zeeb     unsigned long *frame_cnt, unsigned long *byte_cnt)
52156b4cac81SBjoern A. Zeeb {
52166b4cac81SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
52176b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
521886220d3cSBjoern A. Zeeb 	unsigned long fc, bc;
52196b4cac81SBjoern A. Zeeb 
52206b4cac81SBjoern A. Zeeb 	ltxq = TXQ_TO_LTXQ(txq);
52216b4cac81SBjoern A. Zeeb 
52226b4cac81SBjoern A. Zeeb 	fc = bc = 0;
5223eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_LOCK(ltxq);
52246b4cac81SBjoern A. Zeeb 	skb_queue_walk(&ltxq->skbq, skb) {
52256b4cac81SBjoern A. Zeeb 		fc++;
52266b4cac81SBjoern A. Zeeb 		bc += skb->len;
52276b4cac81SBjoern A. Zeeb 	}
5228eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_UNLOCK(ltxq);
52296b4cac81SBjoern A. Zeeb 	if (frame_cnt)
52306b4cac81SBjoern A. Zeeb 		*frame_cnt = fc;
52316b4cac81SBjoern A. Zeeb 	if (byte_cnt)
52326b4cac81SBjoern A. Zeeb 		*byte_cnt = bc;
52336b4cac81SBjoern A. Zeeb 
52346b4cac81SBjoern A. Zeeb 	/* Validate that this is doing the correct thing. */
52356b4cac81SBjoern A. Zeeb 	/* Should we keep track on en/dequeue? */
52366b4cac81SBjoern A. Zeeb 	IMPROVE();
52376b4cac81SBjoern A. Zeeb }
52386b4cac81SBjoern A. Zeeb 
52396b4cac81SBjoern A. Zeeb /*
52406b4cac81SBjoern A. Zeeb  * We are called from ieee80211_free_txskb() or ieee80211_tx_status().
52416b4cac81SBjoern A. Zeeb  * The latter tries to derive the success status from the info flags
52426b4cac81SBjoern A. Zeeb  * passed back from the driver.  rawx_mit() saves the ni on the m and the
52436b4cac81SBjoern A. Zeeb  * m on the skb for us to be able to give feedback to net80211.
52446b4cac81SBjoern A. Zeeb  */
5245a8397571SBjoern A. Zeeb static void
5246a8397571SBjoern A. Zeeb _lkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
52476b4cac81SBjoern A. Zeeb     int status)
52486b4cac81SBjoern A. Zeeb {
52496b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
52506b4cac81SBjoern A. Zeeb 	struct mbuf *m;
52516b4cac81SBjoern A. Zeeb 
52526b4cac81SBjoern A. Zeeb 	m = skb->m;
52536b4cac81SBjoern A. Zeeb 	skb->m = NULL;
52546b4cac81SBjoern A. Zeeb 
52556b4cac81SBjoern A. Zeeb 	if (m != NULL) {
52566b4cac81SBjoern A. Zeeb 		ni = m->m_pkthdr.PH_loc.ptr;
52576b4cac81SBjoern A. Zeeb 		/* Status: 0 is ok, != 0 is error. */
52586b4cac81SBjoern A. Zeeb 		ieee80211_tx_complete(ni, m, status);
52596b4cac81SBjoern A. Zeeb 		/* ni & mbuf were consumed. */
52606b4cac81SBjoern A. Zeeb 	}
5261a8397571SBjoern A. Zeeb }
52626b4cac81SBjoern A. Zeeb 
5263a8397571SBjoern A. Zeeb void
5264a8397571SBjoern A. Zeeb linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
5265a8397571SBjoern A. Zeeb     int status)
5266a8397571SBjoern A. Zeeb {
5267a8397571SBjoern A. Zeeb 
5268a8397571SBjoern A. Zeeb 	_lkpi_ieee80211_free_txskb(hw, skb, status);
52696b4cac81SBjoern A. Zeeb 	kfree_skb(skb);
52706b4cac81SBjoern A. Zeeb }
52716b4cac81SBjoern A. Zeeb 
5272383b3e8fSBjoern A. Zeeb void
5273a8397571SBjoern A. Zeeb linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
5274a8397571SBjoern A. Zeeb     struct ieee80211_tx_status *txstat)
5275383b3e8fSBjoern A. Zeeb {
5276a8397571SBjoern A. Zeeb 	struct sk_buff *skb;
5277383b3e8fSBjoern A. Zeeb 	struct ieee80211_tx_info *info;
5278383b3e8fSBjoern A. Zeeb 	struct ieee80211_ratectl_tx_status txs;
5279383b3e8fSBjoern A. Zeeb 	struct ieee80211_node *ni;
5280383b3e8fSBjoern A. Zeeb 	int status;
5281383b3e8fSBjoern A. Zeeb 
5282a8397571SBjoern A. Zeeb 	skb = txstat->skb;
5283383b3e8fSBjoern A. Zeeb 	if (skb->m != NULL) {
5284383b3e8fSBjoern A. Zeeb 		struct mbuf *m;
5285383b3e8fSBjoern A. Zeeb 
5286383b3e8fSBjoern A. Zeeb 		m = skb->m;
5287383b3e8fSBjoern A. Zeeb 		ni = m->m_pkthdr.PH_loc.ptr;
5288383b3e8fSBjoern A. Zeeb 		memset(&txs, 0, sizeof(txs));
5289383b3e8fSBjoern A. Zeeb 	} else {
5290383b3e8fSBjoern A. Zeeb 		ni = NULL;
5291383b3e8fSBjoern A. Zeeb 	}
5292383b3e8fSBjoern A. Zeeb 
5293a8397571SBjoern A. Zeeb 	info = txstat->info;
5294383b3e8fSBjoern A. Zeeb 	if (info->flags & IEEE80211_TX_STAT_ACK) {
5295383b3e8fSBjoern A. Zeeb 		status = 0;	/* No error. */
5296383b3e8fSBjoern A. Zeeb 		txs.status = IEEE80211_RATECTL_TX_SUCCESS;
5297383b3e8fSBjoern A. Zeeb 	} else {
5298383b3e8fSBjoern A. Zeeb 		status = 1;
5299383b3e8fSBjoern A. Zeeb 		txs.status = IEEE80211_RATECTL_TX_FAIL_UNSPECIFIED;
5300383b3e8fSBjoern A. Zeeb 	}
5301383b3e8fSBjoern A. Zeeb 
5302383b3e8fSBjoern A. Zeeb 	if (ni != NULL) {
5303e2c5ab09SJohn Baldwin 		int ridx __unused;
5304383b3e8fSBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
5305383b3e8fSBjoern A. Zeeb 		int old_rate;
5306383b3e8fSBjoern A. Zeeb 
5307383b3e8fSBjoern A. Zeeb 		old_rate = ni->ni_vap->iv_bss->ni_txrate;
5308383b3e8fSBjoern A. Zeeb #endif
5309383b3e8fSBjoern A. Zeeb 		txs.pktlen = skb->len;
5310383b3e8fSBjoern A. Zeeb 		txs.flags |= IEEE80211_RATECTL_STATUS_PKTLEN;
5311383b3e8fSBjoern A. Zeeb 		if (info->status.rates[0].count > 1) {
5312383b3e8fSBjoern A. Zeeb 			txs.long_retries = info->status.rates[0].count - 1;	/* 1 + retries in drivers. */
5313383b3e8fSBjoern A. Zeeb 			txs.flags |= IEEE80211_RATECTL_STATUS_LONG_RETRY;
5314383b3e8fSBjoern A. Zeeb 		}
5315383b3e8fSBjoern A. Zeeb #if 0		/* Unused in net80211 currently. */
5316a8397571SBjoern A. Zeeb 		/* XXX-BZ convert check .flags for MCS/VHT/.. */
5317383b3e8fSBjoern A. Zeeb 		txs.final_rate = info->status.rates[0].idx;
5318383b3e8fSBjoern A. Zeeb 		txs.flags |= IEEE80211_RATECTL_STATUS_FINAL_RATE;
5319383b3e8fSBjoern A. Zeeb #endif
5320adff403fSBjoern A. Zeeb 		if (info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID) {
5321383b3e8fSBjoern A. Zeeb 			txs.rssi = info->status.ack_signal;		/* XXX-BZ CONVERT? */
5322383b3e8fSBjoern A. Zeeb 			txs.flags |= IEEE80211_RATECTL_STATUS_RSSI;
5323383b3e8fSBjoern A. Zeeb 		}
5324383b3e8fSBjoern A. Zeeb 
5325383b3e8fSBjoern A. Zeeb 		IMPROVE("only update of rate matches but that requires us to get a proper rate");
5326383b3e8fSBjoern A. Zeeb 		ieee80211_ratectl_tx_complete(ni, &txs);
5327383b3e8fSBjoern A. Zeeb 		ridx = ieee80211_ratectl_rate(ni->ni_vap->iv_bss, NULL, 0);
5328383b3e8fSBjoern A. Zeeb 
5329383b3e8fSBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
5330383b3e8fSBjoern A. Zeeb 		if (linuxkpi_debug_80211 & D80211_TRACE_TX) {
5331383b3e8fSBjoern A. Zeeb 			printf("TX-RATE: %s: old %d new %d ridx %d, "
5332383b3e8fSBjoern A. Zeeb 			    "long_retries %d\n", __func__,
5333383b3e8fSBjoern A. Zeeb 			    old_rate, ni->ni_vap->iv_bss->ni_txrate,
5334383b3e8fSBjoern A. Zeeb 			    ridx, txs.long_retries);
5335383b3e8fSBjoern A. Zeeb 		}
5336383b3e8fSBjoern A. Zeeb #endif
5337383b3e8fSBjoern A. Zeeb 	}
5338383b3e8fSBjoern A. Zeeb 
5339383b3e8fSBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
5340383b3e8fSBjoern A. Zeeb 	if (linuxkpi_debug_80211 & D80211_TRACE_TX)
5341383b3e8fSBjoern A. Zeeb 		printf("TX-STATUS: %s: hw %p skb %p status %d : flags %#x "
5342383b3e8fSBjoern A. Zeeb 		    "band %u hw_queue %u tx_time_est %d : "
5343383b3e8fSBjoern A. Zeeb 		    "rates [ %u %u %#x, %u %u %#x, %u %u %#x, %u %u %#x ] "
5344383b3e8fSBjoern A. Zeeb 		    "ack_signal %u ampdu_ack_len %u ampdu_len %u antenna %u "
5345adff403fSBjoern A. Zeeb 		    "tx_time %u flags %#x "
5346383b3e8fSBjoern A. Zeeb 		    "status_driver_data [ %p %p ]\n",
5347383b3e8fSBjoern A. Zeeb 		    __func__, hw, skb, status, info->flags,
5348383b3e8fSBjoern A. Zeeb 		    info->band, info->hw_queue, info->tx_time_est,
5349383b3e8fSBjoern A. Zeeb 		    info->status.rates[0].idx, info->status.rates[0].count,
5350383b3e8fSBjoern A. Zeeb 		    info->status.rates[0].flags,
5351383b3e8fSBjoern A. Zeeb 		    info->status.rates[1].idx, info->status.rates[1].count,
5352383b3e8fSBjoern A. Zeeb 		    info->status.rates[1].flags,
5353383b3e8fSBjoern A. Zeeb 		    info->status.rates[2].idx, info->status.rates[2].count,
5354383b3e8fSBjoern A. Zeeb 		    info->status.rates[2].flags,
5355383b3e8fSBjoern A. Zeeb 		    info->status.rates[3].idx, info->status.rates[3].count,
5356383b3e8fSBjoern A. Zeeb 		    info->status.rates[3].flags,
5357383b3e8fSBjoern A. Zeeb 		    info->status.ack_signal, info->status.ampdu_ack_len,
5358383b3e8fSBjoern A. Zeeb 		    info->status.ampdu_len, info->status.antenna,
5359adff403fSBjoern A. Zeeb 		    info->status.tx_time, info->status.flags,
5360383b3e8fSBjoern A. Zeeb 		    info->status.status_driver_data[0],
5361383b3e8fSBjoern A. Zeeb 		    info->status.status_driver_data[1]);
5362383b3e8fSBjoern A. Zeeb #endif
5363383b3e8fSBjoern A. Zeeb 
5364a8397571SBjoern A. Zeeb 	if (txstat->free_list) {
5365a8397571SBjoern A. Zeeb 		_lkpi_ieee80211_free_txskb(hw, skb, status);
5366a8397571SBjoern A. Zeeb 		list_add_tail(&skb->list, txstat->free_list);
5367a8397571SBjoern A. Zeeb 	} else {
5368383b3e8fSBjoern A. Zeeb 		linuxkpi_ieee80211_free_txskb(hw, skb, status);
5369383b3e8fSBjoern A. Zeeb 	}
5370a8397571SBjoern A. Zeeb }
5371a8397571SBjoern A. Zeeb 
5372a8397571SBjoern A. Zeeb void
5373a8397571SBjoern A. Zeeb linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
5374a8397571SBjoern A. Zeeb {
5375a8397571SBjoern A. Zeeb 	struct ieee80211_tx_status status;
5376a8397571SBjoern A. Zeeb 
5377a8397571SBjoern A. Zeeb 	memset(&status, 0, sizeof(status));
5378a8397571SBjoern A. Zeeb 	status.info = IEEE80211_SKB_CB(skb);
5379a8397571SBjoern A. Zeeb 	status.skb = skb;
5380a8397571SBjoern A. Zeeb 	/* sta, n_rates, rates, free_list? */
5381a8397571SBjoern A. Zeeb 
5382a8397571SBjoern A. Zeeb 	ieee80211_tx_status_ext(hw, &status);
5383a8397571SBjoern A. Zeeb }
5384383b3e8fSBjoern A. Zeeb 
53856b4cac81SBjoern A. Zeeb /*
53866b4cac81SBjoern A. Zeeb  * This is an internal bandaid for the moment for the way we glue
53876b4cac81SBjoern A. Zeeb  * skbs and mbufs together for TX.  Once we have skbs backed by
53886b4cac81SBjoern A. Zeeb  * mbufs this should go away.
53896b4cac81SBjoern A. Zeeb  * This is a public function but kept on the private KPI (lkpi_)
53906b4cac81SBjoern A. Zeeb  * and is not exposed by a header file.
53916b4cac81SBjoern A. Zeeb  */
53926b4cac81SBjoern A. Zeeb static void
53936b4cac81SBjoern A. Zeeb lkpi_ieee80211_free_skb_mbuf(void *p)
53946b4cac81SBjoern A. Zeeb {
53956b4cac81SBjoern A. Zeeb 	struct ieee80211_node *ni;
53966b4cac81SBjoern A. Zeeb 	struct mbuf *m;
53976b4cac81SBjoern A. Zeeb 
53986b4cac81SBjoern A. Zeeb 	if (p == NULL)
53996b4cac81SBjoern A. Zeeb 		return;
54006b4cac81SBjoern A. Zeeb 
54016b4cac81SBjoern A. Zeeb 	m = (struct mbuf *)p;
54026b4cac81SBjoern A. Zeeb 	M_ASSERTPKTHDR(m);
54036b4cac81SBjoern A. Zeeb 
54046b4cac81SBjoern A. Zeeb 	ni = m->m_pkthdr.PH_loc.ptr;
54056b4cac81SBjoern A. Zeeb 	m->m_pkthdr.PH_loc.ptr = NULL;
54066b4cac81SBjoern A. Zeeb 	if (ni != NULL)
54076b4cac81SBjoern A. Zeeb 		ieee80211_free_node(ni);
54086b4cac81SBjoern A. Zeeb 	m_freem(m);
54096b4cac81SBjoern A. Zeeb }
54106b4cac81SBjoern A. Zeeb 
54116b4cac81SBjoern A. Zeeb void
54126b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_queue_delayed_work(struct ieee80211_hw *hw,
54136b4cac81SBjoern A. Zeeb     struct delayed_work *w, int delay)
54146b4cac81SBjoern A. Zeeb {
54156b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
54166b4cac81SBjoern A. Zeeb 
54176b4cac81SBjoern A. Zeeb 	/* Need to make sure hw is in a stable (non-suspended) state. */
54186b4cac81SBjoern A. Zeeb 	IMPROVE();
54196b4cac81SBjoern A. Zeeb 
54206b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
54216b4cac81SBjoern A. Zeeb 	queue_delayed_work(lhw->workq, w, delay);
54226b4cac81SBjoern A. Zeeb }
54236b4cac81SBjoern A. Zeeb 
54246b4cac81SBjoern A. Zeeb void
54256b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_queue_work(struct ieee80211_hw *hw,
54266b4cac81SBjoern A. Zeeb     struct work_struct *w)
54276b4cac81SBjoern A. Zeeb {
54286b4cac81SBjoern A. Zeeb 	struct lkpi_hw *lhw;
54296b4cac81SBjoern A. Zeeb 
54306b4cac81SBjoern A. Zeeb 	/* Need to make sure hw is in a stable (non-suspended) state. */
54316b4cac81SBjoern A. Zeeb 	IMPROVE();
54326b4cac81SBjoern A. Zeeb 
54336b4cac81SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
54346b4cac81SBjoern A. Zeeb 	queue_work(lhw->workq, w);
54356b4cac81SBjoern A. Zeeb }
54366b4cac81SBjoern A. Zeeb 
54376b4cac81SBjoern A. Zeeb struct sk_buff *
5438ade774b1SBjoern A. Zeeb linuxkpi_ieee80211_probereq_get(struct ieee80211_hw *hw, uint8_t *addr,
5439ade774b1SBjoern A. Zeeb     uint8_t *ssid, size_t ssid_len, size_t tailroom)
5440ade774b1SBjoern A. Zeeb {
5441ade774b1SBjoern A. Zeeb 	struct sk_buff *skb;
5442ade774b1SBjoern A. Zeeb 	struct ieee80211_frame *wh;
5443ade774b1SBjoern A. Zeeb 	uint8_t *p;
5444ade774b1SBjoern A. Zeeb 	size_t len;
5445ade774b1SBjoern A. Zeeb 
5446ade774b1SBjoern A. Zeeb 	len = sizeof(*wh);
5447ade774b1SBjoern A. Zeeb 	len += 2 + ssid_len;
5448ade774b1SBjoern A. Zeeb 
5449ade774b1SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + len + tailroom);
5450ade774b1SBjoern A. Zeeb 	if (skb == NULL)
5451ade774b1SBjoern A. Zeeb 		return (NULL);
5452ade774b1SBjoern A. Zeeb 
5453ade774b1SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
5454ade774b1SBjoern A. Zeeb 
5455ade774b1SBjoern A. Zeeb 	wh = skb_put_zero(skb, sizeof(*wh));
5456ade774b1SBjoern A. Zeeb 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0;
5457ade774b1SBjoern A. Zeeb 	wh->i_fc[0] |= IEEE80211_FC0_SUBTYPE_PROBE_REQ | IEEE80211_FC0_TYPE_MGT;
5458ade774b1SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr);
5459ade774b1SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(wh->i_addr2, addr);
5460ade774b1SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr);
5461ade774b1SBjoern A. Zeeb 
5462ade774b1SBjoern A. Zeeb 	p = skb_put(skb, 2 + ssid_len);
5463ade774b1SBjoern A. Zeeb 	*p++ = IEEE80211_ELEMID_SSID;
5464ade774b1SBjoern A. Zeeb 	*p++ = ssid_len;
5465ade774b1SBjoern A. Zeeb 	if (ssid_len > 0)
5466ade774b1SBjoern A. Zeeb 		memcpy(p, ssid, ssid_len);
5467ade774b1SBjoern A. Zeeb 
5468ade774b1SBjoern A. Zeeb 	return (skb);
5469ade774b1SBjoern A. Zeeb }
5470ade774b1SBjoern A. Zeeb 
5471ade774b1SBjoern A. Zeeb struct sk_buff *
54726b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_pspoll_get(struct ieee80211_hw *hw,
54736b4cac81SBjoern A. Zeeb     struct ieee80211_vif *vif)
54746b4cac81SBjoern A. Zeeb {
54756b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
54766b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
54776b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
54786b4cac81SBjoern A. Zeeb 	struct ieee80211_frame_pspoll *psp;
54796b4cac81SBjoern A. Zeeb 	uint16_t v;
54806b4cac81SBjoern A. Zeeb 
54816b4cac81SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*psp));
54826b4cac81SBjoern A. Zeeb 	if (skb == NULL)
54836b4cac81SBjoern A. Zeeb 		return (NULL);
54846b4cac81SBjoern A. Zeeb 
54856b4cac81SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
54866b4cac81SBjoern A. Zeeb 
54876b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
54886b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
54896b4cac81SBjoern A. Zeeb 
54906b4cac81SBjoern A. Zeeb 	psp = skb_put_zero(skb, sizeof(*psp));
54916b4cac81SBjoern A. Zeeb 	psp->i_fc[0] = IEEE80211_FC0_VERSION_0;
54926b4cac81SBjoern A. Zeeb 	psp->i_fc[0] |= IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL;
5493616e1330SBjoern A. Zeeb 	v = htole16(vif->cfg.aid | 1<<15 | 1<<16);
54946b4cac81SBjoern A. Zeeb 	memcpy(&psp->i_aid, &v, sizeof(v));
54956b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(psp->i_bssid, vap->iv_bss->ni_macaddr);
54966b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(psp->i_ta, vif->addr);
54976b4cac81SBjoern A. Zeeb 
54986b4cac81SBjoern A. Zeeb 	return (skb);
54996b4cac81SBjoern A. Zeeb }
55006b4cac81SBjoern A. Zeeb 
55016b4cac81SBjoern A. Zeeb struct sk_buff *
55026b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw,
5503adff403fSBjoern A. Zeeb     struct ieee80211_vif *vif, int linkid, bool qos)
55046b4cac81SBjoern A. Zeeb {
55056b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55066b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
55076b4cac81SBjoern A. Zeeb 	struct sk_buff *skb;
55086b4cac81SBjoern A. Zeeb 	struct ieee80211_frame *nullf;
55096b4cac81SBjoern A. Zeeb 
5510adff403fSBjoern A. Zeeb 	IMPROVE("linkid");
5511adff403fSBjoern A. Zeeb 
55126b4cac81SBjoern A. Zeeb 	skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*nullf));
55136b4cac81SBjoern A. Zeeb 	if (skb == NULL)
55146b4cac81SBjoern A. Zeeb 		return (NULL);
55156b4cac81SBjoern A. Zeeb 
55166b4cac81SBjoern A. Zeeb 	skb_reserve(skb, hw->extra_tx_headroom);
55176b4cac81SBjoern A. Zeeb 
55186b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
55196b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
55206b4cac81SBjoern A. Zeeb 
55216b4cac81SBjoern A. Zeeb 	nullf = skb_put_zero(skb, sizeof(*nullf));
55226b4cac81SBjoern A. Zeeb 	nullf->i_fc[0] = IEEE80211_FC0_VERSION_0;
55236b4cac81SBjoern A. Zeeb 	nullf->i_fc[0] |= IEEE80211_FC0_SUBTYPE_NODATA | IEEE80211_FC0_TYPE_DATA;
55246b4cac81SBjoern A. Zeeb 	nullf->i_fc[1] = IEEE80211_FC1_DIR_TODS;
55256b4cac81SBjoern A. Zeeb 
55266b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(nullf->i_addr1, vap->iv_bss->ni_bssid);
55276b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(nullf->i_addr2, vif->addr);
55286b4cac81SBjoern A. Zeeb 	IEEE80211_ADDR_COPY(nullf->i_addr3, vap->iv_bss->ni_macaddr);
55296b4cac81SBjoern A. Zeeb 
55306b4cac81SBjoern A. Zeeb 	return (skb);
55316b4cac81SBjoern A. Zeeb }
55326b4cac81SBjoern A. Zeeb 
55336b4cac81SBjoern A. Zeeb struct wireless_dev *
55346b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_vif_to_wdev(struct ieee80211_vif *vif)
55356b4cac81SBjoern A. Zeeb {
55366b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55376b4cac81SBjoern A. Zeeb 
55386b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
55396b4cac81SBjoern A. Zeeb 	return (&lvif->wdev);
55406b4cac81SBjoern A. Zeeb }
55416b4cac81SBjoern A. Zeeb 
55426b4cac81SBjoern A. Zeeb void
55436b4cac81SBjoern A. Zeeb linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *vif)
55446b4cac81SBjoern A. Zeeb {
55456b4cac81SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55466b4cac81SBjoern A. Zeeb 	struct ieee80211vap *vap;
55476b4cac81SBjoern A. Zeeb 	enum ieee80211_state nstate;
55486b4cac81SBjoern A. Zeeb 	int arg;
55496b4cac81SBjoern A. Zeeb 
55506b4cac81SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
55516b4cac81SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
55526b4cac81SBjoern A. Zeeb 
55536b4cac81SBjoern A. Zeeb 	/*
5554f3229b62SBjoern A. Zeeb 	 * Go to init; otherwise we need to elaborately check state and
55556b4cac81SBjoern A. Zeeb 	 * handle accordingly, e.g., if in RUN we could call iv_bmiss.
55566b4cac81SBjoern A. Zeeb 	 * Let the statemachine handle all neccessary changes.
55576b4cac81SBjoern A. Zeeb 	 */
5558f3229b62SBjoern A. Zeeb 	nstate = IEEE80211_S_INIT;
5559bb81db90SBjoern A. Zeeb 	arg = 0;	/* Not a valid reason. */
55606b4cac81SBjoern A. Zeeb 
5561018d93ecSBjoern A. Zeeb 	ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
5562018d93ecSBjoern A. Zeeb 	    vif, vap, ieee80211_state_name[vap->iv_state]);
55636b4cac81SBjoern A. Zeeb 	ieee80211_new_state(vap, nstate, arg);
55646b4cac81SBjoern A. Zeeb }
55656b4cac81SBjoern A. Zeeb 
5566bb81db90SBjoern A. Zeeb void
5567bb81db90SBjoern A. Zeeb linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *vif)
5568bb81db90SBjoern A. Zeeb {
5569bb81db90SBjoern A. Zeeb 	struct lkpi_vif *lvif;
5570bb81db90SBjoern A. Zeeb 	struct ieee80211vap *vap;
5571bb81db90SBjoern A. Zeeb 
5572bb81db90SBjoern A. Zeeb 	lvif = VIF_TO_LVIF(vif);
5573bb81db90SBjoern A. Zeeb 	vap = LVIF_TO_VAP(lvif);
5574bb81db90SBjoern A. Zeeb 
5575bb81db90SBjoern A. Zeeb 	ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
5576bb81db90SBjoern A. Zeeb 	    vif, vap, ieee80211_state_name[vap->iv_state]);
55773540911bSBjoern A. Zeeb 	ieee80211_beacon_miss(vap->iv_ic);
5578bb81db90SBjoern A. Zeeb }
5579bb81db90SBjoern A. Zeeb 
55805edde07cSBjoern A. Zeeb /* -------------------------------------------------------------------------- */
55815edde07cSBjoern A. Zeeb 
55825a9a0d78SBjoern A. Zeeb void
55835a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_stop_queue(struct ieee80211_hw *hw, int qnum)
55845a9a0d78SBjoern A. Zeeb {
55855a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
55865a9a0d78SBjoern A. Zeeb 	struct lkpi_vif *lvif;
55875a9a0d78SBjoern A. Zeeb 	struct ieee80211_vif *vif;
55885a9a0d78SBjoern A. Zeeb 	int ac_count, ac;
55895a9a0d78SBjoern A. Zeeb 
55905a9a0d78SBjoern A. Zeeb 	KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
55915a9a0d78SBjoern A. Zeeb 	    __func__, qnum, hw->queues, hw));
55925a9a0d78SBjoern A. Zeeb 
55935a9a0d78SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
55945a9a0d78SBjoern A. Zeeb 
55955a9a0d78SBjoern A. Zeeb 	/* See lkpi_ic_vap_create(). */
55965a9a0d78SBjoern A. Zeeb 	if (hw->queues >= IEEE80211_NUM_ACS)
55975a9a0d78SBjoern A. Zeeb 		ac_count = IEEE80211_NUM_ACS;
55985a9a0d78SBjoern A. Zeeb 	else
55995a9a0d78SBjoern A. Zeeb 		ac_count = 1;
56005a9a0d78SBjoern A. Zeeb 
56015a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
56025a9a0d78SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
56035a9a0d78SBjoern A. Zeeb 
56045a9a0d78SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
56055a9a0d78SBjoern A. Zeeb 		for (ac = 0; ac < ac_count; ac++) {
56065a9a0d78SBjoern A. Zeeb 			IMPROVE_TXQ("LOCKING");
56075a9a0d78SBjoern A. Zeeb 			if (qnum == vif->hw_queue[ac]) {
56080d2cb6a6SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
56095a9a0d78SBjoern A. Zeeb 				/*
56105a9a0d78SBjoern A. Zeeb 				 * For now log this to better understand
56115a9a0d78SBjoern A. Zeeb 				 * how this is supposed to work.
56125a9a0d78SBjoern A. Zeeb 				 */
56130d2cb6a6SBjoern A. Zeeb 				if (lvif->hw_queue_stopped[ac] &&
56140d2cb6a6SBjoern A. Zeeb 				    (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) != 0)
56155a9a0d78SBjoern A. Zeeb 					ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
56165a9a0d78SBjoern A. Zeeb 					    "lvif %p vif %p ac %d qnum %d already "
56175a9a0d78SBjoern A. Zeeb 					    "stopped\n", __func__, __LINE__,
56185a9a0d78SBjoern A. Zeeb 					    lhw, hw, lvif, vif, ac, qnum);
56190d2cb6a6SBjoern A. Zeeb #endif
56205a9a0d78SBjoern A. Zeeb 				lvif->hw_queue_stopped[ac] = true;
56215a9a0d78SBjoern A. Zeeb 			}
56225a9a0d78SBjoern A. Zeeb 		}
56235a9a0d78SBjoern A. Zeeb 	}
56245a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
56255a9a0d78SBjoern A. Zeeb }
56265a9a0d78SBjoern A. Zeeb 
56275a9a0d78SBjoern A. Zeeb void
56285a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_stop_queues(struct ieee80211_hw *hw)
56295a9a0d78SBjoern A. Zeeb {
56305a9a0d78SBjoern A. Zeeb 	int i;
56315a9a0d78SBjoern A. Zeeb 
56325a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Locking; do we need further info?");
56335a9a0d78SBjoern A. Zeeb 	for (i = 0; i < hw->queues; i++)
56345a9a0d78SBjoern A. Zeeb 		linuxkpi_ieee80211_stop_queue(hw, i);
56355a9a0d78SBjoern A. Zeeb }
56365a9a0d78SBjoern A. Zeeb 
56375a9a0d78SBjoern A. Zeeb 
56385a9a0d78SBjoern A. Zeeb static void
56395a9a0d78SBjoern A. Zeeb lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
56405a9a0d78SBjoern A. Zeeb {
56415a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
56425a9a0d78SBjoern A. Zeeb 	struct lkpi_vif *lvif;
56435a9a0d78SBjoern A. Zeeb 	struct lkpi_sta *lsta;
56445a9a0d78SBjoern A. Zeeb 	int ac_count, ac, tid;
56455a9a0d78SBjoern A. Zeeb 
56465a9a0d78SBjoern A. Zeeb 	/* See lkpi_ic_vap_create(). */
56475a9a0d78SBjoern A. Zeeb 	if (hw->queues >= IEEE80211_NUM_ACS)
56485a9a0d78SBjoern A. Zeeb 		ac_count = IEEE80211_NUM_ACS;
56495a9a0d78SBjoern A. Zeeb 	else
56505a9a0d78SBjoern A. Zeeb 		ac_count = 1;
56515a9a0d78SBjoern A. Zeeb 
56525a9a0d78SBjoern A. Zeeb 	lhw = wiphy_priv(hw->wiphy);
56535a9a0d78SBjoern A. Zeeb 
56545a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Locking");
56555a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_LOCK(lhw);
56565a9a0d78SBjoern A. Zeeb 	TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
56575a9a0d78SBjoern A. Zeeb 		struct ieee80211_vif *vif;
56585a9a0d78SBjoern A. Zeeb 
56595a9a0d78SBjoern A. Zeeb 		vif = LVIF_TO_VIF(lvif);
56605a9a0d78SBjoern A. Zeeb 		for (ac = 0; ac < ac_count; ac++) {
56615a9a0d78SBjoern A. Zeeb 
56625a9a0d78SBjoern A. Zeeb 			if (hwq == vif->hw_queue[ac]) {
56635a9a0d78SBjoern A. Zeeb 
56645a9a0d78SBjoern A. Zeeb 				/* XXX-BZ what about software scan? */
56655a9a0d78SBjoern A. Zeeb 
56660d2cb6a6SBjoern A. Zeeb #ifdef LINUXKPI_DEBUG_80211
56675a9a0d78SBjoern A. Zeeb 				/*
56685a9a0d78SBjoern A. Zeeb 				 * For now log this to better understand
56695a9a0d78SBjoern A. Zeeb 				 * how this is supposed to work.
56705a9a0d78SBjoern A. Zeeb 				 */
56710d2cb6a6SBjoern A. Zeeb 				if (!lvif->hw_queue_stopped[ac] &&
56720d2cb6a6SBjoern A. Zeeb 				    (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) != 0)
56735a9a0d78SBjoern A. Zeeb 					ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
56745a9a0d78SBjoern A. Zeeb 					    "lvif %p vif %p ac %d hw_q not stopped\n",
56755a9a0d78SBjoern A. Zeeb 					    __func__, __LINE__,
56765a9a0d78SBjoern A. Zeeb 					    lhw, hw, lvif, vif, ac);
56770d2cb6a6SBjoern A. Zeeb #endif
56785a9a0d78SBjoern A. Zeeb 				lvif->hw_queue_stopped[ac] = false;
56795a9a0d78SBjoern A. Zeeb 
56805a9a0d78SBjoern A. Zeeb 				LKPI_80211_LVIF_LOCK(lvif);
56815a9a0d78SBjoern A. Zeeb 				TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
56825a9a0d78SBjoern A. Zeeb 					struct ieee80211_sta *sta;
56835a9a0d78SBjoern A. Zeeb 
56845a9a0d78SBjoern A. Zeeb 					sta = LSTA_TO_STA(lsta);
56855a9a0d78SBjoern A. Zeeb 					for (tid = 0; tid < nitems(sta->txq); tid++) {
56865a9a0d78SBjoern A. Zeeb 						struct lkpi_txq *ltxq;
56875a9a0d78SBjoern A. Zeeb 
56885a9a0d78SBjoern A. Zeeb 						if (sta->txq[tid] == NULL)
56895a9a0d78SBjoern A. Zeeb 							continue;
56905a9a0d78SBjoern A. Zeeb 
56915a9a0d78SBjoern A. Zeeb 						if (sta->txq[tid]->ac != ac)
56925a9a0d78SBjoern A. Zeeb 							continue;
56935a9a0d78SBjoern A. Zeeb 
56945a9a0d78SBjoern A. Zeeb 						ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
56955a9a0d78SBjoern A. Zeeb 						if (!ltxq->stopped)
56965a9a0d78SBjoern A. Zeeb 							continue;
56975a9a0d78SBjoern A. Zeeb 
56985a9a0d78SBjoern A. Zeeb 						ltxq->stopped = false;
56995a9a0d78SBjoern A. Zeeb 
57005a9a0d78SBjoern A. Zeeb 						/* XXX-BZ see when this explodes with all the locking. taskq? */
57015a9a0d78SBjoern A. Zeeb 						lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
57025a9a0d78SBjoern A. Zeeb 					}
57035a9a0d78SBjoern A. Zeeb 				}
57045a9a0d78SBjoern A. Zeeb 				LKPI_80211_LVIF_UNLOCK(lvif);
57055a9a0d78SBjoern A. Zeeb 			}
57065a9a0d78SBjoern A. Zeeb 		}
57075a9a0d78SBjoern A. Zeeb 	}
57085a9a0d78SBjoern A. Zeeb 	LKPI_80211_LHW_LVIF_UNLOCK(lhw);
57095a9a0d78SBjoern A. Zeeb }
57105a9a0d78SBjoern A. Zeeb 
57115a9a0d78SBjoern A. Zeeb void
57125a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
57135a9a0d78SBjoern A. Zeeb {
57145a9a0d78SBjoern A. Zeeb 	int i;
57155a9a0d78SBjoern A. Zeeb 
57165a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Is this all/enough here?");
57175a9a0d78SBjoern A. Zeeb 	for (i = 0; i < hw->queues; i++)
57185a9a0d78SBjoern A. Zeeb 		lkpi_ieee80211_wake_queues(hw, i);
57195a9a0d78SBjoern A. Zeeb }
57205a9a0d78SBjoern A. Zeeb 
57215a9a0d78SBjoern A. Zeeb void
57225a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
57235a9a0d78SBjoern A. Zeeb {
57245a9a0d78SBjoern A. Zeeb 
57255a9a0d78SBjoern A. Zeeb 	KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
57265a9a0d78SBjoern A. Zeeb 	    __func__, qnum, hw->queues, hw));
57275a9a0d78SBjoern A. Zeeb 
57285a9a0d78SBjoern A. Zeeb 	lkpi_ieee80211_wake_queues(hw, qnum);
57295a9a0d78SBjoern A. Zeeb }
57305a9a0d78SBjoern A. Zeeb 
57315a9a0d78SBjoern A. Zeeb /* This is just hardware queues. */
57325a9a0d78SBjoern A. Zeeb void
57335a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
57345a9a0d78SBjoern A. Zeeb {
57355a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
57365a9a0d78SBjoern A. Zeeb 
57375a9a0d78SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
57385a9a0d78SBjoern A. Zeeb 
57395a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("Are there reasons why we wouldn't schedule?");
57405a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("LOCKING");
57415a9a0d78SBjoern A. Zeeb 	if (++lhw->txq_generation[ac] == 0)
57425a9a0d78SBjoern A. Zeeb 		lhw->txq_generation[ac]++;
57435a9a0d78SBjoern A. Zeeb }
57445a9a0d78SBjoern A. Zeeb 
57455a9a0d78SBjoern A. Zeeb struct ieee80211_txq *
57465a9a0d78SBjoern A. Zeeb linuxkpi_ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
57475a9a0d78SBjoern A. Zeeb {
57485a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
57495a9a0d78SBjoern A. Zeeb 	struct ieee80211_txq *txq;
57505a9a0d78SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
57515a9a0d78SBjoern A. Zeeb 
57525a9a0d78SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
57535a9a0d78SBjoern A. Zeeb 	txq = NULL;
57545a9a0d78SBjoern A. Zeeb 
57555a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("LOCKING");
57565a9a0d78SBjoern A. Zeeb 
57575a9a0d78SBjoern A. Zeeb 	/* Check that we are scheduled. */
57585a9a0d78SBjoern A. Zeeb 	if (lhw->txq_generation[ac] == 0)
57595a9a0d78SBjoern A. Zeeb 		goto out;
57605a9a0d78SBjoern A. Zeeb 
57615a9a0d78SBjoern A. Zeeb 	ltxq = TAILQ_FIRST(&lhw->scheduled_txqs[ac]);
57625a9a0d78SBjoern A. Zeeb 	if (ltxq == NULL)
57635a9a0d78SBjoern A. Zeeb 		goto out;
57645a9a0d78SBjoern A. Zeeb 	if (ltxq->txq_generation == lhw->txq_generation[ac])
57655a9a0d78SBjoern A. Zeeb 		goto out;
57665a9a0d78SBjoern A. Zeeb 
57675a9a0d78SBjoern A. Zeeb 	ltxq->txq_generation = lhw->txq_generation[ac];
57685a9a0d78SBjoern A. Zeeb 	TAILQ_REMOVE(&lhw->scheduled_txqs[ac], ltxq, txq_entry);
57695a9a0d78SBjoern A. Zeeb 	txq = &ltxq->txq;
57705a9a0d78SBjoern A. Zeeb 	TAILQ_ELEM_INIT(ltxq, txq_entry);
57715a9a0d78SBjoern A. Zeeb 
57725a9a0d78SBjoern A. Zeeb out:
57735a9a0d78SBjoern A. Zeeb 	return (txq);
57745a9a0d78SBjoern A. Zeeb }
57755a9a0d78SBjoern A. Zeeb 
57765a9a0d78SBjoern A. Zeeb void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
57775a9a0d78SBjoern A. Zeeb     struct ieee80211_txq *txq, bool withoutpkts)
57785a9a0d78SBjoern A. Zeeb {
57795a9a0d78SBjoern A. Zeeb 	struct lkpi_hw *lhw;
57805a9a0d78SBjoern A. Zeeb 	struct lkpi_txq *ltxq;
5781eac3646fSBjoern A. Zeeb 	bool ltxq_empty;
57825a9a0d78SBjoern A. Zeeb 
57835a9a0d78SBjoern A. Zeeb 	ltxq = TXQ_TO_LTXQ(txq);
57845a9a0d78SBjoern A. Zeeb 
57855a9a0d78SBjoern A. Zeeb 	IMPROVE_TXQ("LOCKING");
57865a9a0d78SBjoern A. Zeeb 
57875a9a0d78SBjoern A. Zeeb 	/* Only schedule if work to do or asked to anyway. */
5788eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_LOCK(ltxq);
5789eac3646fSBjoern A. Zeeb 	ltxq_empty = skb_queue_empty(&ltxq->skbq);
5790eac3646fSBjoern A. Zeeb 	LKPI_80211_LTXQ_UNLOCK(ltxq);
5791eac3646fSBjoern A. Zeeb 	if (!withoutpkts && ltxq_empty)
57925a9a0d78SBjoern A. Zeeb 		goto out;
57935a9a0d78SBjoern A. Zeeb 
57945a9a0d78SBjoern A. Zeeb 	/* Make sure we do not double-schedule. */
57955a9a0d78SBjoern A. Zeeb 	if (ltxq->txq_entry.tqe_next != NULL)
57965a9a0d78SBjoern A. Zeeb 		goto out;
57975a9a0d78SBjoern A. Zeeb 
57985a9a0d78SBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
57995a9a0d78SBjoern A. Zeeb 	TAILQ_INSERT_TAIL(&lhw->scheduled_txqs[txq->ac], ltxq, txq_entry);
58005a9a0d78SBjoern A. Zeeb out:
58015a9a0d78SBjoern A. Zeeb 	return;
58025a9a0d78SBjoern A. Zeeb }
58035a9a0d78SBjoern A. Zeeb 
5804eac3646fSBjoern A. Zeeb void
5805eac3646fSBjoern A. Zeeb linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
5806eac3646fSBjoern A. Zeeb     struct ieee80211_txq *txq)
5807eac3646fSBjoern A. Zeeb {
5808eac3646fSBjoern A. Zeeb 	struct lkpi_hw *lhw;
5809eac3646fSBjoern A. Zeeb 	struct ieee80211_txq *ntxq;
5810eac3646fSBjoern A. Zeeb 	struct ieee80211_tx_control control;
5811eac3646fSBjoern A. Zeeb         struct sk_buff *skb;
5812eac3646fSBjoern A. Zeeb 
5813eac3646fSBjoern A. Zeeb 	lhw = HW_TO_LHW(hw);
5814eac3646fSBjoern A. Zeeb 
5815eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_LOCK(lhw);
5816eac3646fSBjoern A. Zeeb 	ieee80211_txq_schedule_start(hw, txq->ac);
5817eac3646fSBjoern A. Zeeb 	do {
5818eac3646fSBjoern A. Zeeb 		ntxq = ieee80211_next_txq(hw, txq->ac);
5819eac3646fSBjoern A. Zeeb 		if (ntxq == NULL)
5820eac3646fSBjoern A. Zeeb 			break;
5821eac3646fSBjoern A. Zeeb 
5822eac3646fSBjoern A. Zeeb 		memset(&control, 0, sizeof(control));
5823eac3646fSBjoern A. Zeeb 		control.sta = ntxq->sta;
5824eac3646fSBjoern A. Zeeb 		do {
5825eac3646fSBjoern A. Zeeb 			skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
5826eac3646fSBjoern A. Zeeb 			if (skb == NULL)
5827eac3646fSBjoern A. Zeeb 				break;
5828eac3646fSBjoern A. Zeeb 			lkpi_80211_mo_tx(hw, &control, skb);
5829eac3646fSBjoern A. Zeeb 		} while(1);
5830eac3646fSBjoern A. Zeeb 
5831eac3646fSBjoern A. Zeeb 		ieee80211_return_txq(hw, ntxq, false);
5832eac3646fSBjoern A. Zeeb 	} while (1);
5833eac3646fSBjoern A. Zeeb 	ieee80211_txq_schedule_end(hw, txq->ac);
5834eac3646fSBjoern A. Zeeb 	LKPI_80211_LHW_TXQ_UNLOCK(lhw);
5835eac3646fSBjoern A. Zeeb }
5836eac3646fSBjoern A. Zeeb 
58375a9a0d78SBjoern A. Zeeb /* -------------------------------------------------------------------------- */
58385a9a0d78SBjoern A. Zeeb 
58395edde07cSBjoern A. Zeeb struct lkpi_cfg80211_bss {
58405edde07cSBjoern A. Zeeb 	u_int refcnt;
58415edde07cSBjoern A. Zeeb 	struct cfg80211_bss bss;
58425edde07cSBjoern A. Zeeb };
58435edde07cSBjoern A. Zeeb 
58445edde07cSBjoern A. Zeeb struct lkpi_cfg80211_get_bss_iter_lookup {
58455edde07cSBjoern A. Zeeb 	struct wiphy *wiphy;
58465edde07cSBjoern A. Zeeb 	struct linuxkpi_ieee80211_channel *chan;
58475edde07cSBjoern A. Zeeb 	const uint8_t *bssid;
58485edde07cSBjoern A. Zeeb 	const uint8_t *ssid;
58495edde07cSBjoern A. Zeeb 	size_t ssid_len;
58505edde07cSBjoern A. Zeeb 	enum ieee80211_bss_type bss_type;
58515edde07cSBjoern A. Zeeb 	enum ieee80211_privacy privacy;
58525edde07cSBjoern A. Zeeb 
58535edde07cSBjoern A. Zeeb 	/*
58545edde07cSBjoern A. Zeeb 	 * Something to store a copy of the result as the net80211 scan cache
58555edde07cSBjoern A. Zeeb 	 * is not refoucnted so a scan entry might go away any time.
58565edde07cSBjoern A. Zeeb 	 */
58575edde07cSBjoern A. Zeeb 	bool match;
58585edde07cSBjoern A. Zeeb 	struct cfg80211_bss *bss;
58595edde07cSBjoern A. Zeeb };
58605edde07cSBjoern A. Zeeb 
58615edde07cSBjoern A. Zeeb static void
58625edde07cSBjoern A. Zeeb lkpi_cfg80211_get_bss_iterf(void *arg, const struct ieee80211_scan_entry *se)
58635edde07cSBjoern A. Zeeb {
58645edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_get_bss_iter_lookup *lookup;
58655edde07cSBjoern A. Zeeb 	size_t ielen;
58665edde07cSBjoern A. Zeeb 
58675edde07cSBjoern A. Zeeb 	lookup = arg;
58685edde07cSBjoern A. Zeeb 
58695edde07cSBjoern A. Zeeb 	/* Do not try to find another match. */
58705edde07cSBjoern A. Zeeb 	if (lookup->match)
58715edde07cSBjoern A. Zeeb 		return;
58725edde07cSBjoern A. Zeeb 
58735edde07cSBjoern A. Zeeb 	/* Nothing to store result. */
58745edde07cSBjoern A. Zeeb 	if (lookup->bss == NULL)
58755edde07cSBjoern A. Zeeb 		return;
58765edde07cSBjoern A. Zeeb 
58775edde07cSBjoern A. Zeeb 	if (lookup->privacy != IEEE80211_PRIVACY_ANY) {
58785edde07cSBjoern A. Zeeb 		/* if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) */
58795edde07cSBjoern A. Zeeb 		/* We have no idea what to compare to as the drivers only request ANY */
58805edde07cSBjoern A. Zeeb 		return;
58815edde07cSBjoern A. Zeeb 	}
58825edde07cSBjoern A. Zeeb 
58835edde07cSBjoern A. Zeeb 	if (lookup->bss_type != IEEE80211_BSS_TYPE_ANY) {
58845edde07cSBjoern A. Zeeb 		/* if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS)) */
58855edde07cSBjoern A. Zeeb 		/* We have no idea what to compare to as the drivers only request ANY */
58865edde07cSBjoern A. Zeeb 		return;
58875edde07cSBjoern A. Zeeb 	}
58885edde07cSBjoern A. Zeeb 
58895edde07cSBjoern A. Zeeb 	if (lookup->chan != NULL) {
58905edde07cSBjoern A. Zeeb 		struct linuxkpi_ieee80211_channel *chan;
58915edde07cSBjoern A. Zeeb 
58925edde07cSBjoern A. Zeeb 		chan = linuxkpi_ieee80211_get_channel(lookup->wiphy,
58935edde07cSBjoern A. Zeeb 		    se->se_chan->ic_freq);
58945edde07cSBjoern A. Zeeb 		if (chan == NULL || chan != lookup->chan)
58955edde07cSBjoern A. Zeeb 			return;
58965edde07cSBjoern A. Zeeb 	}
58975edde07cSBjoern A. Zeeb 
58985edde07cSBjoern A. Zeeb 	if (lookup->bssid && !IEEE80211_ADDR_EQ(lookup->bssid, se->se_bssid))
58995edde07cSBjoern A. Zeeb 		return;
59005edde07cSBjoern A. Zeeb 
59015edde07cSBjoern A. Zeeb 	if (lookup->ssid) {
59025edde07cSBjoern A. Zeeb 		if (lookup->ssid_len != se->se_ssid[1] ||
59035edde07cSBjoern A. Zeeb 		    se->se_ssid[1] == 0)
59045edde07cSBjoern A. Zeeb 			return;
59055edde07cSBjoern A. Zeeb 		if (memcmp(lookup->ssid, se->se_ssid+2, lookup->ssid_len) != 0)
59065edde07cSBjoern A. Zeeb 			return;
59075edde07cSBjoern A. Zeeb 	}
59085edde07cSBjoern A. Zeeb 
59095edde07cSBjoern A. Zeeb 	ielen = se->se_ies.len;
59105edde07cSBjoern A. Zeeb 
59115edde07cSBjoern A. Zeeb 	lookup->bss->ies = malloc(sizeof(*lookup->bss->ies) + ielen,
59125edde07cSBjoern A. Zeeb 	    M_LKPI80211, M_NOWAIT | M_ZERO);
59135edde07cSBjoern A. Zeeb 	if (lookup->bss->ies == NULL)
59145edde07cSBjoern A. Zeeb 		return;
59155edde07cSBjoern A. Zeeb 
59165edde07cSBjoern A. Zeeb 	lookup->bss->ies->data = (uint8_t *)lookup->bss->ies + sizeof(*lookup->bss->ies);
59175edde07cSBjoern A. Zeeb 	lookup->bss->ies->len = ielen;
59185edde07cSBjoern A. Zeeb 	if (ielen)
59195edde07cSBjoern A. Zeeb 		memcpy(lookup->bss->ies->data, se->se_ies.data, ielen);
59205edde07cSBjoern A. Zeeb 
59215edde07cSBjoern A. Zeeb 	lookup->match = true;
59225edde07cSBjoern A. Zeeb }
59235edde07cSBjoern A. Zeeb 
59245edde07cSBjoern A. Zeeb struct cfg80211_bss *
59255edde07cSBjoern A. Zeeb linuxkpi_cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan,
59265edde07cSBjoern A. Zeeb     const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len,
59275edde07cSBjoern A. Zeeb     enum ieee80211_bss_type bss_type, enum ieee80211_privacy privacy)
59285edde07cSBjoern A. Zeeb {
59295edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_bss *lbss;
59305edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_get_bss_iter_lookup lookup;
59315edde07cSBjoern A. Zeeb 	struct lkpi_hw *lhw;
59325edde07cSBjoern A. Zeeb 	struct ieee80211vap *vap;
59335edde07cSBjoern A. Zeeb 
59345edde07cSBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
59355edde07cSBjoern A. Zeeb 
59365edde07cSBjoern A. Zeeb 	/* Let's hope we can alloc. */
59375edde07cSBjoern A. Zeeb 	lbss = malloc(sizeof(*lbss), M_LKPI80211, M_NOWAIT | M_ZERO);
59385edde07cSBjoern A. Zeeb 	if (lbss == NULL) {
59395edde07cSBjoern A. Zeeb 		ic_printf(lhw->ic, "%s: alloc failed.\n", __func__);
59405edde07cSBjoern A. Zeeb 		return (NULL);
59415edde07cSBjoern A. Zeeb 	}
59425edde07cSBjoern A. Zeeb 
59435edde07cSBjoern A. Zeeb 	lookup.wiphy = wiphy;
59445edde07cSBjoern A. Zeeb 	lookup.chan = chan;
59455edde07cSBjoern A. Zeeb 	lookup.bssid = bssid;
59465edde07cSBjoern A. Zeeb 	lookup.ssid = ssid;
59475edde07cSBjoern A. Zeeb 	lookup.ssid_len = ssid_len;
59485edde07cSBjoern A. Zeeb 	lookup.bss_type = bss_type;
59495edde07cSBjoern A. Zeeb 	lookup.privacy = privacy;
59505edde07cSBjoern A. Zeeb 	lookup.match = false;
59515edde07cSBjoern A. Zeeb 	lookup.bss = &lbss->bss;
59525edde07cSBjoern A. Zeeb 
59535edde07cSBjoern A. Zeeb 	IMPROVE("Iterate over all VAPs comparing perm_addr and addresses?");
59545edde07cSBjoern A. Zeeb 	vap = TAILQ_FIRST(&lhw->ic->ic_vaps);
59555edde07cSBjoern A. Zeeb 	ieee80211_scan_iterate(vap, lkpi_cfg80211_get_bss_iterf, &lookup);
59565edde07cSBjoern A. Zeeb 	if (!lookup.match) {
59575edde07cSBjoern A. Zeeb 		free(lbss, M_LKPI80211);
59585edde07cSBjoern A. Zeeb 		return (NULL);
59595edde07cSBjoern A. Zeeb 	}
59605edde07cSBjoern A. Zeeb 
59615edde07cSBjoern A. Zeeb 	refcount_init(&lbss->refcnt, 1);
59625edde07cSBjoern A. Zeeb 	return (&lbss->bss);
59635edde07cSBjoern A. Zeeb }
59645edde07cSBjoern A. Zeeb 
59655edde07cSBjoern A. Zeeb void
59665edde07cSBjoern A. Zeeb linuxkpi_cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss)
59675edde07cSBjoern A. Zeeb {
59685edde07cSBjoern A. Zeeb 	struct lkpi_cfg80211_bss *lbss;
59695edde07cSBjoern A. Zeeb 
59705edde07cSBjoern A. Zeeb 	lbss = container_of(bss, struct lkpi_cfg80211_bss, bss);
59715edde07cSBjoern A. Zeeb 
59725edde07cSBjoern A. Zeeb 	/* Free everything again on refcount ... */
59735edde07cSBjoern A. Zeeb 	if (refcount_release(&lbss->refcnt)) {
59745edde07cSBjoern A. Zeeb 		free(lbss->bss.ies, M_LKPI80211);
59755edde07cSBjoern A. Zeeb 		free(lbss, M_LKPI80211);
59765edde07cSBjoern A. Zeeb 	}
59775edde07cSBjoern A. Zeeb }
59785edde07cSBjoern A. Zeeb 
59795edde07cSBjoern A. Zeeb void
59805edde07cSBjoern A. Zeeb linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
59815edde07cSBjoern A. Zeeb {
59825edde07cSBjoern A. Zeeb 	struct lkpi_hw *lhw;
59835edde07cSBjoern A. Zeeb 	struct ieee80211com *ic;
59845edde07cSBjoern A. Zeeb 	struct ieee80211vap *vap;
59855edde07cSBjoern A. Zeeb 
59865edde07cSBjoern A. Zeeb 	lhw = wiphy_priv(wiphy);
59875edde07cSBjoern A. Zeeb 	ic = lhw->ic;
59885edde07cSBjoern A. Zeeb 
59895edde07cSBjoern A. Zeeb 	/*
59905edde07cSBjoern A. Zeeb 	 * If we haven't called ieee80211_ifattach() yet
59915edde07cSBjoern A. Zeeb 	 * or there is no VAP, there are no scans to flush.
59925edde07cSBjoern A. Zeeb 	 */
59935edde07cSBjoern A. Zeeb 	if (ic == NULL ||
59945edde07cSBjoern A. Zeeb 	    (lhw->sc_flags & LKPI_MAC80211_DRV_STARTED) == 0)
59955edde07cSBjoern A. Zeeb 		return;
59965edde07cSBjoern A. Zeeb 
59975edde07cSBjoern A. Zeeb 	/* Should only happen on the current one? Not seen it late enough. */
59985edde07cSBjoern A. Zeeb 	IEEE80211_LOCK(ic);
59995edde07cSBjoern A. Zeeb 	TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
60005edde07cSBjoern A. Zeeb 		ieee80211_scan_flush(vap);
60015edde07cSBjoern A. Zeeb 	IEEE80211_UNLOCK(ic);
60025edde07cSBjoern A. Zeeb }
60035edde07cSBjoern A. Zeeb 
60045edde07cSBjoern A. Zeeb /* -------------------------------------------------------------------------- */
60055edde07cSBjoern A. Zeeb 
60066b4cac81SBjoern A. Zeeb MODULE_VERSION(linuxkpi_wlan, 1);
60076b4cac81SBjoern A. Zeeb MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1);
60086b4cac81SBjoern A. Zeeb MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1);
6009