aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common/src/linux_80211.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linuxkpi/common/src/linux_80211.c')
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c2578
1 files changed, 2077 insertions, 501 deletions
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index 1eb520b2140f..d598e9af0050 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2020-2022 The FreeBSD Foundation
+ * Copyright (c) 2020-2023 The FreeBSD Foundation
* Copyright (c) 2020-2022 Bjoern A. Zeeb
*
* This software was developed by Björn Zeeb under sponsorship from
@@ -39,9 +39,6 @@
* We call the internal versions lxxx (e.g., hw -> lhw, sta -> lsta).
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
#include <sys/param.h>
#include <sys/types.h>
#include <sys/kernel.h>
@@ -64,6 +61,7 @@ __FBSDID("$FreeBSD$");
#include <net80211/ieee80211_proto.h>
#include <net80211/ieee80211_ratectl.h>
#include <net80211/ieee80211_radiotap.h>
+#include <net80211/ieee80211_vht.h>
#define LINUXKPI_NET80211
#include <net/mac80211.h>
@@ -71,10 +69,22 @@ __FBSDID("$FreeBSD$");
#include <linux/workqueue.h>
#include "linux_80211.h"
+#define LKPI_80211_WME
/* #define LKPI_80211_HW_CRYPTO */
+/* #define LKPI_80211_VHT */
+/* #define LKPI_80211_HT */
+#if defined(LKPI_80211_VHT) && !defined(LKPI_80211_HT)
+#define LKPI_80211_HT
+#endif
static MALLOC_DEFINE(M_LKPI80211, "lkpi80211", "LinuxKPI 80211 compat");
+/* XXX-BZ really want this and others in queue.h */
+#define TAILQ_ELEM_INIT(elm, field) do { \
+ (elm)->field.tqe_next = NULL; \
+ (elm)->field.tqe_prev = NULL; \
+} while (0)
+
/* -------------------------------------------------------------------------- */
/* Keep public for as long as header files are using it too. */
@@ -88,22 +98,6 @@ SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN,
&linuxkpi_debug_80211, 0, "LinuxKPI 802.11 debug level");
-#ifndef D80211_TODO
-#define D80211_TODO 0x1
-#endif
-#ifndef D80211_IMPROVE
-#define D80211_IMPROVE 0x2
-#endif
-#define D80211_TRACE 0x10
-#define D80211_TRACEOK 0x20
-#define D80211_TRACE_TX 0x100
-#define D80211_TRACE_TX_DUMP 0x200
-#define D80211_TRACE_RX 0x1000
-#define D80211_TRACE_RX_DUMP 0x2000
-#define D80211_TRACE_RX_BEACONS 0x4000
-#define D80211_TRACEX (D80211_TRACE_TX|D80211_TRACE_RX)
-#define D80211_TRACEX_DUMP (D80211_TRACE_TX_DUMP|D80211_TRACE_RX_DUMP)
-#define D80211_TRACE_STA 0x10000
#define UNIMPLEMENTED if (linuxkpi_debug_80211 & D80211_TODO) \
printf("XXX-TODO %s:%d: UNIMPLEMENTED\n", __func__, __LINE__)
#define TRACEOK() if (linuxkpi_debug_80211 & D80211_TRACEOK) \
@@ -118,16 +112,14 @@ SYSCTL_INT(_compat_linuxkpi_80211, OID_AUTO, debug, CTLFLAG_RWTUN,
#define PREP_TX_INFO_DURATION 0 /* Let the driver do its thing. */
#endif
-/* c.f. ieee80211_ioctl.c */
-static const uint8_t zerobssid[IEEE80211_ADDR_LEN];
-
/* This is DSAP | SSAP | CTRL | ProtoID/OrgCode{3}. */
const uint8_t rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
/* IEEE 802.11-05/0257r1 */
const uint8_t bridge_tunnel_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
-const uint8_t tid_to_mac80211_ac[] = {
+/* IEEE 802.11e Table 20i-UP-to-AC mappings. */
+static const uint8_t ieee80211e_up_to_ac[] = {
IEEE80211_AC_BE,
IEEE80211_AC_BK,
IEEE80211_AC_BK,
@@ -149,10 +141,89 @@ const struct cfg80211_ops linuxkpi_mac80211cfgops = {
*/
};
+#if 0
static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *,
struct ieee80211_node *);
+#endif
static void lkpi_80211_txq_task(void *, int);
+static void lkpi_80211_lhw_rxq_task(void *, int);
static void lkpi_ieee80211_free_skb_mbuf(void *);
+#ifdef LKPI_80211_WME
+static int lkpi_wme_update(struct lkpi_hw *, struct ieee80211vap *, bool);
+#endif
+
+#if defined(LKPI_80211_HT)
+static void
+lkpi_sta_sync_ht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *ht_rx_nss)
+{
+ struct ieee80211vap *vap;
+ uint8_t *ie;
+ struct ieee80211_ht_cap *htcap;
+ int i, rx_nss;
+
+ if ((ni->ni_flags & IEEE80211_NODE_HT) == 0)
+ return;
+
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan) &&
+ IEEE80211_IS_CHAN_HT40(ni->ni_chan))
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_40;
+
+ sta->deflink.ht_cap.ht_supported = true;
+
+ /* htcap->ampdu_params_info */
+ vap = ni->ni_vap;
+ sta->deflink.ht_cap.ampdu_density = _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MPDUDENSITY);
+ if (sta->deflink.ht_cap.ampdu_density > vap->iv_ampdu_density)
+ sta->deflink.ht_cap.ampdu_density = vap->iv_ampdu_density;
+ sta->deflink.ht_cap.ampdu_factor = _IEEE80211_MASKSHIFT(ni->ni_htparam, IEEE80211_HTCAP_MAXRXAMPDU);
+ if (sta->deflink.ht_cap.ampdu_factor > vap->iv_ampdu_rxmax)
+ sta->deflink.ht_cap.ampdu_factor = vap->iv_ampdu_rxmax;
+
+ ie = ni->ni_ies.htcap_ie;
+ KASSERT(ie != NULL, ("%s: HT but no htcap_ie on ni %p\n", __func__, ni));
+ if (ie[0] == IEEE80211_ELEMID_VENDOR)
+ ie += 4;
+ ie += 2;
+ htcap = (struct ieee80211_ht_cap *)ie;
+ sta->deflink.ht_cap.cap = htcap->cap_info;
+ sta->deflink.ht_cap.mcs = htcap->mcs;
+
+ rx_nss = 0;
+ for (i = 0; i < nitems(htcap->mcs.rx_mask); i++) {
+ if (htcap->mcs.rx_mask[i])
+ rx_nss++;
+ }
+ if (ht_rx_nss != NULL)
+ *ht_rx_nss = rx_nss;
+
+ IMPROVE("sta->wme, sta->deflink.agg.max*");
+}
+#endif
+
+#if defined(LKPI_80211_VHT)
+static void
+lkpi_sta_sync_vht_from_ni(struct ieee80211_sta *sta, struct ieee80211_node *ni, int *vht_rx_nss)
+{
+
+ if ((ni->ni_flags & IEEE80211_NODE_VHT) == 0)
+ return;
+
+ if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
+#ifdef __notyet__
+ if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160; /* XXX? */
+ } else
+#endif
+ if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_160;
+ else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_80;
+ }
+
+ IMPROVE("VHT sync ni to sta");
+ return;
+}
+#endif
static void
lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
@@ -178,22 +249,14 @@ lkpi_lsta_dump(struct lkpi_sta *lsta, struct ieee80211_node *ni,
static void
lkpi_lsta_remove(struct lkpi_sta *lsta, struct lkpi_vif *lvif)
{
- struct ieee80211_node *ni;
- ni = lsta->ni;
LKPI_80211_LVIF_LOCK(lvif);
+ KASSERT(lsta->lsta_entry.tqe_prev != NULL,
+ ("%s: lsta %p lsta_entry.tqe_prev %p ni %p\n", __func__,
+ lsta, lsta->lsta_entry.tqe_prev, lsta->ni));
TAILQ_REMOVE(&lvif->lsta_head, lsta, lsta_entry);
LKPI_80211_LVIF_UNLOCK(lvif);
-
- lsta->ni = NULL;
- ni->ni_drv_data = NULL;
- if (ni != NULL)
- ieee80211_free_node(ni);
-
- IMPROVE("more from lkpi_ic_node_free() should happen here.");
-
- free(lsta, M_LKPI80211);
}
static struct lkpi_sta *
@@ -204,7 +267,9 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
struct ieee80211_sta *sta;
- int tid;
+ int band, i, tid;
+ int ht_rx_nss;
+ int vht_rx_nss;
lsta = malloc(sizeof(*lsta) + hw->sta_data_size, M_LKPI80211,
M_NOWAIT | M_ZERO);
@@ -213,13 +278,16 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
lsta->added_to_drv = false;
lsta->state = IEEE80211_STA_NOTEXIST;
-#if 0
/*
- * This needs to be done in node_init() as ieee80211_alloc_node()
- * will initialise the refcount after us.
+ * Link the ni to the lsta here without taking a reference.
+ * For one we would have to take the reference in node_init()
+ * as ieee80211_alloc_node() will initialise the refcount after us.
+ * For the other a ni and an lsta are 1:1 mapped and always together
+ * from [ic_]node_alloc() to [ic_]node_free() so we are essentally
+ * using the ni references for the lsta as well despite it being
+ * two separate allocations.
*/
- lsta->ni = ieee80211_ref_node(ni);
-#endif
+ lsta->ni = ni;
/* The back-pointer "drv_data" to net80211_node let's us get lsta. */
ni->ni_drv_data = lsta;
@@ -228,6 +296,8 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
sta = LSTA_TO_STA(lsta);
IEEE80211_ADDR_COPY(sta->addr, mac);
+
+ /* TXQ */
for (tid = 0; tid < nitems(sta->txq); tid++) {
struct lkpi_txq *ltxq;
@@ -245,30 +315,128 @@ lkpi_lsta_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN],
IMPROVE("AP/if we support non-STA here too");
ltxq->txq.ac = IEEE80211_AC_VO;
} else {
- ltxq->txq.ac = tid_to_mac80211_ac[tid & 7];
+ ltxq->txq.ac = ieee80211e_up_to_ac[tid & 7];
}
ltxq->seen_dequeue = false;
+ ltxq->stopped = false;
ltxq->txq.vif = vif;
ltxq->txq.tid = tid;
ltxq->txq.sta = sta;
+ TAILQ_ELEM_INIT(ltxq, txq_entry);
skb_queue_head_init(&ltxq->skbq);
+ LKPI_80211_LTXQ_LOCK_INIT(ltxq);
sta->txq[tid] = &ltxq->txq;
}
+ /* Deflink information. */
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ struct ieee80211_supported_band *supband;
+
+ supband = hw->wiphy->bands[band];
+ if (supband == NULL)
+ continue;
+
+ for (i = 0; i < supband->n_bitrates; i++) {
+
+ IMPROVE("Further supband->bitrates[i]* checks?");
+ /* or should we get them from the ni? */
+ sta->deflink.supp_rates[band] |= BIT(i);
+ }
+ }
+
+ sta->deflink.smps_mode = IEEE80211_SMPS_OFF;
+ sta->deflink.bandwidth = IEEE80211_STA_RX_BW_20;
+ sta->deflink.rx_nss = 0;
+
+ ht_rx_nss = 0;
+#if defined(LKPI_80211_HT)
+ lkpi_sta_sync_ht_from_ni(sta, ni, &ht_rx_nss);
+#endif
+ vht_rx_nss = 0;
+#if defined(LKPI_80211_VHT)
+ lkpi_sta_sync_vht_from_ni(sta, ni, &vht_rx_nss);
+#endif
+
+ sta->deflink.rx_nss = MAX(ht_rx_nss, sta->deflink.rx_nss);
+ sta->deflink.rx_nss = MAX(vht_rx_nss, sta->deflink.rx_nss);
+ IMPROVE("he, ... smps_mode, ..");
+
+ /* Link configuration. */
+ IEEE80211_ADDR_COPY(sta->deflink.addr, sta->addr);
+ sta->link[0] = &sta->deflink;
+ for (i = 1; i < nitems(sta->link); i++) {
+ IMPROVE("more links; only link[0] = deflink currently.");
+ }
+
/* Deferred TX path. */
- mtx_init(&lsta->txq_mtx, "lsta_txq", NULL, MTX_DEF);
+ LKPI_80211_LSTA_TXQ_LOCK_INIT(lsta);
TASK_INIT(&lsta->txq_task, 0, lkpi_80211_txq_task, lsta);
mbufq_init(&lsta->txq, IFQ_MAXLEN);
+ lsta->txq_ready = true;
return (lsta);
cleanup:
- for (; tid >= 0; tid--)
+ for (; tid >= 0; tid--) {
+ struct lkpi_txq *ltxq;
+
+ ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
+ LKPI_80211_LTXQ_LOCK_DESTROY(ltxq);
free(sta->txq[tid], M_LKPI80211);
+ }
free(lsta, M_LKPI80211);
return (NULL);
}
+static void
+lkpi_lsta_free(struct lkpi_sta *lsta, struct ieee80211_node *ni)
+{
+ struct mbuf *m;
+
+ if (lsta->added_to_drv)
+ panic("%s: Trying to free an lsta still known to firmware: "
+ "lsta %p ni %p added_to_drv %d\n",
+ __func__, lsta, ni, lsta->added_to_drv);
+
+ /* XXX-BZ free resources, ... */
+ IMPROVE();
+
+ /* Drain sta->txq[] */
+
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ lsta->txq_ready = false;
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+
+ /* Drain taskq, won't be restarted until added_to_drv is set again. */
+ while (taskqueue_cancel(taskqueue_thread, &lsta->txq_task, NULL) != 0)
+ taskqueue_drain(taskqueue_thread, &lsta->txq_task);
+
+ /* Flush mbufq (make sure to release ni refs!). */
+ m = mbufq_dequeue(&lsta->txq);
+ while (m != NULL) {
+ struct ieee80211_node *nim;
+
+ nim = (struct ieee80211_node *)m->m_pkthdr.rcvif;
+ if (nim != NULL)
+ ieee80211_free_node(nim);
+ m_freem(m);
+ m = mbufq_dequeue(&lsta->txq);
+ }
+ KASSERT(mbufq_empty(&lsta->txq), ("%s: lsta %p has txq len %d != 0\n",
+ __func__, lsta, mbufq_len(&lsta->txq)));
+ LKPI_80211_LSTA_TXQ_LOCK_DESTROY(lsta);
+
+ /* Remove lsta from vif; that is done by the state machine. Should assert it? */
+
+ IMPROVE("Make sure everything is cleaned up.");
+
+ /* Free lsta. */
+ lsta->ni = NULL;
+ ni->ni_drv_data = NULL;
+ free(lsta, M_LKPI80211);
+}
+
+
static enum nl80211_band
lkpi_net80211_chan_to_nl80211_band(struct ieee80211_channel *c)
{
@@ -380,7 +548,7 @@ lkpi_l80211_to_net80211_cyphers(uint32_t wlan_cipher_suite)
case WLAN_CIPHER_SUITE_TKIP:
return (IEEE80211_CRYPTO_TKIP);
case WLAN_CIPHER_SUITE_CCMP:
- return (IEEE80211_CIPHER_AES_CCM);
+ return (IEEE80211_CRYPTO_AES_CCM);
case WLAN_CIPHER_SUITE_WEP104:
return (IEEE80211_CRYPTO_WEP);
case WLAN_CIPHER_SUITE_AES_CMAC:
@@ -487,6 +655,7 @@ lkpi_find_lkpi80211_chan(struct lkpi_hw *lhw,
return (NULL);
}
+#if 0
static struct linuxkpi_ieee80211_channel *
lkpi_get_lkpi80211_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
{
@@ -511,6 +680,7 @@ lkpi_get_lkpi80211_chan(struct ieee80211com *ic, struct ieee80211_node *ni)
return (chan);
}
+#endif
struct linuxkpi_ieee80211_channel *
linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
@@ -536,31 +706,6 @@ linuxkpi_ieee80211_get_channel(struct wiphy *wiphy, uint32_t freq)
return (NULL);
}
-void
-linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
-{
- struct lkpi_hw *lhw;
- struct ieee80211com *ic;
- struct ieee80211vap *vap;
-
- lhw = wiphy_priv(wiphy);
- ic = lhw->ic;
-
- /*
- * If we haven't called ieee80211_ifattach() yet
- * or there is no VAP, there are no scans to flush.
- */
- if (ic == NULL ||
- (lhw->sc_flags & LKPI_MAC80211_DRV_STARTED) == 0)
- return;
-
- /* Should only happen on the current one? Not seen it late enough. */
- IEEE80211_LOCK(ic);
- TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
- ieee80211_scan_flush(vap);
- IEEE80211_UNLOCK(ic);
-}
-
#ifdef LKPI_80211_HW_CRYPTO
static int
_lkpi_iv_key_set_delete(struct ieee80211vap *vap, const struct ieee80211_key *k,
@@ -751,7 +896,7 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
"dtim_period %u sync_dtim_count %u sync_tsf %ju "
"sync_device_ts %u bss_changed %#08x\n",
__func__, __LINE__, _f, _l,
- vif->bss_conf.assoc, vif->bss_conf.aid,
+ vif->cfg.assoc, vif->cfg.aid,
vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
vif->bss_conf.sync_dtim_count,
(uintmax_t)vif->bss_conf.sync_tsf,
@@ -782,7 +927,7 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
"dtim_period %u sync_dtim_count %u sync_tsf %ju "
"sync_device_ts %u bss_changed %#08x\n",
__func__, __LINE__, _f, _l,
- vif->bss_conf.assoc, vif->bss_conf.aid,
+ vif->cfg.assoc, vif->cfg.aid,
vif->bss_conf.beacon_int, vif->bss_conf.dtim_period,
vif->bss_conf.sync_dtim_count,
(uintmax_t)vif->bss_conf.sync_tsf,
@@ -798,8 +943,12 @@ lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
{
struct ieee80211_hw *hw;
int error;
+ bool cancel;
- if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) == 0)
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ if (!cancel)
return;
hw = LHW_TO_HW(lhw);
@@ -808,13 +957,18 @@ lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
LKPI_80211_LHW_LOCK(lhw);
/* Need to cancel the scan. */
lkpi_80211_mo_cancel_hw_scan(hw, vif);
+ LKPI_80211_LHW_UNLOCK(lhw);
/* Need to make sure we see ieee80211_scan_completed. */
- error = msleep(lhw, &lhw->mtx, 0, "lhwscanstop", hz/2);
- LKPI_80211_LHW_UNLOCK(lhw);
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0)
+ error = msleep(lhw, &lhw->scan_mtx, 0, "lhwscanstop", hz/2);
+ cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+
IEEE80211_LOCK(lhw->ic);
- if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0)
+ if (cancel)
ic_printf(lhw->ic, "%s: failed to cancel scan: %d (%p, %p)\n",
__func__, error, lhw, vif);
}
@@ -844,7 +998,7 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
struct lkpi_hw *lhw)
{
sta->aid = 0;
- if (vif->bss_conf.assoc) {
+ if (vif->cfg.assoc) {
struct ieee80211_hw *hw;
enum ieee80211_bss_changed changed;
@@ -852,8 +1006,8 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
lkpi_update_mcast_filter(lhw->ic, true);
changed = 0;
- vif->bss_conf.assoc = false;
- vif->bss_conf.aid = 0;
+ vif->cfg.assoc = false;
+ vif->cfg.aid = 0;
changed |= BSS_CHANGED_ASSOC;
/*
* This will remove the sta from firmware for iwlwifi.
@@ -874,28 +1028,32 @@ lkpi_wake_tx_queues(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
{
struct lkpi_txq *ltxq;
int tid;
+ bool ltxq_empty;
/* Wake up all queues to know they are allocated in the driver. */
for (tid = 0; tid < nitems(sta->txq); tid++) {
- if (tid == IEEE80211_NUM_TIDS) {
- IMPROVE("station specific?");
- if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ))
- continue;
- } else if (tid >= hw->queues)
+ if (tid == IEEE80211_NUM_TIDS) {
+ IMPROVE("station specific?");
+ if (!ieee80211_hw_check(hw, STA_MMPDU_TXQ))
continue;
+ } else if (tid >= hw->queues)
+ continue;
- if (sta->txq[tid] == NULL)
- continue;
+ if (sta->txq[tid] == NULL)
+ continue;
- ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
- if (dequeue_seen && !ltxq->seen_dequeue)
- continue;
+ ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
+ if (dequeue_seen && !ltxq->seen_dequeue)
+ continue;
- if (no_emptyq && skb_queue_empty(&ltxq->skbq))
- continue;
+ LKPI_80211_LTXQ_LOCK(ltxq);
+ ltxq_empty = skb_queue_empty(&ltxq->skbq);
+ LKPI_80211_LTXQ_UNLOCK(ltxq);
+ if (no_emptyq && ltxq_empty)
+ continue;
- lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
}
}
@@ -915,6 +1073,7 @@ static int
lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
{
struct linuxkpi_ieee80211_channel *chan;
+ struct lkpi_chanctx *lchanctx;
struct ieee80211_chanctx_conf *conf;
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
@@ -922,35 +1081,73 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct ieee80211_vif *vif;
struct ieee80211_node *ni;
struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
enum ieee80211_bss_changed bss_changed;
struct ieee80211_prep_tx_info prep_tx_info;
uint32_t changed;
int error;
- chan = lkpi_get_lkpi80211_chan(vap->iv_ic, vap->iv_bss);
+ /*
+ * In here we use vap->iv_bss until lvif->lvif_bss is set.
+ * For all later (STATE >= AUTH) functions we need to use the lvif
+ * cache which will be tracked even through (*iv_update_bss)().
+ */
+
+ if (vap->iv_bss == NULL) {
+ ic_printf(vap->iv_ic, "%s: no iv_bss for vap %p\n", __func__, vap);
+ return (EINVAL);
+ }
+ /*
+ * Keep the ni alive locally. In theory (and practice) iv_bss can change
+ * once we unlock here. This is due to net80211 allowing state changes
+ * and new join1() despite having an active node as well as due to
+ * the fact that the iv_bss can be swapped under the hood in (*iv_update_bss).
+ */
+ ni = ieee80211_ref_node(vap->iv_bss);
+ if (ni->ni_chan == NULL || ni->ni_chan == IEEE80211_CHAN_ANYC) {
+ ic_printf(vap->iv_ic, "%s: no channel set for iv_bss ni %p "
+ "on vap %p\n", __func__, ni, vap);
+ ieee80211_free_node(ni); /* Error handling for the local ni. */
+ return (EINVAL);
+ }
+
+ lhw = vap->iv_ic->ic_softc;
+ chan = lkpi_find_lkpi80211_chan(lhw, ni->ni_chan);
if (chan == NULL) {
- ic_printf(vap->iv_ic, "%s: failed to get channel\n", __func__);
+ ic_printf(vap->iv_ic, "%s: failed to get LKPI channel from "
+ "iv_bss ni %p on vap %p\n", __func__, ni, vap);
+ ieee80211_free_node(ni); /* Error handling for the local ni. */
return (ESRCH);
}
- lhw = vap->iv_ic->ic_softc;
hw = LHW_TO_HW(lhw);
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
- ni = ieee80211_ref_node(vap->iv_bss);
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* XXX-BZ KASSERT later? */
+ if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL) {
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+ return (EBUSY);
+ }
+ LKPI_80211_LVIF_UNLOCK(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
/* Add chanctx (or if exists, change it). */
if (vif->chanctx_conf != NULL) {
conf = vif->chanctx_conf;
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
IMPROVE("diff changes for changed, working on live copy, rcu");
} else {
/* Keep separate alloc as in Linux this is rcu managed? */
- conf = malloc(sizeof(*conf) + hw->chanctx_data_size,
+ lchanctx = malloc(sizeof(*lchanctx) + hw->chanctx_data_size,
M_LKPI80211, M_WAITOK | M_ZERO);
+ conf = &lchanctx->conf;
}
conf->rx_chains_dynamic = 1;
@@ -961,12 +1158,54 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
conf->def.width = NL80211_CHAN_WIDTH_20_NOHT;
conf->def.center_freq1 = chan->center_freq;
conf->def.center_freq2 = 0;
+ IMPROVE("Check vht_cap from band not just chan?");
+ KASSERT(ni->ni_chan != NULL && ni->ni_chan != IEEE80211_CHAN_ANYC,
+ ("%s:%d: ni %p ni_chan %p\n", __func__, __LINE__, ni, ni->ni_chan));
+#ifdef LKPI_80211_HT
+ if (IEEE80211_IS_CHAN_HT(ni->ni_chan)) {
+ if (IEEE80211_IS_CHAN_HT40(ni->ni_chan)) {
+ conf->def.width = NL80211_CHAN_WIDTH_40;
+ } else
+ conf->def.width = NL80211_CHAN_WIDTH_20;
+ }
+#endif
+#ifdef LKPI_80211_VHT
+ if (IEEE80211_IS_CHAN_VHT(ni->ni_chan)) {
+#ifdef __notyet__
+ if (IEEE80211_IS_CHAN_VHT80P80(ni->ni_chan)) {
+ conf->def.width = NL80211_CHAN_WIDTH_80P80;
+ conf->def.center_freq2 = 0; /* XXX */
+ } else
+#endif
+ if (IEEE80211_IS_CHAN_VHT160(ni->ni_chan))
+ conf->def.width = NL80211_CHAN_WIDTH_160;
+ else if (IEEE80211_IS_CHAN_VHT80(ni->ni_chan))
+ conf->def.width = NL80211_CHAN_WIDTH_80;
+ }
+#endif
/* Responder ... */
conf->min_def.chan = chan;
conf->min_def.width = NL80211_CHAN_WIDTH_20_NOHT;
conf->min_def.center_freq1 = chan->center_freq;
conf->min_def.center_freq2 = 0;
- IMPROVE("currently 20_NOHT only");
+ IMPROVE("currently 20_NOHT min_def only");
+
+ /* Set bss info (bss_info_changed). */
+ bss_changed = 0;
+ vif->bss_conf.bssid = ni->ni_bssid;
+ bss_changed |= BSS_CHANGED_BSSID;
+ vif->bss_conf.txpower = ni->ni_txpower;
+ bss_changed |= BSS_CHANGED_TXPOWER;
+ vif->cfg.idle = false;
+ bss_changed |= BSS_CHANGED_IDLE;
+
+ /* vif->bss_conf.basic_rates ? Where exactly? */
+
+ /* Should almost assert it is this. */
+ vif->cfg.assoc = false;
+ vif->cfg.aid = 0;
+
+ bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
error = 0;
if (vif->chanctx_conf != NULL) {
@@ -982,65 +1221,76 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif->bss_conf.chandef.width = conf->def.width;
vif->bss_conf.chandef.center_freq1 =
conf->def.center_freq1;
+#ifdef LKPI_80211_HT
+ if (vif->bss_conf.chandef.width == NL80211_CHAN_WIDTH_40) {
+ /* Note: it is 10 not 20. */
+ if (IEEE80211_IS_CHAN_HT40U(ni->ni_chan))
+ vif->bss_conf.chandef.center_freq1 += 10;
+ else if (IEEE80211_IS_CHAN_HT40D(ni->ni_chan))
+ vif->bss_conf.chandef.center_freq1 -= 10;
+ }
+#endif
vif->bss_conf.chandef.center_freq2 =
conf->def.center_freq2;
} else {
+ ic_printf(vap->iv_ic, "%s:%d: mo_add_chanctx "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
}
+
+ vif->bss_conf.chanctx_conf = conf;
+
/* Assign vif chanctx. */
if (error == 0)
- error = lkpi_80211_mo_assign_vif_chanctx(hw, vif, conf);
+ error = lkpi_80211_mo_assign_vif_chanctx(hw, vif,
+ &vif->bss_conf, conf);
if (error == EOPNOTSUPP)
error = 0;
if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_assign_vif_chanctx "
+ "failed: %d\n", __func__, __LINE__, error);
lkpi_80211_mo_remove_chanctx(hw, conf);
- free(conf, M_LKPI80211);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
+ free(lchanctx, M_LKPI80211);
goto out;
}
}
IMPROVE("update radiotap chan fields too");
- /* Set bss info (bss_info_changed). */
- bss_changed = 0;
- vif->bss_conf.bssid = ni->ni_bssid;
- bss_changed |= BSS_CHANGED_BSSID;
- vif->bss_conf.txpower = ni->ni_txpower;
- bss_changed |= BSS_CHANGED_TXPOWER;
- vif->bss_conf.idle = false;
- bss_changed |= BSS_CHANGED_IDLE;
-
- /* Should almost assert it is this. */
- vif->bss_conf.assoc = false;
- vif->bss_conf.aid = 0;
-
- bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
-
/* RATES */
IMPROVE("bss info: not all needs to come now and rates are missing");
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
/*
- * This is a bandaid for now. If we went through (*iv_update_bss)()
- * and then removed the lsta we end up here without a lsta and have
- * to manually allocate and link it in as lkpi_ic_node_alloc()/init()
- * would normally do.
- * XXX-BZ I do not like this but currently we have no good way of
- * intercepting the bss swap and state changes and packets going out
- * workflow so live with this. It is a compat layer after all.
+ * Given ni and lsta are 1:1 from alloc to free we can assert that
+ * ni always has lsta data attach despite net80211 node swapping
+ * under the hoods.
*/
- if (ni->ni_drv_data == NULL) {
- lsta = lkpi_lsta_alloc(vap, ni->ni_macaddr, hw, ni);
- if (lsta == NULL) {
- error = ENOMEM;
- goto out;
- }
- lsta->ni = ieee80211_ref_node(ni);
- } else {
- lsta = ni->ni_drv_data;
- }
+ KASSERT(ni->ni_drv_data != NULL, ("%s: ni %p ni_drv_data %p\n",
+ __func__, ni, ni->ni_drv_data));
+ lsta = ni->ni_drv_data;
- /* Insert the [l]sta into the list of known stations. */
LKPI_80211_LVIF_LOCK(lvif);
+ /* Re-check given (*iv_update_bss) could have happened. */
+ /* XXX-BZ KASSERT later? or deal as error? */
+ if (lvif->lvif_bss_synched || lvif->lvif_bss != NULL)
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d, ni %p lsta %p\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched, ni, lsta);
+
+ /*
+ * Reference the ni for this cache of lsta/ni on lvif->lvif_bss
+ * essentially out lsta version of the iv_bss.
+ * Do NOT use iv_bss here anymore as that may have diverged from our
+ * function local ni already and would lead to inconsistencies.
+ */
+ ieee80211_ref_node(ni);
+ lvif->lvif_bss = lsta;
+ lvif->lvif_bss_synched = true;
+
+ /* Insert the [l]sta into the list of known stations. */
TAILQ_INSERT_TAIL(&lvif->lsta_head, lsta, lsta_entry);
LKPI_80211_LVIF_UNLOCK(lvif);
@@ -1048,10 +1298,11 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NOTEXIST, ("%s: lsta %p state not "
"NOTEXIST: %#x\n", __func__, lsta, lsta->state));
- sta = LSTA_TO_STA(lsta);
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE);
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
if (error != 0) {
IMPROVE("do we need to undo the chan ctx?");
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
}
#if 0
@@ -1060,6 +1311,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+#if 0
/*
* Wakeup all queues now that sta is there so we have as much time to
* possibly prepare the queue in the driver to be ready for the 1st
@@ -1068,7 +1320,8 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* XXX-BZ and by now we know that this does not work on all drivers
* for all queues.
*/
- lkpi_wake_tx_queues(hw, sta, false, false);
+ lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), false, false);
+#endif
/* Start mgd_prepare_tx. */
memset(&prep_tx_info, 0, sizeof(prep_tx_info));
@@ -1086,7 +1339,12 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
*/
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
+ /*
+ * Release the reference that keop the ni stable locally
+ * during the work of this function.
+ */
if (ni != NULL)
ieee80211_free_node(ni);
return (error);
@@ -1110,14 +1368,29 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
- /* Keep ni around. */
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
+ LKPI_80211_LVIF_LOCK(lvif);
+#ifdef LINUXKPI_DEBUG_80211
+ /* XXX-BZ KASSERT later; state going down so no action. */
+ if (lvif->lvif_bss == NULL)
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+ ni = lsta->ni; /* Reference held for lvif_bss. */
sta = LSTA_TO_STA(lsta);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -1148,9 +1421,11 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
"NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST);
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
if (error != 0) {
IMPROVE("do we need to undo the chan ctx?");
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
}
#if 0
@@ -1159,28 +1434,40 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* Remove ni reference for this cache of lsta. */
+ lvif->lvif_bss = NULL;
+ lvif->lvif_bss_synched = false;
+ LKPI_80211_LVIF_UNLOCK(lvif);
lkpi_lsta_remove(lsta, lvif);
+ /*
+ * The very last release the reference on the ni for the ni/lsta on
+ * lvif->lvif_bss. Upon return from this both ni and lsta are invalid
+ * and potentially freed.
+ */
+ ieee80211_free_node(ni);
/* conf_tx */
/* Take the chan ctx down. */
if (vif->chanctx_conf != NULL) {
+ struct lkpi_chanctx *lchanctx;
struct ieee80211_chanctx_conf *conf;
conf = vif->chanctx_conf;
/* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf);
+ lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
/* NB: vif->chanctx_conf is NULL now. */
/* Remove chan ctx. */
lkpi_80211_mo_remove_chanctx(hw, conf);
- free(conf, M_LKPI80211);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
+ free(lchanctx, M_LKPI80211);
}
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
- if (ni != NULL)
- ieee80211_free_node(ni);
return (error);
}
@@ -1202,9 +1489,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
- struct ieee80211_node *ni;
struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
struct ieee80211_prep_tx_info prep_tx_info;
int error;
@@ -1214,21 +1499,39 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- ni = NULL;
+ LKPI_80211_LHW_LOCK(lhw);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* XXX-BZ KASSERT later? */
+ if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ error = ENOTRECOVERABLE;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+
+ KASSERT(lsta != NULL, ("%s: lsta %p\n", __func__, lsta));
/* Finish auth. */
IMPROVE("event callback");
/* Update sta_state (NONE to AUTH). */
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
"NONE: %#x\n", __func__, lsta, lsta->state));
- sta = LSTA_TO_STA(lsta);
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
/* End mgd_complete_tx. */
if (lsta->in_mgd) {
@@ -1249,7 +1552,7 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
}
/* Wake tx queue to get packet out. */
- lkpi_wake_tx_queues(hw, sta, true, true);
+ lkpi_wake_tx_queues(hw, LSTA_TO_STA(lsta), true, true);
/*
* <twiddle> .. we end up in "assoc_to_run"
@@ -1262,9 +1565,8 @@ lkpi_sta_auth_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, in
*/
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
- if (ni != NULL)
- ieee80211_free_node(ni);
return (error);
}
@@ -1276,19 +1578,37 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
- struct ieee80211_node *ni;
struct lkpi_sta *lsta;
struct ieee80211_prep_tx_info prep_tx_info;
+ int error;
lhw = vap->iv_ic->ic_softc;
hw = LHW_TO_HW(lhw);
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
- ni = ieee80211_ref_node(vap->iv_bss);
-
IEEE80211_UNLOCK(vap->iv_ic);
- lsta = ni->ni_drv_data;
+ LKPI_80211_LHW_LOCK(lhw);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* XXX-BZ KASSERT later? */
+ if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+
+ KASSERT(lsta != NULL, ("%s: lsta %p! lvif %p vap %p\n", __func__,
+ lsta, lvif, vap));
IMPROVE("event callback?");
@@ -1310,11 +1630,12 @@ lkpi_sta_a_to_a(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
lsta->in_mgd = true;
}
+ error = 0;
+out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
- if (ni != NULL)
- ieee80211_free_node(ni);
- return (0);
+ return (error);
}
static int
@@ -1336,15 +1657,30 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
- /* Keep ni around. */
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
+ IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+#ifdef LINUXKPI_DEBUG_80211
+ /* XXX-BZ KASSERT later; state going down so no action. */
+ if (lvif->lvif_bss == NULL)
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+
+ ni = lsta->ni; /* Reference held for lvif_bss. */
sta = LSTA_TO_STA(lsta);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- IEEE80211_UNLOCK(vap->iv_ic);
-
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -1357,14 +1693,19 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
lsta->in_mgd = true;
}
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
error = lvif->iv_newstate(vap, nstate, arg);
- if (error != 0)
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
+ "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
goto outni;
+ }
IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1394,64 +1735,83 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
"AUTH: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ /* Update bss info (bss_info_changed) (assoc, aid, ..). */
+ /*
+ * We need to do this now, before sta changes to IEEE80211_STA_NOTEXIST
+ * as otherwise drivers (iwlwifi at least) will silently not remove
+ * the sta from the firmware and when we will add a new one trigger
+ * a fw assert.
+ */
+ lkpi_disassoc(sta, vif, lhw);
+
/* Adjust sta and change state (from NONE) to NOTEXIST. */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
"NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST);
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
if (error != 0) {
IMPROVE("do we need to undo the chan ctx?");
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
}
-#if 0
- lsta->added_to_drv = false; /* mo manages. */
-#endif
-
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- /* Update bss info (bss_info_changed) (assoc, aid, ..). */
- /* We need to do this now, can only do after sta is IEEE80211_STA_NOTEXIST. */
- lkpi_disassoc(sta, vif, lhw);
+ lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */
IMPROVE("Any bss_info changes to announce?");
bss_changed = 0;
vif->bss_conf.qos = 0;
bss_changed |= BSS_CHANGED_QOS;
- vif->bss_conf.ssid_len = 0;
- memset(vif->bss_conf.ssid, '\0', sizeof(vif->bss_conf.ssid));
+ vif->cfg.ssid_len = 0;
+ memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
bss_changed |= BSS_CHANGED_BSSID;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* Remove ni reference for this cache of lsta. */
+ lvif->lvif_bss = NULL;
+ lvif->lvif_bss_synched = false;
+ LKPI_80211_LVIF_UNLOCK(lvif);
lkpi_lsta_remove(lsta, lvif);
+ /*
+ * The very last release the reference on the ni for the ni/lsta on
+ * lvif->lvif_bss. Upon return from this both ni and lsta are invalid
+ * and potentially freed.
+ */
+ ieee80211_free_node(ni);
/* conf_tx */
/* Take the chan ctx down. */
if (vif->chanctx_conf != NULL) {
+ struct lkpi_chanctx *lchanctx;
struct ieee80211_chanctx_conf *conf;
conf = vif->chanctx_conf;
/* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf);
+ lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
/* NB: vif->chanctx_conf is NULL now. */
/* Remove chan ctx. */
lkpi_80211_mo_remove_chanctx(hw, conf);
- free(conf, M_LKPI80211);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
+ free(lchanctx, M_LKPI80211);
}
error = EALREADY;
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
outni:
- if (ni != NULL)
- ieee80211_free_node(ni);
return (error);
}
@@ -1508,36 +1868,65 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif = LVIF_TO_VIF(lvif);
IEEE80211_UNLOCK(vap->iv_ic);
- ni = NULL;
+ LKPI_80211_LHW_LOCK(lhw);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* XXX-BZ KASSERT later? */
+ if (!lvif->lvif_bss_synched || lvif->lvif_bss == NULL) {
+#ifdef LINUXKPI_DEBUG_80211
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ error = ENOTRECOVERABLE;
+ goto out;
+ }
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+
+ ni = lsta->ni; /* Reference held for lvif_bss. */
IMPROVE("ponder some of this moved to ic_newassoc, scan_assoc_success, "
"and to lesser extend ieee80211_notify_node_join");
/* Finish assoc. */
/* Update sta_state (AUTH to ASSOC) and set aid. */
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
- KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
"AUTH: %#x\n", __func__, lsta, lsta->state));
sta = LSTA_TO_STA(lsta);
sta->aid = IEEE80211_NODE_AID(ni);
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC);
- if (error != 0)
+#ifdef LKPI_80211_WME
+ if (vap->iv_flags & IEEE80211_F_WME)
+ sta->wme = true;
+#endif
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
IMPROVE("wme / conf_tx [all]");
/* Update bss info (bss_info_changed) (assoc, aid, ..). */
bss_changed = 0;
- if (!vif->bss_conf.assoc || vif->bss_conf.aid != IEEE80211_NODE_AID(ni)) {
- vif->bss_conf.assoc = true;
- vif->bss_conf.aid = IEEE80211_NODE_AID(ni);
+#ifdef LKPI_80211_WME
+ bss_changed |= lkpi_wme_update(lhw, vap, true);
+#endif
+ if (!vif->cfg.assoc || vif->cfg.aid != IEEE80211_NODE_AID(ni)) {
+ vif->cfg.assoc = true;
+ vif->cfg.aid = IEEE80211_NODE_AID(ni);
bss_changed |= BSS_CHANGED_ASSOC;
}
/* We set SSID but this is not BSSID! */
- vif->bss_conf.ssid_len = ni->ni_esslen;
- memcpy(vif->bss_conf.ssid, ni->ni_essid, ni->ni_esslen);
+ vif->cfg.ssid_len = ni->ni_esslen;
+ memcpy(vif->cfg.ssid, ni->ni_essid, ni->ni_esslen);
if ((vap->iv_flags & IEEE80211_F_SHPREAMBLE) !=
vif->bss_conf.use_short_preamble) {
vif->bss_conf.use_short_preamble ^= 1;
@@ -1588,13 +1977,20 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
IMPROVE("net80211 does not consider node authorized");
}
+#if defined(LKPI_80211_HT)
+ IMPROVE("Is this the right spot, has net80211 done all updates already?");
+ lkpi_sta_sync_ht_from_ni(sta, ni, NULL);
+#endif
+
/* Update sta_state (ASSOC to AUTHORIZED). */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
"ASSOC: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTHORIZED);
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTHORIZED);
if (error != 0) {
IMPROVE("undo some changes?");
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTHORIZED) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
}
@@ -1611,9 +2007,8 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
- if (ni != NULL)
- ieee80211_free_node(ni);
return (error);
}
@@ -1649,14 +2044,29 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
- /* Keep ni around. */
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
+ LKPI_80211_LVIF_LOCK(lvif);
+#ifdef LINUXKPI_DEBUG_80211
+ /* XXX-BZ KASSERT later; state going down so no action. */
+ if (lvif->lvif_bss == NULL)
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+
+ ni = lsta->ni; /* Reference held for lvif_bss. */
sta = LSTA_TO_STA(lsta);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -1670,14 +2080,19 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta->in_mgd = true;
}
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
error = lvif->iv_newstate(vap, nstate, arg);
- if (error != 0)
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
+ "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
goto outni;
+ }
IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1709,9 +2124,12 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
"AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1719,9 +2137,12 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
"ASSOC: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1732,10 +2153,9 @@ lkpi_sta_run_to_assoc(struct ieee80211vap *vap, enum ieee80211_state nstate, int
error = EALREADY;
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
outni:
- if (ni != NULL)
- ieee80211_free_node(ni);
return (error);
}
@@ -1758,15 +2178,30 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
- /* Keep ni around. */
- ni = ieee80211_ref_node(vap->iv_bss);
- lsta = ni->ni_drv_data;
+ IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
+
+ LKPI_80211_LVIF_LOCK(lvif);
+#ifdef LINUXKPI_DEBUG_80211
+ /* XXX-BZ KASSERT later; state going down so no action. */
+ if (lvif->lvif_bss == NULL)
+ ic_printf(vap->iv_ic, "%s:%d: lvif %p vap %p iv_bss %p lvif_bss %p "
+ "lvif_bss->ni %p synched %d\n", __func__, __LINE__,
+ lvif, vap, vap->iv_bss, lvif->lvif_bss,
+ (lvif->lvif_bss != NULL) ? lvif->lvif_bss->ni : NULL,
+ lvif->lvif_bss_synched);
+#endif
+ lsta = lvif->lvif_bss;
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ KASSERT(lsta != NULL && lsta->ni != NULL, ("%s: lsta %p ni %p "
+ "lvif %p vap %p\n", __func__,
+ lsta, (lsta != NULL) ? lsta->ni : NULL, lvif, vap));
+
+ ni = lsta->ni; /* Reference held for lvif_bss. */
sta = LSTA_TO_STA(lsta);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
- IEEE80211_UNLOCK(vap->iv_ic);
-
/* flush, drop. */
lkpi_80211_mo_flush(hw, vif, nitems(sta->txq), true);
@@ -1779,14 +2214,19 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta->in_mgd = true;
}
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
/* Call iv_newstate first so we get potential DISASSOC packet out. */
error = lvif->iv_newstate(vap, nstate, arg);
- if (error != 0)
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: iv_newstate(%p, %d, %d) "
+ "failed: %d\n", __func__, __LINE__, vap, nstate, arg, error);
goto outni;
+ }
IEEE80211_UNLOCK(vap->iv_ic);
+ LKPI_80211_LHW_LOCK(lhw);
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1816,9 +2256,12 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_AUTHORIZED, ("%s: lsta %p state not "
"AUTHORIZED: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_ASSOC);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_ASSOC);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(ASSOC) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1826,9 +2269,12 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_ASSOC, ("%s: lsta %p state not "
"ASSOC: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_AUTH);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_AUTH);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(AUTH) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
@@ -1836,67 +2282,81 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_AUTH, ("%s: lsta %p state not "
"AUTH: %#x\n", __func__, lsta, lsta->state));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NONE);
- if (error != 0)
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NONE);
+ if (error != 0) {
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NONE) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
+ }
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
+ /* Update bss info (bss_info_changed) (assoc, aid, ..). */
+ /*
+ * One would expect this to happen when going off AUTHORIZED.
+ * See comment there; removes the sta from fw.
+ */
+ lkpi_disassoc(sta, vif, lhw);
+
/* Adjust sta and change state (from NONE) to NOTEXIST. */
KASSERT(lsta != NULL, ("%s: ni %p lsta is NULL\n", __func__, ni));
KASSERT(lsta->state == IEEE80211_STA_NONE, ("%s: lsta %p state not "
"NONE: %#x, nstate %d arg %d\n", __func__, lsta, lsta->state, nstate, arg));
- error = lkpi_80211_mo_sta_state(hw, vif, sta, IEEE80211_STA_NOTEXIST);
+ error = lkpi_80211_mo_sta_state(hw, vif, lsta, IEEE80211_STA_NOTEXIST);
if (error != 0) {
IMPROVE("do we need to undo the chan ctx?");
+ ic_printf(vap->iv_ic, "%s:%d: mo_sta_state(NOTEXIST) "
+ "failed: %d\n", __func__, __LINE__, error);
goto out;
}
-#if 0
- lsta->added_to_drv = false; /* mo manages. */
-#endif
- lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
-
- /* Update bss info (bss_info_changed) (assoc, aid, ..). */
- /*
- * One would expect this to happen when going off AUTHORIZED.
- * See comment there; removes the sta from fw.
- */
- lkpi_disassoc(sta, vif, lhw);
+ lkpi_lsta_dump(lsta, ni, __func__, __LINE__); /* sta no longer save to use. */
IMPROVE("Any bss_info changes to announce?");
bss_changed = 0;
vif->bss_conf.qos = 0;
bss_changed |= BSS_CHANGED_QOS;
- vif->bss_conf.ssid_len = 0;
- memset(vif->bss_conf.ssid, '\0', sizeof(vif->bss_conf.ssid));
+ vif->cfg.ssid_len = 0;
+ memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
bss_changed |= BSS_CHANGED_BSSID;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ LKPI_80211_LVIF_LOCK(lvif);
+ /* Remove ni reference for this cache of lsta. */
+ lvif->lvif_bss = NULL;
+ lvif->lvif_bss_synched = false;
+ LKPI_80211_LVIF_UNLOCK(lvif);
lkpi_lsta_remove(lsta, lvif);
+ /*
+ * The very last release the reference on the ni for the ni/lsta on
+ * lvif->lvif_bss. Upon return from this both ni and lsta are invalid
+ * and potentially freed.
+ */
+ ieee80211_free_node(ni);
/* conf_tx */
/* Take the chan ctx down. */
if (vif->chanctx_conf != NULL) {
+ struct lkpi_chanctx *lchanctx;
struct ieee80211_chanctx_conf *conf;
conf = vif->chanctx_conf;
/* Remove vif context. */
- lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->chanctx_conf);
+ lkpi_80211_mo_unassign_vif_chanctx(hw, vif, &vif->bss_conf, &vif->chanctx_conf);
/* NB: vif->chanctx_conf is NULL now. */
/* Remove chan ctx. */
lkpi_80211_mo_remove_chanctx(hw, conf);
- free(conf, M_LKPI80211);
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(conf);
+ free(lchanctx, M_LKPI80211);
}
error = EALREADY;
out:
+ LKPI_80211_LHW_UNLOCK(lhw);
IEEE80211_LOCK(vap->iv_ic);
outni:
- if (ni != NULL)
- ieee80211_free_node(ni);
return (error);
}
@@ -2041,12 +2501,11 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
}
if (error != 0) {
- /* XXX-BZ currently expected so ignore. */
ic_printf(vap->iv_ic, "%s: error %d during state transition "
"%d (%s) -> %d (%s)\n", __func__, error,
ostate, ieee80211_state_name[ostate],
nstate, ieee80211_state_name[nstate]);
- /* return (error); */
+ return (error);
}
#ifdef LINUXKPI_DEBUG_80211
@@ -2070,60 +2529,25 @@ static struct ieee80211_node *
lkpi_iv_update_bss(struct ieee80211vap *vap, struct ieee80211_node *ni)
{
struct lkpi_vif *lvif;
- struct ieee80211_node *obss;
- struct lkpi_sta *lsta;
- struct ieee80211_sta *sta;
+ struct ieee80211_node *rni;
- obss = vap->iv_bss;
-
-#ifdef LINUXKPI_DEBUG_80211
- if (linuxkpi_debug_80211 & D80211_TRACE)
- ic_printf(vap->iv_ic, "%s: obss %p ni_drv_data %p "
- "ni %p ni_drv_data %p\n", __func__,
- obss, (obss != NULL) ? obss->ni_drv_data : NULL,
- ni, (ni != NULL) ? ni->ni_drv_data : NULL);
-#endif
-
- /* Nothing to copy from. Just return. */
- if (obss == NULL || obss->ni_drv_data == NULL)
- goto out;
-
- /* Nothing to copy to. Just return. */
- IMPROVE("clearing the obss might still be needed?");
- if (ni == NULL)
- goto out;
+ IEEE80211_LOCK_ASSERT(vap->iv_ic);
- /* Nothing changed? panic? */
- if (obss == ni)
- goto out;
+ lvif = VAP_TO_LVIF(vap);
- lsta = obss->ni_drv_data;
- obss->ni_drv_data = ni->ni_drv_data;
- ni->ni_drv_data = lsta;
- if (lsta != NULL) {
- lsta->ni = ni;
- sta = LSTA_TO_STA(lsta);
- IEEE80211_ADDR_COPY(sta->addr, lsta->ni->ni_macaddr);
- }
- lsta = obss->ni_drv_data;
- if (lsta != NULL) {
- lsta->ni = obss;
- sta = LSTA_TO_STA(lsta);
- IEEE80211_ADDR_COPY(sta->addr, lsta->ni->ni_macaddr);
- }
+ LKPI_80211_LVIF_LOCK(lvif);
+ lvif->lvif_bss_synched = false;
+ LKPI_80211_LVIF_UNLOCK(lvif);
-out:
- lvif = VAP_TO_LVIF(vap);
- return (lvif->iv_update_bss(vap, ni));
+ rni = lvif->iv_update_bss(vap, ni);
+ return (rni);
}
+#ifdef LKPI_80211_WME
static int
-lkpi_ic_wme_update(struct ieee80211com *ic)
+lkpi_wme_update(struct lkpi_hw *lhw, struct ieee80211vap *vap, bool planned)
{
- /* This needs queuing and go at the right moment. */
-#ifdef WITH_WME_UPDATE
- struct ieee80211vap *vap;
- struct lkpi_hw *lhw;
+ struct ieee80211com *ic;
struct ieee80211_hw *hw;
struct lkpi_vif *lvif;
struct ieee80211_vif *vif;
@@ -2133,56 +2557,79 @@ lkpi_ic_wme_update(struct ieee80211com *ic)
enum ieee80211_bss_changed changed;
int error;
uint16_t ac;
-#endif
IMPROVE();
KASSERT(WME_NUM_AC == IEEE80211_NUM_ACS, ("%s: WME_NUM_AC %d != "
"IEEE80211_NUM_ACS %d\n", __func__, WME_NUM_AC, IEEE80211_NUM_ACS));
-#ifdef WITH_WME_UPDATE
- vap = TAILQ_FIRST(&ic->ic_vaps);
if (vap == NULL)
return (0);
- /* We should factor this out into per-vap (*wme_update). */
- lhw = ic->ic_softc;
+ if ((vap->iv_flags & IEEE80211_F_WME) == 0)
+ return (0);
+
if (lhw->ops->conf_tx == NULL)
return (0);
- /* XXX-BZ check amount of hw queues */
- hw = LHW_TO_HW(lhw);
- lvif = VAP_TO_LVIF(vap);
- vif = LVIF_TO_VIF(lvif);
+ if (!planned && (vap->iv_state != IEEE80211_S_RUN)) {
+ lhw->update_wme = true;
+ return (0);
+ }
+ lhw->update_wme = false;
+ ic = lhw->ic;
ieee80211_wme_ic_getparams(ic, &chp);
IEEE80211_LOCK(ic);
for (ac = 0; ac < WME_NUM_AC; ac++)
wmeparr[ac] = chp.cap_wmeParams[ac];
IEEE80211_UNLOCK(ic);
+ hw = LHW_TO_HW(lhw);
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+
/* Configure tx queues (conf_tx) & send BSS_CHANGED_QOS. */
LKPI_80211_LHW_LOCK(lhw);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
struct wmeParams *wmep;
- /* XXX-BZ should keep this in lvif? */
wmep = &wmeparr[ac];
bzero(&txqp, sizeof(txqp));
txqp.cw_min = wmep->wmep_logcwmin;
txqp.cw_max = wmep->wmep_logcwmax;
txqp.txop = wmep->wmep_txopLimit;
txqp.aifs = wmep->wmep_aifsn;
- error = lkpi_80211_mo_conf_tx(hw, vif, ac, &txqp);
+ error = lkpi_80211_mo_conf_tx(hw, vif, /* link_id */0, ac, &txqp);
if (error != 0)
- printf("%s: conf_tx ac %u failed %d\n",
+ ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
__func__, ac, error);
}
LKPI_80211_LHW_UNLOCK(lhw);
changed = BSS_CHANGED_QOS;
- lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+ if (!planned)
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
+
+ return (changed);
+}
#endif
- return (0);
+static int
+lkpi_ic_wme_update(struct ieee80211com *ic)
+{
+#ifdef LKPI_80211_WME
+ struct ieee80211vap *vap;
+ struct lkpi_hw *lhw;
+
+ IMPROVE("Use the per-VAP callback in net80211.");
+ vap = TAILQ_FIRST(&ic->ic_vaps);
+ if (vap == NULL)
+ return (0);
+
+ lhw = ic->ic_softc;
+
+ lkpi_wme_update(lhw, vap, false);
+#endif
+ return (0); /* unused */
}
static struct ieee80211vap *
@@ -2196,9 +2643,11 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
struct lkpi_vif *lvif;
struct ieee80211vap *vap;
struct ieee80211_vif *vif;
+ struct ieee80211_tx_queue_params txqp;
enum ieee80211_bss_changed changed;
size_t len;
int error, i;
+ uint16_t ac;
if (!TAILQ_EMPTY(&ic->ic_vaps)) /* 1 so far. Add <n> once this works. */
return (NULL);
@@ -2212,6 +2661,8 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
lvif = malloc(len, M_80211_VAP, M_WAITOK | M_ZERO);
mtx_init(&lvif->mtx, "lvif", NULL, MTX_DEF);
TAILQ_INIT(&lvif->lsta_head);
+ lvif->lvif_bss = NULL;
+ lvif->lvif_bss_synched = false;
vap = LVIF_TO_VAP(lvif);
vif = LVIF_TO_VIF(lvif);
@@ -2226,25 +2677,27 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
/* XXX-BZ hardcoded for now! */
#if 1
vif->chanctx_conf = NULL;
- vif->bss_conf.idle = true;
- vif->bss_conf.ps = false;
+ vif->bss_conf.vif = vif;
+ /* vap->iv_myaddr is not set until net80211::vap_setup or vap_attach. */
+ IEEE80211_ADDR_COPY(vif->bss_conf.addr, mac);
+ vif->bss_conf.link_id = 0; /* Non-MLO operation. */
vif->bss_conf.chandef.width = NL80211_CHAN_WIDTH_20_NOHT;
vif->bss_conf.use_short_preamble = false; /* vap->iv_flags IEEE80211_F_SHPREAMBLE */
vif->bss_conf.use_short_slot = false; /* vap->iv_flags IEEE80211_F_SHSLOT */
vif->bss_conf.qos = false;
vif->bss_conf.use_cts_prot = false; /* vap->iv_protmode */
vif->bss_conf.ht_operation_mode = IEEE80211_HT_OP_MODE_PROTECTION_NONE;
- vif->bss_conf.assoc = false;
- vif->bss_conf.aid = 0;
+ vif->cfg.aid = 0;
+ vif->cfg.assoc = false;
+ vif->cfg.idle = true;
+ vif->cfg.ps = false;
+ IMPROVE("Check other fields and then figure out whats is left elsewhere of them");
/*
* We need to initialize it to something as the bss_info_changed call
* will try to copy from it in iwlwifi and NULL is a panic.
* We will set the proper one in scan_to_auth() before being assoc.
- * NB: the logic there with using an array as bssid_override and checking
- * for non-NULL later is flawed but in their workflow does not seem to
- * matter.
*/
- vif->bss_conf.bssid = zerobssid;
+ vif->bss_conf.bssid = ieee80211broadcastaddr;
#endif
#if 0
vif->bss_conf.dtim_period = 0; /* IEEE80211_DTIM_DEFAULT ; must stay 0. */
@@ -2255,6 +2708,12 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
vif->bss_conf.beacon_int = 16;
#endif
+ /* Link Config */
+ vif->link_conf[0] = &vif->bss_conf;
+ for (i = 0; i < nitems(vif->link_conf); i++) {
+ IMPROVE("more than 1 link one day");
+ }
+
/* Setup queue defaults; driver may override in (*add_interface). */
for (i = 0; i < IEEE80211_NUM_ACS; i++) {
if (ieee80211_hw_check(hw, QUEUE_CONTROL))
@@ -2263,6 +2722,9 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
vif->hw_queue[i] = i;
else
vif->hw_queue[i] = 0;
+
+ /* Initialize the queue to running. Stopped? */
+ lvif->hw_queue_stopped[i] = false;
}
vif->cab_queue = IEEE80211_INVAL_HW_QUEUE;
@@ -2270,7 +2732,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
error = lkpi_80211_mo_start(hw);
if (error != 0) {
- printf("%s: failed to start hw: %d\n", __func__, error);
+ ic_printf(ic, "%s: failed to start hw: %d\n", __func__, error);
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
return (NULL);
@@ -2279,7 +2741,7 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
error = lkpi_80211_mo_add_interface(hw, vif);
if (error != 0) {
IMPROVE(); /* XXX-BZ mo_stop()? */
- printf("%s: failed to add interface: %d\n", __func__, error);
+ ic_printf(ic, "%s: failed to add interface: %d\n", __func__, error);
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
return (NULL);
@@ -2293,7 +2755,24 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
changed = 0;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
- /* conf_tx setup; default WME? */
+ /* Configure tx queues (conf_tx), default WME & send BSS_CHANGED_QOS. */
+ IMPROVE("Hardcoded values; to fix see 802.11-2016, 9.4.2.29 EDCA Parameter Set element");
+ LKPI_80211_LHW_LOCK(lhw);
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+
+ bzero(&txqp, sizeof(txqp));
+ txqp.cw_min = 15;
+ txqp.cw_max = 1023;
+ txqp.txop = 0;
+ txqp.aifs = 2;
+ error = lkpi_80211_mo_conf_tx(hw, vif, /* link_id */0, ac, &txqp);
+ if (error != 0)
+ ic_printf(ic, "%s: conf_tx ac %u failed %d\n",
+ __func__, ac, error);
+ }
+ LKPI_80211_LHW_UNLOCK(lhw);
+ changed = BSS_CHANGED_QOS;
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
/* Force MC init. */
lkpi_update_mcast_filter(ic, true);
@@ -2316,6 +2795,10 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
#endif
}
+#ifdef LKPI_80211_HT
+ /* Stay with the iv_ampdu_rxmax,limit / iv_ampdu_density defaults until later. */
+#endif
+
ieee80211_ratectl_init(vap);
/* Complete setup. */
@@ -2328,9 +2811,9 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
ic->ic_set_channel(ic);
/* XXX-BZ do we need to be able to update these? */
- hw->wiphy->frag_threshold = vap->iv_fragthreshold;
+ hw->wiphy->frag_threshold = vap->iv_fragthreshold;
lkpi_80211_mo_set_frag_threshold(hw, vap->iv_fragthreshold);
- hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
+ hw->wiphy->rts_threshold = vap->iv_rtsthreshold;
lkpi_80211_mo_set_rts_threshold(hw, vap->iv_rtsthreshold);
/* any others? */
IMPROVE();
@@ -2338,6 +2821,23 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
return (vap);
}
+void
+linuxkpi_ieee80211_unregister_hw(struct ieee80211_hw *hw)
+{
+
+ wiphy_unregister(hw->wiphy);
+ linuxkpi_ieee80211_ifdetach(hw);
+
+ IMPROVE();
+}
+
+void
+linuxkpi_ieee80211_restart_hw(struct ieee80211_hw *hw)
+{
+
+ TODO();
+}
+
static void
lkpi_ic_vap_delete(struct ieee80211vap *vap)
{
@@ -2356,10 +2856,17 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry);
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
- lkpi_80211_mo_remove_interface(hw, vif);
ieee80211_ratectl_deinit(vap);
ieee80211_vap_detach(vap);
+
+ IMPROVE("clear up other bits in this state");
+
+ lkpi_80211_mo_remove_interface(hw, vif);
+
+ /* Single VAP, so we can do this here. */
+ lkpi_80211_mo_stop(hw);
+
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
}
@@ -2391,23 +2898,35 @@ static void
lkpi_ic_parent(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
+#ifdef HW_START_STOP
struct ieee80211_hw *hw;
int error;
+#endif
bool start_all;
IMPROVE();
lhw = ic->ic_softc;
+#ifdef HW_START_STOP
hw = LHW_TO_HW(lhw);
+#endif
start_all = false;
+ /* IEEE80211_UNLOCK(ic); */
+ LKPI_80211_LHW_LOCK(lhw);
if (ic->ic_nrunning > 0) {
+#ifdef HW_START_STOP
error = lkpi_80211_mo_start(hw);
if (error == 0)
+#endif
start_all = true;
} else {
+#ifdef HW_START_STOP
lkpi_80211_mo_stop(hw);
+#endif
}
+ LKPI_80211_LHW_UNLOCK(lhw);
+ /* IEEE80211_LOCK(ic); */
if (start_all)
ieee80211_start_all(ic);
@@ -2454,11 +2973,13 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
{
struct ieee80211_supported_band *supband;
struct linuxkpi_ieee80211_channel *channels;
+ struct ieee80211com *ic;
const struct ieee80211_channel *chan;
const struct ieee80211_rateset *rs;
uint8_t *pb;
int band, i;
+ ic = vap->iv_ic;
for (band = 0; band < NUM_NL80211_BANDS; band++) {
if ((band_mask & (1 << band)) == 0)
continue;
@@ -2479,7 +3000,7 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
if (channels[i].flags & IEEE80211_CHAN_DISABLED)
continue;
- chan = ieee80211_find_channel(vap->iv_ic,
+ chan = ieee80211_find_channel(ic,
channels[i].center_freq, 0);
if (chan != NULL)
break;
@@ -2490,10 +3011,31 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
continue;
pb = p;
- rs = ieee80211_get_suprates(vap->iv_ic, chan); /* calls chan2mode */
+ rs = ieee80211_get_suprates(ic, chan); /* calls chan2mode */
p = ieee80211_add_rates(p, rs);
p = ieee80211_add_xrates(p, rs);
+#if defined(LKPI_80211_HT)
+ if ((vap->iv_flags_ht & IEEE80211_FHT_HT) != 0) {
+ struct ieee80211_channel *c;
+
+ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ vap->iv_flags_ht);
+ p = ieee80211_add_htcap_ch(p, vap, c);
+ }
+#endif
+#if defined(LKPI_80211_VHT)
+ if ((vap->iv_vht_flags & IEEE80211_FVHT_VHT) != 0) {
+ struct ieee80211_channel *c;
+
+ c = ieee80211_ht_adjust_channel(ic, ic->ic_curchan,
+ vap->iv_flags_ht);
+ c = ieee80211_vht_adjust_channel(ic, c,
+ vap->iv_vht_flags);
+ p = ieee80211_add_vhtcap_ch(p, vap, c);
+ }
+#endif
+
scan_ies->ies[band] = pb;
scan_ies->len[band] = p - pb;
}
@@ -2526,12 +3068,17 @@ lkpi_ic_scan_start(struct ieee80211com *ic)
struct ieee80211_scan_state *ss;
struct ieee80211vap *vap;
int error;
+ bool is_hw_scan;
lhw = ic->ic_softc;
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
/* A scan is still running. */
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
return;
}
+ is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
ss = ic->ic_scan;
vap = ss->ss_vap;
@@ -2541,7 +3088,7 @@ lkpi_ic_scan_start(struct ieee80211com *ic)
}
hw = LHW_TO_HW(lhw);
- if ((lhw->scan_flags & LKPI_LHW_SCAN_HW) == 0) {
+ if (!is_hw_scan) {
/* If hw_scan is cleared clear FEXT_SCAN_OFFLOAD too. */
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
sw_scan:
@@ -2568,12 +3115,7 @@ sw_scan:
int band, i, ssid_count, common_ie_len;
uint32_t band_mask;
uint8_t *ie, *ieend;
-
- if (!ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
- IMPROVE("individual band scans not yet supported");
- /* In theory net80211 would have to drive this. */
- return;
- }
+ bool running;
ssid_count = min(ss->ss_nssid, hw->wiphy->max_scan_ssids);
ssids_len = ssid_count * sizeof(*ssids);
@@ -2587,6 +3129,18 @@ sw_scan:
ss->ss_chans[ss->ss_next + i]);
band_mask |= (1 << band);
}
+
+ if (!ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
+ IMPROVE("individual band scans not yet supported, only scanning first band");
+ /* In theory net80211 should drive this. */
+ /* Probably we need to add local logic for now;
+ * need to deal with scan_complete
+ * and cancel_scan and keep local state.
+ * Also cut the nchan down above.
+ */
+ /* XXX-BZ ath10k does not set this but still does it? &$%^ */
+ }
+
chan_len = nchan * (sizeof(lc) + sizeof(*lc));
common_ie_len = 0;
@@ -2603,10 +3157,7 @@ sw_scan:
common_ie_len, hw->wiphy->max_scan_ie_len);
}
- KASSERT(lhw->hw_req == NULL, ("%s: ic %p lhw %p hw_req %p "
- "!= NULL\n", __func__, ic, lhw, lhw->hw_req));
-
- lhw->hw_req = hw_req = malloc(sizeof(*hw_req) + ssids_len +
+ hw_req = malloc(sizeof(*hw_req) + ssids_len +
s6ghzlen + chan_len + lhw->supbands * lhw->scan_ie_len +
common_ie_len, M_LKPI80211, M_WAITOK | M_ZERO);
@@ -2624,6 +3175,7 @@ sw_scan:
memcpy(hw_req->req.mac_addr, xxx, IEEE80211_ADDR_LEN);
memset(hw_req->req.mac_addr_mask, 0xxx, IEEE80211_ADDR_LEN);
#endif
+ eth_broadcast_addr(hw_req->req.bssid);
hw_req->req.n_channels = nchan;
cpp = (struct linuxkpi_ieee80211_channel **)(hw_req + 1);
@@ -2636,7 +3188,7 @@ sw_scan:
c = ss->ss_chans[ss->ss_next + i];
lc->hw_value = c->ic_ieee;
- lc->center_freq = c->ic_freq;
+ lc->center_freq = c->ic_freq; /* XXX */
/* lc->flags */
lc->band = lkpi_net80211_chan_to_nl80211_band(c);
lc->max_power = c->ic_maxpower;
@@ -2673,12 +3225,48 @@ sw_scan:
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
+
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ /* Re-check under lock. */
+ running = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+ if (!running) {
+ KASSERT(lhw->hw_req == NULL, ("%s: ic %p lhw %p hw_req %p "
+ "!= NULL\n", __func__, ic, lhw, lhw->hw_req));
+
+ lhw->scan_flags |= LKPI_LHW_SCAN_RUNNING;
+ lhw->hw_req = hw_req;
+ }
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ if (running) {
+ free(hw_req, M_LKPI80211);
+ return;
+ }
+
error = lkpi_80211_mo_hw_scan(hw, vif, hw_req);
if (error != 0) {
ieee80211_cancel_scan(vap);
- free(hw_req, M_LKPI80211);
- lhw->hw_req = NULL;
+ /*
+ * ieee80211_scan_completed must be called in either
+ * case of error or none. So let the free happen there
+ * and only there.
+ * That would be fine in theory but in practice drivers
+ * behave differently:
+ * ath10k does not return hw_scan until after scan_complete
+ * and can then still return an error.
+ * rtw88 can return 1 or -EBUSY without scan_complete
+ * iwlwifi can return various errors before scan starts
+ * ...
+ * So we cannot rely on that behaviour and have to check
+ * and balance between both code paths.
+ */
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0) {
+ free(lhw->hw_req, M_LKPI80211);
+ lhw->hw_req = NULL;
+ lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
+ }
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
/*
* XXX-SIGH magic number.
@@ -2686,7 +3274,15 @@ sw_scan:
* not possible. Fall back to sw scan in that case.
*/
if (error == 1) {
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
lhw->scan_flags &= ~LKPI_LHW_SCAN_HW;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ /*
+ * XXX If we clear this now and later a driver
+ * thinks it * can do a hw_scan again, we will
+ * currently not re-enable it?
+ */
+ vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
ieee80211_start_scan(vap,
IEEE80211_SCAN_ACTIVE |
IEEE80211_SCAN_NOPICK |
@@ -2708,13 +3304,18 @@ static void
lkpi_ic_scan_end(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
+ bool is_hw_scan;
lhw = ic->ic_softc;
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) == 0) {
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
return;
}
+ is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
- if ((lhw->scan_flags & LKPI_LHW_SCAN_HW) == 0) {
+ if (!is_hw_scan) {
struct ieee80211_scan_state *ss;
struct ieee80211vap *vap;
struct ieee80211_hw *hw;
@@ -2741,9 +3342,13 @@ lkpi_ic_scan_curchan(struct ieee80211_scan_state *ss,
unsigned long maxdwell)
{
struct lkpi_hw *lhw;
+ bool is_hw_scan;
lhw = ss->ss_ic->ic_softc;
- if ((lhw->scan_flags & LKPI_LHW_SCAN_HW) == 0)
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ if (!is_hw_scan)
lhw->ic_scan_curchan(ss, maxdwell);
}
@@ -2751,9 +3356,13 @@ static void
lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss)
{
struct lkpi_hw *lhw;
+ bool is_hw_scan;
lhw = ss->ss_ic->ic_softc;
- if ((lhw->scan_flags & LKPI_LHW_SCAN_HW) == 0)
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ if (!is_hw_scan)
lhw->ic_scan_mindwell(ss);
}
@@ -2765,6 +3374,7 @@ lkpi_ic_set_channel(struct ieee80211com *ic)
struct ieee80211_channel *c;
struct linuxkpi_ieee80211_channel *chan;
int error;
+ bool hw_scan_running;
lhw = ic->ic_softc;
@@ -2773,8 +3383,12 @@ lkpi_ic_set_channel(struct ieee80211com *ic)
return;
/* If we have a hw_scan running do not switch channels. */
- if ((lhw->scan_flags & (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW)) ==
- (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW))
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ hw_scan_running =
+ (lhw->scan_flags & (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW)) ==
+ (LKPI_LHW_SCAN_RUNNING|LKPI_LHW_SCAN_HW);
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ if (hw_scan_running)
return;
c = ic->ic_curchan;
@@ -2796,6 +3410,9 @@ lkpi_ic_set_channel(struct ieee80211com *ic)
hw = LHW_TO_HW(lhw);
cfg80211_chandef_create(&hw->conf.chandef, chan,
+#ifdef LKPI_80211_HT
+ (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
+#endif
NL80211_CHAN_NO_HT);
error = lkpi_80211_mo_config(hw, IEEE80211_CONF_CHANGE_CHANNEL);
@@ -2862,7 +3479,6 @@ lkpi_ic_node_init(struct ieee80211_node *ni)
{
struct ieee80211com *ic;
struct lkpi_hw *lhw;
- struct lkpi_sta *lsta;
int error;
ic = ni->ni_ic;
@@ -2874,11 +3490,6 @@ lkpi_ic_node_init(struct ieee80211_node *ni)
return (error);
}
- lsta = ni->ni_drv_data;
-
- /* Now take the reference before linking it to the table. */
- lsta->ni = ieee80211_ref_node(ni);
-
/* XXX-BZ Sync other state over. */
IMPROVE();
@@ -2911,30 +3522,15 @@ lkpi_ic_node_free(struct ieee80211_node *ni)
ic = ni->ni_ic;
lhw = ic->ic_softc;
lsta = ni->ni_drv_data;
- if (lsta == NULL)
- goto out;
- /* XXX-BZ free resources, ... */
- IMPROVE();
-
- /* Flush mbufq (make sure to release ni refs!). */
-#ifdef __notyet__
- KASSERT(mbufq_len(&lsta->txq) == 0, ("%s: lsta %p has txq len %d != 0\n",
- __func__, lsta, mbufq_len(&lsta->txq)));
-#endif
- /* Drain taskq. */
-
- /* Drain sta->txq[] */
- mtx_destroy(&lsta->txq_mtx);
+ /* KASSERT lsta is not NULL here. Print ni/ni__refcnt. */
- /* Remove lsta if added_to_drv. */
-
- /* Remove lsta from vif */
- /* Remove ref from lsta node... */
- /* Free lsta. */
- lkpi_lsta_remove(lsta, VAP_TO_LVIF(ni->ni_vap));
+ /*
+ * Pass in the original ni just in case of error we could check that
+ * it is the same as lsta->ni.
+ */
+ lkpi_lsta_free(lsta, ni);
-out:
if (lhw->ic_node_free != NULL)
lhw->ic_node_free(ni);
}
@@ -2946,11 +3542,21 @@ lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
struct lkpi_sta *lsta;
lsta = ni->ni_drv_data;
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ if (!lsta->txq_ready) {
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
+ /*
+ * Free the mbuf (do NOT release ni ref for the m_pkthdr.rcvif!
+ * ieee80211_raw_output() does that in case of error).
+ */
+ m_free(m);
+ return (ENETDOWN);
+ }
/* Queue the packet and enqueue the task to handle it. */
- LKPI_80211_LSTA_LOCK(lsta);
mbufq_enqueue(&lsta->txq, m);
- LKPI_80211_LSTA_UNLOCK(lsta);
+ taskqueue_enqueue(taskqueue_thread, &lsta->txq_task);
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE_TX)
@@ -2959,7 +3565,6 @@ lkpi_ic_raw_xmit(struct ieee80211_node *ni, struct mbuf *m,
mbufq_len(&lsta->txq));
#endif
- taskqueue_enqueue(taskqueue_thread, &lsta->txq_task);
return (0);
}
@@ -2982,6 +3587,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
struct ieee80211_tx_info *info;
struct ieee80211_sta *sta;
struct ieee80211_hdr *hdr;
+ struct lkpi_txq *ltxq;
void *buf;
uint8_t ac, tid;
@@ -3039,7 +3645,7 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
*/
skb = dev_alloc_skb(hw->extra_tx_headroom + m->m_pkthdr.len);
if (skb == NULL) {
- printf("XXX ERROR %s: skb alloc failed\n", __func__);
+ ic_printf(ic, "ERROR %s: skb alloc failed\n", __func__);
ieee80211_free_node(ni);
m_freem(m);
return;
@@ -3065,11 +3671,19 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
hdr = (void *)skb->data;
tid = linuxkpi_ieee80211_get_tid(hdr, true);
if (tid == IEEE80211_NONQOS_TID) { /* == IEEE80211_NUM_TIDS */
- skb->priority = 0;
- ac = IEEE80211_AC_BE;
+ if (!ieee80211_is_data(hdr->frame_control)) {
+ /* MGMT and CTRL frames go on TID 7/VO. */
+ skb->priority = 7;
+ ac = IEEE80211_AC_VO;
+ } else {
+ /* Other non-QOS traffic goes to BE. */
+ /* Contrary to net80211 we MUST NOT promote M_EAPOL. */
+ skb->priority = 0;
+ ac = IEEE80211_AC_BE;
+ }
} else {
skb->priority = tid & IEEE80211_QOS_CTL_TID_MASK;
- ac = tid_to_mac80211_ac[tid & 7];
+ ac = ieee80211e_up_to_ac[tid & 7];
}
skb_set_queue_mapping(skb, ac);
@@ -3084,53 +3698,53 @@ lkpi_80211_txq_tx_one(struct lkpi_sta *lsta, struct mbuf *m)
info->control.flags |= IEEE80211_TX_CTRL_PORT_CTRL_PROTO;
info->control.vif = vif;
/* XXX-BZ info->control.rates */
+#ifdef __notyet__
+#ifdef LKPI_80211_HT
+ info->control.rts_cts_rate_idx=
+ info->control.use_rts= /* RTS */
+ info->control.use_cts_prot= /* RTS/CTS*/
+#endif
+#endif
- lsta = lkpi_find_lsta_by_ni(lvif, ni);
- if (lsta != NULL) {
- sta = LSTA_TO_STA(lsta);
+ sta = LSTA_TO_STA(lsta);
#ifdef LKPI_80211_HW_CRYPTO
- info->control.hw_key = lsta->kc;
+ info->control.hw_key = lsta->kc;
#endif
- } else {
- sta = NULL;
- }
IMPROVE();
- if (sta != NULL) {
- struct lkpi_txq *ltxq;
-
- ltxq = NULL;
- if (!ieee80211_is_data_present(hdr->frame_control)) {
- if (vif->type == NL80211_IFTYPE_STATION &&
- lsta->added_to_drv &&
- sta->txq[IEEE80211_NUM_TIDS] != NULL)
- ltxq = TXQ_TO_LTXQ(sta->txq[IEEE80211_NUM_TIDS]);
- } else if (lsta->added_to_drv &&
- sta->txq[skb->priority] != NULL) {
- ltxq = TXQ_TO_LTXQ(sta->txq[skb->priority]);
- }
- if (ltxq == NULL)
- goto ops_tx;
+ ltxq = NULL;
+ if (!ieee80211_is_data_present(hdr->frame_control)) {
+ if (vif->type == NL80211_IFTYPE_STATION &&
+ lsta->added_to_drv &&
+ sta->txq[IEEE80211_NUM_TIDS] != NULL)
+ ltxq = TXQ_TO_LTXQ(sta->txq[IEEE80211_NUM_TIDS]);
+ } else if (lsta->added_to_drv &&
+ sta->txq[skb->priority] != NULL) {
+ ltxq = TXQ_TO_LTXQ(sta->txq[skb->priority]);
+ }
+ if (ltxq == NULL)
+ goto ops_tx;
- KASSERT(ltxq != NULL, ("%s: lsta %p sta %p m %p skb %p "
- "ltxq %p != NULL\n", __func__, lsta, sta, m, skb, ltxq));
+ KASSERT(ltxq != NULL, ("%s: lsta %p sta %p m %p skb %p "
+ "ltxq %p != NULL\n", __func__, lsta, sta, m, skb, ltxq));
- skb_queue_tail(&ltxq->skbq, skb);
+ LKPI_80211_LTXQ_LOCK(ltxq);
+ skb_queue_tail(&ltxq->skbq, skb);
#ifdef LINUXKPI_DEBUG_80211
- if (linuxkpi_debug_80211 & D80211_TRACE_TX)
- printf("%s:%d mo_wake_tx_queue :: %d %u lsta %p sta %p "
- "ni %p %6D skb %p lxtq %p { qlen %u, ac %d tid %u } "
- "WAKE_TX_Q ac %d prio %u qmap %u\n",
- __func__, __LINE__,
- curthread->td_tid, (unsigned int)ticks,
- lsta, sta, ni, ni->ni_macaddr, ":", skb, ltxq,
- skb_queue_len(&ltxq->skbq), ltxq->txq.ac,
- ltxq->txq.tid, ac, skb->priority, skb->qmap);
+ if (linuxkpi_debug_80211 & D80211_TRACE_TX)
+ printf("%s:%d mo_wake_tx_queue :: %d %u lsta %p sta %p "
+ "ni %p %6D skb %p lxtq %p { qlen %u, ac %d tid %u } "
+ "WAKE_TX_Q ac %d prio %u qmap %u\n",
+ __func__, __LINE__,
+ curthread->td_tid, (unsigned int)ticks,
+ lsta, sta, ni, ni->ni_macaddr, ":", skb, ltxq,
+ skb_queue_len(&ltxq->skbq), ltxq->txq.ac,
+ ltxq->txq.tid, ac, skb->priority, skb->qmap);
#endif
- lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
- return;
- }
+ LKPI_80211_LTXQ_UNLOCK(ltxq);
+ lkpi_80211_mo_wake_tx_queue(hw, &ltxq->txq);
+ return;
ops_tx:
#ifdef LINUXKPI_DEBUG_80211
@@ -3142,9 +3756,7 @@ ops_tx:
#endif
memset(&control, 0, sizeof(control));
control.sta = sta;
-
lkpi_80211_mo_tx(hw, &control, skb);
- return;
}
static void
@@ -3165,9 +3777,13 @@ lkpi_80211_txq_task(void *ctx, int pending)
mbufq_init(&mq, IFQ_MAXLEN);
- LKPI_80211_LSTA_LOCK(lsta);
+ LKPI_80211_LSTA_TXQ_LOCK(lsta);
+ /*
+ * Do not re-check lsta->txq_ready here; we may have a pending
+ * disassoc frame still.
+ */
mbufq_concat(&mq, &lsta->txq);
- LKPI_80211_LSTA_UNLOCK(lsta);
+ LKPI_80211_LSTA_TXQ_UNLOCK(lsta);
m = mbufq_dequeue(&mq);
while (m != NULL) {
@@ -3190,6 +3806,291 @@ lkpi_ic_transmit(struct ieee80211com *ic, struct mbuf *m)
return (lkpi_ic_raw_xmit(ni, m, NULL));
}
+#ifdef LKPI_80211_HT
+static int
+lkpi_ic_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh,
+ const uint8_t *frm, const uint8_t *efrm)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ return (lhw->ic_recv_action(ni, wh, frm, efrm));
+}
+
+static int
+lkpi_ic_send_action(struct ieee80211_node *ni, int category, int action, void *sa)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ return (lhw->ic_send_action(ni, category, action, sa));
+}
+
+
+static int
+lkpi_ic_ampdu_enable(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ return (lhw->ic_ampdu_enable(ni, tap));
+}
+
+static int
+lkpi_ic_addba_request(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+ int dialogtoken, int baparamset, int batimeout)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ return (lhw->ic_addba_request(ni, tap, dialogtoken, baparamset, batimeout));
+}
+
+static int
+lkpi_ic_addba_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+ int status, int baparamset, int batimeout)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ return (lhw->ic_addba_response(ni, tap, status, baparamset, batimeout));
+}
+
+static void
+lkpi_ic_addba_stop(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ lhw->ic_addba_stop(ni, tap);
+}
+
+static void
+lkpi_ic_addba_response_timeout(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ lhw->ic_addba_response_timeout(ni, tap);
+}
+
+static void
+lkpi_ic_bar_response(struct ieee80211_node *ni, struct ieee80211_tx_ampdu *tap,
+ int status)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ IMPROVE_HT();
+
+ lhw->ic_bar_response(ni, tap, status);
+}
+
+static int
+lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap,
+ int baparamset, int batimeout, int baseqctl)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params;
+ int error;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+ vap = ni->ni_vap;
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ lsta = ni->ni_drv_data;
+ sta = LSTA_TO_STA(lsta);
+
+ params.sta = sta;
+ params.action = IEEE80211_AMPDU_RX_START;
+ params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
+ if (params.buf_size == 0)
+ params.buf_size = IEEE80211_MAX_AMPDU_BUF_HT;
+ else
+ params.buf_size = min(params.buf_size, IEEE80211_MAX_AMPDU_BUF_HT);
+ if (params.buf_size > hw->max_rx_aggregation_subframes)
+ params.buf_size = hw->max_rx_aggregation_subframes;
+ params.timeout = le16toh(batimeout);
+ params.ssn = _IEEE80211_MASKSHIFT(le16toh(baseqctl), IEEE80211_BASEQ_START);
+ params.tid = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_TID);
+ params.amsdu = false;
+
+ IMPROVE_HT("Do we need to distinguish based on SUPPORTS_REORDERING_BUFFER?");
+
+ /* This may call kalloc. Make sure we can sleep. */
+ error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ if (error != 0) {
+ ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
+ __func__, error, ni, rap);
+ return (error);
+ }
+ IMPROVE_HT("net80211 is missing the error check on return and assumes success");
+
+ error = lhw->ic_ampdu_rx_start(ni, rap, baparamset, batimeout, baseqctl);
+ return (error);
+}
+
+static void
+lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
+{
+ struct ieee80211com *ic;
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct ieee80211vap *vap;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_sta *lsta;
+ struct ieee80211_sta *sta;
+ struct ieee80211_ampdu_params params;
+ int error;
+ uint8_t tid;
+
+ ic = ni->ni_ic;
+ lhw = ic->ic_softc;
+
+ /*
+ * We should not (cannot) call into mac80211 ops with AMPDU_RX_STOP if
+ * we did not START. Some drivers pass it down to firmware which will
+ * simply barf and net80211 calls ieee80211_ht_node_cleanup() from
+ * ieee80211_ht_node_init() amongst others which will iterate over all
+ * tid and call ic_ampdu_rx_stop() unconditionally.
+ * XXX net80211 should probably be more "gentle" in these cases and
+ * track some state itself.
+ */
+ if ((rap->rxa_flags & IEEE80211_AGGR_RUNNING) == 0)
+ goto net80211_only;
+
+ hw = LHW_TO_HW(lhw);
+ vap = ni->ni_vap;
+ lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
+ lsta = ni->ni_drv_data;
+ sta = LSTA_TO_STA(lsta);
+
+ IMPROVE_HT("This really should be passed from ht_recv_action_ba_delba.");
+ for (tid = 0; tid < WME_NUM_TID; tid++) {
+ if (&ni->ni_rx_ampdu[tid] == rap)
+ break;
+ }
+
+ params.sta = sta;
+ params.action = IEEE80211_AMPDU_RX_STOP;
+ params.buf_size = 0;
+ params.timeout = 0;
+ params.ssn = 0;
+ params.tid = tid;
+ params.amsdu = false;
+
+ error = lkpi_80211_mo_ampdu_action(hw, vif, &params);
+ if (error != 0)
+ ic_printf(ic, "%s: mo_ampdu_action returned %d. ni %p rap %p\n",
+ __func__, error, ni, rap);
+
+net80211_only:
+ lhw->ic_ampdu_rx_stop(ni, rap);
+}
+#endif
+
+static void
+lkpi_ic_getradiocaps_ht(struct ieee80211com *ic, struct ieee80211_hw *hw,
+ uint8_t *bands, int *chan_flags, enum nl80211_band band)
+{
+#ifdef LKPI_80211_HT
+ struct ieee80211_sta_ht_cap *ht_cap;
+
+ ht_cap = &hw->wiphy->bands[band]->ht_cap;
+ if (!ht_cap->ht_supported)
+ return;
+
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ setbit(bands, IEEE80211_MODE_11NG);
+ break;
+ case NL80211_BAND_5GHZ:
+ setbit(bands, IEEE80211_MODE_11NA);
+ break;
+ default:
+ IMPROVE("Unsupported band %d", band);
+ return;
+ }
+
+ ic->ic_htcaps = IEEE80211_HTC_HT; /* HT operation */
+
+ /*
+ * Rather than manually checking each flag and
+ * translating IEEE80211_HT_CAP_ to IEEE80211_HTCAP_,
+ * simply copy the 16bits.
+ */
+ ic->ic_htcaps |= ht_cap->cap;
+
+ /* Then deal with the other flags. */
+ if (ieee80211_hw_check(hw, AMPDU_AGGREGATION))
+ ic->ic_htcaps |= IEEE80211_HTC_AMPDU;
+#ifdef __notyet__
+ if (ieee80211_hw_check(hw, TX_AMSDU))
+ ic->ic_htcaps |= IEEE80211_HTC_AMSDU;
+ if (ieee80211_hw_check(hw, SUPPORTS_AMSDU_IN_AMPDU))
+ ic->ic_htcaps |= (IEEE80211_HTC_RX_AMSDU_AMPDU |
+ IEEE80211_HTC_TX_AMSDU_AMPDU);
+#endif
+
+ IMPROVE("PS, ampdu_*, ht_cap.mcs.tx_params, ...");
+ ic->ic_htcaps |= IEEE80211_HTCAP_SMPS_OFF;
+
+ /* Only add HT40 channels if supported. */
+ if ((ic->ic_htcaps & IEEE80211_HTCAP_CHWIDTH40) != 0 &&
+ chan_flags != NULL)
+ *chan_flags |= NET80211_CBW_FLAG_HT40;
+#endif
+}
+
static void
lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
int *n, struct ieee80211_channel *c)
@@ -3213,13 +4114,12 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
chan_flags = 0;
setbit(bands, IEEE80211_MODE_11B);
/* XXX-BZ unclear how to check for 11g. */
+
+ IMPROVE("the bitrates may have flags?");
setbit(bands, IEEE80211_MODE_11G);
-#ifdef __notyet__
- if (hw->wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.ht_supported) {
- setbit(bands, IEEE80211_MODE_11NG);
- chan_flags |= NET80211_CBW_FLAG_HT40;
- }
-#endif
+
+ lkpi_ic_getradiocaps_ht(ic, hw, bands, &chan_flags,
+ NL80211_BAND_2GHZ);
channels = hw->wiphy->bands[NL80211_BAND_2GHZ]->channels;
for (i = 0; i < nchans && *n < maxchan; i++) {
@@ -3227,8 +4127,8 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
int cflags = chan_flags;
if (channels[i].flags & IEEE80211_CHAN_DISABLED) {
- printf("%s: %s: Skipping disabled chan "
- "[%u/%u/%#x]\n", ic->ic_name, __func__,
+ ic_printf(ic, "%s: Skipping disabled chan "
+ "[%u/%u/%#x]\n", __func__,
channels[i].hw_value,
channels[i].center_freq, channels[i].flags);
continue;
@@ -3248,11 +4148,11 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
error = ieee80211_add_channel_cbw(c, maxchan, n,
channels[i].hw_value, channels[i].center_freq,
channels[i].max_power,
- nflags, bands, chan_flags);
+ nflags, bands, cflags);
/* net80211::ENOBUFS: *n >= maxchans */
if (error != 0 && error != ENOBUFS)
- printf("%s: %s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
- "returned error %d\n", ic->ic_name,
+ ic_printf(ic, "%s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
+ "returned error %d\n",
__func__, channels[i].hw_value,
channels[i].center_freq, channels[i].flags,
nflags, chan_flags, cflags, error);
@@ -3269,24 +4169,24 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
memset(bands, 0, sizeof(bands));
chan_flags = 0;
setbit(bands, IEEE80211_MODE_11A);
-#ifdef __not_yet__
- if (hw->wiphy->bands[NL80211_BAND_5GHZ]->ht_cap.ht_supported) {
- setbit(bands, IEEE80211_MODE_11NA);
- chan_flags |= NET80211_CBW_FLAG_HT40;
- }
+
+ lkpi_ic_getradiocaps_ht(ic, hw, bands, &chan_flags,
+ NL80211_BAND_5GHZ);
+
+#ifdef LKPI_80211_VHT
if (hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.vht_supported){
ic->ic_flags_ext |= IEEE80211_FEXT_VHT;
- ic->ic_vhtcaps =
+ ic->ic_vht_cap.vht_cap_info =
hw->wiphy->bands[NL80211_BAND_5GHZ]->vht_cap.cap;
setbit(bands, IEEE80211_MODE_VHT_5GHZ);
chan_flags |= NET80211_CBW_FLAG_VHT80;
if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160MHZ(
- ic->ic_vhtcaps))
+ ic->ic_vht_cap.vht_cap_info))
chan_flags |= NET80211_CBW_FLAG_VHT160;
if (IEEE80211_VHTCAP_SUPP_CHAN_WIDTH_IS_160_80P80MHZ(
- ic->ic_vhtcaps))
+ ic->ic_vht_cap.vht_cap_info))
chan_flags |= NET80211_CBW_FLAG_VHT80P80;
}
#endif
@@ -3297,8 +4197,8 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
int cflags = chan_flags;
if (channels[i].flags & IEEE80211_CHAN_DISABLED) {
- printf("%s: %s: Skipping disabled chan "
- "[%u/%u/%#x]\n", ic->ic_name, __func__,
+ ic_printf(ic, "%s: Skipping disabled chan "
+ "[%u/%u/%#x]\n", __func__,
channels[i].hw_value,
channels[i].center_freq, channels[i].flags);
continue;
@@ -3318,11 +4218,11 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
error = ieee80211_add_channel_cbw(c, maxchan, n,
channels[i].hw_value, channels[i].center_freq,
channels[i].max_power,
- nflags, bands, chan_flags);
+ nflags, bands, cflags);
/* net80211::ENOBUFS: *n >= maxchans */
if (error != 0 && error != ENOBUFS)
- printf("%s: %s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
- "returned error %d\n", ic->ic_name,
+ ic_printf(ic, "%s: Adding chan %u/%u/%#x/%#x/%#x/%#x "
+ "returned error %d\n",
__func__, channels[i].hw_value,
channels[i].center_freq, channels[i].flags,
nflags, chan_flags, cflags, error);
@@ -3354,6 +4254,7 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
struct ieee80211_hw *hw;
struct lkpi_hw *lhw;
struct wiphy *wiphy;
+ int ac;
/* Get us and the driver data also allocated. */
wiphy = wiphy_new(&linuxkpi_mac80211cfgops, sizeof(*lhw) + priv_len);
@@ -3363,9 +4264,21 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
lhw = wiphy_priv(wiphy);
lhw->ops = ops;
- mtx_init(&lhw->mtx, "lhw", NULL, MTX_DEF | MTX_RECURSE);
+ LKPI_80211_LHW_LOCK_INIT(lhw);
+ LKPI_80211_LHW_SCAN_LOCK_INIT(lhw);
+ LKPI_80211_LHW_TXQ_LOCK_INIT(lhw);
sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
TAILQ_INIT(&lhw->lvif_head);
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
+ lhw->txq_generation[ac] = 1;
+ TAILQ_INIT(&lhw->scheduled_txqs[ac]);
+ }
+
+ /* Deferred RX path. */
+ LKPI_80211_LHW_RXQ_LOCK_INIT(lhw);
+ TASK_INIT(&lhw->rxq_task, 0, lkpi_80211_lhw_rxq_task, lhw);
+ mbufq_init(&lhw->rxq, IFQ_MAXLEN);
+ lhw->rxq_stopped = false;
/*
* XXX-BZ TODO make sure there is a "_null" function to all ops
@@ -3392,14 +4305,47 @@ void
linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
+ struct mbuf *m;
lhw = HW_TO_LHW(hw);
free(lhw->ic, M_LKPI80211);
lhw->ic = NULL;
+ /*
+ * Drain the deferred RX path.
+ */
+ LKPI_80211_LHW_RXQ_LOCK(lhw);
+ lhw->rxq_stopped = true;
+ LKPI_80211_LHW_RXQ_UNLOCK(lhw);
+
+ /* Drain taskq, won't be restarted due to rxq_stopped being set. */
+ while (taskqueue_cancel(taskqueue_thread, &lhw->rxq_task, NULL) != 0)
+ taskqueue_drain(taskqueue_thread, &lhw->rxq_task);
+
+ /* Flush mbufq (make sure to release ni refs!). */
+ m = mbufq_dequeue(&lhw->rxq);
+ while (m != NULL) {
+ struct m_tag *mtag;
+
+ mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL);
+ if (mtag != NULL) {
+ struct lkpi_80211_tag_rxni *rxni;
+
+ rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
+ ieee80211_free_node(rxni->ni);
+ }
+ m_freem(m);
+ m = mbufq_dequeue(&lhw->rxq);
+ }
+ KASSERT(mbufq_empty(&lhw->rxq), ("%s: lhw %p has rxq len %d != 0\n",
+ __func__, lhw, mbufq_len(&lhw->rxq)));
+ LKPI_80211_LHW_RXQ_LOCK_DESTROY(lhw);
+
/* Cleanup more of lhw here or in wiphy_free()? */
+ LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
+ LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw);
+ LKPI_80211_LHW_LOCK_DESTROY(lhw);
sx_destroy(&lhw->lvif_sx);
- mtx_destroy(&lhw->mtx);
IMPROVE();
}
@@ -3482,7 +4428,9 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
IEEE80211_C_STA |
IEEE80211_C_MONITOR |
IEEE80211_C_WPA | /* WPA/RSN */
+#ifdef LKPI_80211_WME
IEEE80211_C_WME |
+#endif
#if 0
IEEE80211_C_PMGT |
#endif
@@ -3505,18 +4453,6 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
lhw->scan_flags |= LKPI_LHW_SCAN_HW;
}
-#ifdef __notyet__
- ic->ic_htcaps = IEEE80211_HTC_HT /* HT operation */
- | IEEE80211_HTC_AMPDU /* A-MPDU tx/rx */
- | IEEE80211_HTC_AMSDU /* A-MSDU tx/rx */
- | IEEE80211_HTCAP_MAXAMSDU_3839
- /* max A-MSDU length */
- | IEEE80211_HTCAP_SMPS_OFF; /* SM power save off */
- ic->ic_htcaps |= IEEE80211_HTCAP_SHORTGI20;
- ic->ic_htcaps |= IEEE80211_HTCAP_CHWIDTH40 | IEEE80211_HTCAP_SHORTGI40;
- ic->ic_htcaps |= IEEE80211_HTCAP_TXSTBC;
-#endif
-
/*
* The wiphy variables report bitmasks of avail antennas.
* (*get_antenna) get the current bitmask sets which can be
@@ -3575,6 +4511,33 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
lhw->ic_node_free = ic->ic_node_free;
ic->ic_node_free = lkpi_ic_node_free;
+#ifdef LKPI_80211_HT
+ lhw->ic_recv_action = ic->ic_recv_action;
+ ic->ic_recv_action = lkpi_ic_recv_action;
+ lhw->ic_send_action = ic->ic_send_action;
+ ic->ic_send_action = lkpi_ic_send_action;
+
+ lhw->ic_ampdu_enable = ic->ic_ampdu_enable;
+ ic->ic_ampdu_enable = lkpi_ic_ampdu_enable;
+
+ lhw->ic_addba_request = ic->ic_addba_request;
+ ic->ic_addba_request = lkpi_ic_addba_request;
+ lhw->ic_addba_response = ic->ic_addba_response;
+ ic->ic_addba_response = lkpi_ic_addba_response;
+ lhw->ic_addba_stop = ic->ic_addba_stop;
+ ic->ic_addba_stop = lkpi_ic_addba_stop;
+ lhw->ic_addba_response_timeout = ic->ic_addba_response_timeout;
+ ic->ic_addba_response_timeout = lkpi_ic_addba_response_timeout;
+
+ lhw->ic_bar_response = ic->ic_bar_response;
+ ic->ic_bar_response = lkpi_ic_bar_response;
+
+ lhw->ic_ampdu_rx_start = ic->ic_ampdu_rx_start;
+ ic->ic_ampdu_rx_start = lkpi_ic_ampdu_rx_start;
+ lhw->ic_ampdu_rx_stop = ic->ic_ampdu_rx_stop;
+ ic->ic_ampdu_rx_stop = lkpi_ic_ampdu_rx_stop;
+#endif
+
lkpi_radiotap_attach(lhw);
/*
@@ -3584,8 +4547,7 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
* in any band so we can scale [(ext) sup rates] IE(s) accordingly.
*/
lhw->supbands = lhw->max_rates = 0;
- for (band = 0; band < NUM_NL80211_BANDS &&
- hw->conf.chandef.chan == NULL; band++) {
+ for (band = 0; band < NUM_NL80211_BANDS; band++) {
struct ieee80211_supported_band *supband;
struct linuxkpi_ieee80211_channel *channels;
@@ -3596,6 +4558,10 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
lhw->supbands++;
lhw->max_rates = max(lhw->max_rates, supband->n_bitrates);
+ /* If we have a channel, we need to keep counting supbands. */
+ if (hw->conf.chandef.chan != NULL)
+ continue;
+
channels = supband->channels;
for (i = 0; i < supband->n_channels; i++) {
@@ -3603,6 +4569,9 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
continue;
cfg80211_chandef_create(&hw->conf.chandef, &channels[i],
+#ifdef LKPI_80211_HT
+ (ic->ic_htcaps & IEEE80211_HTC_HT) ? 0 :
+#endif
NL80211_CHAN_NO_HT);
break;
}
@@ -3620,7 +4589,6 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
/*
* The maximum supported bitrates on any band + size for
* DSSS Parameter Set give our per-band IE size.
- * XXX-BZ FIXME add HT VHT ... later
* SSID is the responsibility of the driver and goes on the side.
* The user specified bits coming from the vap go into the
* "common ies" fields.
@@ -3628,20 +4596,39 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
lhw->scan_ie_len = 2 + IEEE80211_RATE_SIZE;
if (lhw->max_rates > IEEE80211_RATE_SIZE)
lhw->scan_ie_len += 2 + (lhw->max_rates - IEEE80211_RATE_SIZE);
- /*
- * net80211 does not seem to support the DSSS Parameter Set but some of
- * the drivers insert it so calculate the extra fixed space in.
- */
- lhw->scan_ie_len += 2 + 1;
+
+ if (hw->wiphy->features & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES) {
+ /*
+ * net80211 does not seem to support the DSSS Parameter Set but
+ * some of the drivers insert it so calculate the extra fixed
+ * space in.
+ */
+ lhw->scan_ie_len += 2 + 1;
+ }
+
+#if defined(LKPI_80211_HT)
+ if ((ic->ic_htcaps & IEEE80211_HTC_HT) != 0)
+ lhw->scan_ie_len += sizeof(struct ieee80211_ie_htcap);
+#endif
+#if defined(LKPI_80211_VHT)
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_VHT) != 0)
+ lhw->scan_ie_len += 2 + sizeof(struct ieee80211_vht_cap);
+#endif
/* Reduce the max_scan_ie_len "left" by the amount we consume already. */
- if (hw->wiphy->max_scan_ie_len > 0)
+ if (hw->wiphy->max_scan_ie_len > 0) {
+ if (lhw->scan_ie_len > hw->wiphy->max_scan_ie_len)
+ goto err;
hw->wiphy->max_scan_ie_len -= lhw->scan_ie_len;
+ }
if (bootverbose)
ieee80211_announce(ic);
return (0);
+err:
+ IMPROVE("TODO FIXME CLEANUP");
+ return (-EAGAIN);
}
void
@@ -3671,12 +4658,12 @@ linuxkpi_ieee80211_iterate_interfaces(struct ieee80211_hw *hw,
if (flags & ~(IEEE80211_IFACE_ITER_NORMAL|
IEEE80211_IFACE_ITER_RESUME_ALL|
IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER|
- IEEE80211_IFACE_ITER__ACTIVE|IEEE80211_IFACE_ITER__ATOMIC)) {
+ IEEE80211_IFACE_ITER_ACTIVE|IEEE80211_IFACE_ITER__ATOMIC)) {
ic_printf(lhw->ic, "XXX TODO %s flags(%#x) not yet supported.\n",
__func__, flags);
}
- active = (flags & IEEE80211_IFACE_ITER__ACTIVE) != 0;
+ active = (flags & IEEE80211_IFACE_ITER_ACTIVE) != 0;
atomic = (flags & IEEE80211_IFACE_ITER__ATOMIC) != 0;
nin_drv = (flags & IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER) != 0;
@@ -3734,8 +4721,32 @@ linuxkpi_ieee80211_iterate_chan_contexts(struct ieee80211_hw *hw,
void *),
void *arg)
{
+ struct lkpi_hw *lhw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ struct lkpi_chanctx *lchanctx;
- UNIMPLEMENTED;
+ KASSERT(hw != NULL && iterfunc != NULL,
+ ("%s: hw %p iterfunc %p arg %p\n", __func__, hw, iterfunc, arg));
+
+ lhw = HW_TO_LHW(hw);
+
+ IMPROVE("lchanctx should be its own list somewhere");
+
+ LKPI_80211_LHW_LVIF_LOCK(lhw);
+ TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
+
+ vif = LVIF_TO_VIF(lvif);
+ if (vif->chanctx_conf == NULL)
+ continue;
+
+ lchanctx = CHANCTX_CONF_TO_LCHANCTX(vif->chanctx_conf);
+ if (!lchanctx->added_to_drv)
+ continue;
+
+ iterfunc(hw, &lchanctx->conf, arg);
+ }
+ LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
void
@@ -3767,6 +4778,16 @@ linuxkpi_ieee80211_iterate_stations_atomic(struct ieee80211_hw *hw,
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
}
+struct linuxkpi_ieee80211_regdomain *
+lkpi_get_linuxkpi_ieee80211_regdomain(size_t n)
+{
+ struct linuxkpi_ieee80211_regdomain *regd;
+
+ regd = kzalloc(sizeof(*regd) + n * sizeof(struct ieee80211_reg_rule),
+ GFP_KERNEL);
+ return (regd);
+}
+
int
linuxkpi_regulatory_set_wiphy_regd_sync(struct wiphy *wiphy,
struct linuxkpi_ieee80211_regdomain *regd)
@@ -3804,21 +4825,82 @@ linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw,
ieee80211_scan_done(ss->ss_vap);
- LKPI_80211_LHW_LOCK(lhw);
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
free(lhw->hw_req, M_LKPI80211);
lhw->hw_req = NULL;
lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
wakeup(lhw);
- LKPI_80211_LHW_UNLOCK(lhw);
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
return;
}
+static void
+lkpi_80211_lhw_rxq_rx_one(struct lkpi_hw *lhw, struct mbuf *m)
+{
+ struct ieee80211_node *ni;
+ struct m_tag *mtag;
+ int ok;
+
+ ni = NULL;
+ mtag = m_tag_locate(m, MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI, NULL);
+ if (mtag != NULL) {
+ struct lkpi_80211_tag_rxni *rxni;
+
+ rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
+ ni = rxni->ni;
+ }
+
+ if (ni != NULL) {
+ ok = ieee80211_input_mimo(ni, m);
+ ieee80211_free_node(ni); /* Release the reference. */
+ if (ok < 0)
+ m_freem(m);
+ } else {
+ ok = ieee80211_input_mimo_all(lhw->ic, m);
+ /* mbuf got consumed. */
+ }
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_RX)
+ printf("TRACE %s: handled frame type %#0x\n", __func__, ok);
+#endif
+}
+
+static void
+lkpi_80211_lhw_rxq_task(void *ctx, int pending)
+{
+ struct lkpi_hw *lhw;
+ struct mbufq mq;
+ struct mbuf *m;
+
+ lhw = ctx;
+
+#ifdef LINUXKPI_DEBUG_80211
+ if (linuxkpi_debug_80211 & D80211_TRACE_RX)
+ printf("%s:%d lhw %p pending %d mbuf_qlen %d\n",
+ __func__, __LINE__, lhw, pending, mbufq_len(&lhw->rxq));
+#endif
+
+ mbufq_init(&mq, IFQ_MAXLEN);
+
+ LKPI_80211_LHW_RXQ_LOCK(lhw);
+ mbufq_concat(&mq, &lhw->rxq);
+ LKPI_80211_LHW_RXQ_UNLOCK(lhw);
+
+ m = mbufq_dequeue(&mq);
+ while (m != NULL) {
+ lkpi_80211_lhw_rxq_rx_one(lhw, m);
+ m = mbufq_dequeue(&mq);
+ }
+}
+
+/* For %list see comment towards the end of the function. */
void
linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
- struct ieee80211_sta *sta, struct napi_struct *napi __unused)
+ struct ieee80211_sta *sta, struct napi_struct *napi __unused,
+ struct list_head *list __unused)
{
- struct epoch_tracker et;
struct lkpi_hw *lhw;
struct ieee80211com *ic;
struct mbuf *m;
@@ -3991,8 +5073,9 @@ no_trace_beacons:
* net80211 should take care of the other information (sync_tsf,
* sync_dtim_count) as otherwise we need to parse the beacon.
*/
- }
skip_device_ts:
+ ;
+ }
if (vap != NULL && vap->iv_state > IEEE80211_S_INIT &&
ieee80211_radiotap_active_vap(vap)) {
@@ -4024,22 +5107,45 @@ skip_device_ts:
if (ieee80211_hw_check(hw, RX_INCLUDES_FCS))
m_adj(m, -IEEE80211_CRC_LEN);
- NET_EPOCH_ENTER(et);
+#if 0
+ if (list != NULL) {
+ /*
+ * Normally this would be queued up and delivered by
+ * netif_receive_skb_list(), napi_gro_receive(), or the like.
+ * See mt76::mac80211.c as only current possible consumer.
+ */
+ IMPROVE("we simply pass the packet to net80211 to deal with.");
+ }
+#endif
+
+ /*
+ * Attach meta-information to the mbuf for the deferred RX path.
+ * Currently this is best-effort. Should we need to be hard,
+ * drop the frame and goto err;
+ */
if (ni != NULL) {
- ok = ieee80211_input_mimo(ni, m);
- ieee80211_free_node(ni);
- if (ok < 0)
- m_freem(m);
- } else {
- ok = ieee80211_input_mimo_all(ic, m);
- /* mbuf got consumed. */
+ struct m_tag *mtag;
+ struct lkpi_80211_tag_rxni *rxni;
+
+ mtag = m_tag_alloc(MTAG_ABI_LKPI80211, LKPI80211_TAG_RXNI,
+ sizeof(*rxni), IEEE80211_M_NOWAIT);
+ if (mtag != NULL) {
+ rxni = (struct lkpi_80211_tag_rxni *)(mtag + 1);
+ rxni->ni = ni; /* We hold a reference. */
+ m_tag_prepend(m, mtag);
+ }
}
- NET_EPOCH_EXIT(et);
-#ifdef LINUXKPI_DEBUG_80211
- if (linuxkpi_debug_80211 & D80211_TRACE_RX)
- printf("TRACE %s: handled frame type %#0x\n", __func__, ok);
-#endif
+ LKPI_80211_LHW_RXQ_LOCK(lhw);
+ if (lhw->rxq_stopped) {
+ LKPI_80211_LHW_RXQ_UNLOCK(lhw);
+ m_freem(m);
+ goto err;
+ }
+
+ mbufq_enqueue(&lhw->rxq, m);
+ taskqueue_enqueue(taskqueue_thread, &lhw->rxq_task);
+ LKPI_80211_LHW_RXQ_UNLOCK(lhw);
IMPROVE();
@@ -4120,6 +5226,7 @@ linuxkpi_ieee80211_frequency_to_channel(uint32_t freq, uint32_t flags __unused)
return (ieee80211_mhz2ieee(freq, 0));
}
+#if 0
static struct lkpi_sta *
lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni)
{
@@ -4136,6 +5243,7 @@ lkpi_find_lsta_by_ni(struct lkpi_vif *lvif, struct ieee80211_node *ni)
return (NULL);
}
+#endif
struct ieee80211_sta *
linuxkpi_ieee80211_find_sta(struct ieee80211_vif *vif, const u8 *peer)
@@ -4200,13 +5308,29 @@ linuxkpi_ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
struct lkpi_txq *ltxq;
+ struct lkpi_vif *lvif;
struct sk_buff *skb;
+ skb = NULL;
ltxq = TXQ_TO_LTXQ(txq);
ltxq->seen_dequeue = true;
+ if (ltxq->stopped)
+ goto stopped;
+
+ lvif = VIF_TO_LVIF(ltxq->txq.vif);
+ if (lvif->hw_queue_stopped[ltxq->txq.ac]) {
+ ltxq->stopped = true;
+ goto stopped;
+ }
+
+ IMPROVE("hw(TX_FRAG_LIST)");
+
+ LKPI_80211_LTXQ_LOCK(ltxq);
skb = skb_dequeue(&ltxq->skbq);
+ LKPI_80211_LTXQ_UNLOCK(ltxq);
+stopped:
return (skb);
}
@@ -4221,10 +5345,12 @@ linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *txq,
ltxq = TXQ_TO_LTXQ(txq);
fc = bc = 0;
+ LKPI_80211_LTXQ_LOCK(ltxq);
skb_queue_walk(&ltxq->skbq, skb) {
fc++;
bc += skb->len;
}
+ LKPI_80211_LTXQ_UNLOCK(ltxq);
if (frame_cnt)
*frame_cnt = fc;
if (byte_cnt)
@@ -4241,8 +5367,8 @@ linuxkpi_ieee80211_txq_get_depth(struct ieee80211_txq *txq,
* passed back from the driver. rawx_mit() saves the ni on the m and the
* m on the skb for us to be able to give feedback to net80211.
*/
-void
-linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
+static void
+_lkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
int status)
{
struct ieee80211_node *ni;
@@ -4257,20 +5383,28 @@ linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
ieee80211_tx_complete(ni, m, status);
/* ni & mbuf were consumed. */
}
+}
+void
+linuxkpi_ieee80211_free_txskb(struct ieee80211_hw *hw, struct sk_buff *skb,
+ int status)
+{
+
+ _lkpi_ieee80211_free_txskb(hw, skb, status);
kfree_skb(skb);
}
void
-linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+linuxkpi_ieee80211_tx_status_ext(struct ieee80211_hw *hw,
+ struct ieee80211_tx_status *txstat)
{
+ struct sk_buff *skb;
struct ieee80211_tx_info *info;
struct ieee80211_ratectl_tx_status txs;
struct ieee80211_node *ni;
int status;
- info = IEEE80211_SKB_CB(skb);
-
+ skb = txstat->skb;
if (skb->m != NULL) {
struct mbuf *m;
@@ -4281,6 +5415,7 @@ linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
ni = NULL;
}
+ info = txstat->info;
if (info->flags & IEEE80211_TX_STAT_ACK) {
status = 0; /* No error. */
txs.status = IEEE80211_RATECTL_TX_SUCCESS;
@@ -4303,11 +5438,11 @@ linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
txs.flags |= IEEE80211_RATECTL_STATUS_LONG_RETRY;
}
#if 0 /* Unused in net80211 currently. */
- /* XXX-BZ conver;t check .flags for MCS/VHT/.. */
+ /* XXX-BZ convert check .flags for MCS/VHT/.. */
txs.final_rate = info->status.rates[0].idx;
txs.flags |= IEEE80211_RATECTL_STATUS_FINAL_RATE;
#endif
- if (info->status.is_valid_ack_signal) {
+ if (info->status.flags & IEEE80211_TX_STATUS_ACK_SIGNAL_VALID) {
txs.rssi = info->status.ack_signal; /* XXX-BZ CONVERT? */
txs.flags |= IEEE80211_RATECTL_STATUS_RSSI;
}
@@ -4332,7 +5467,7 @@ linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
"band %u hw_queue %u tx_time_est %d : "
"rates [ %u %u %#x, %u %u %#x, %u %u %#x, %u %u %#x ] "
"ack_signal %u ampdu_ack_len %u ampdu_len %u antenna %u "
- "tx_time %u is_valid_ack_signal %u "
+ "tx_time %u flags %#x "
"status_driver_data [ %p %p ]\n",
__func__, hw, skb, status, info->flags,
info->band, info->hw_queue, info->tx_time_est,
@@ -4346,12 +5481,30 @@ linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
info->status.rates[3].flags,
info->status.ack_signal, info->status.ampdu_ack_len,
info->status.ampdu_len, info->status.antenna,
- info->status.tx_time, info->status.is_valid_ack_signal,
+ info->status.tx_time, info->status.flags,
info->status.status_driver_data[0],
info->status.status_driver_data[1]);
#endif
- linuxkpi_ieee80211_free_txskb(hw, skb, status);
+ if (txstat->free_list) {
+ _lkpi_ieee80211_free_txskb(hw, skb, status);
+ list_add_tail(&skb->list, txstat->free_list);
+ } else {
+ linuxkpi_ieee80211_free_txskb(hw, skb, status);
+ }
+}
+
+void
+linuxkpi_ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct ieee80211_tx_status status;
+
+ memset(&status, 0, sizeof(status));
+ status.info = IEEE80211_SKB_CB(skb);
+ status.skb = skb;
+ /* sta, n_rates, rates, free_list? */
+
+ ieee80211_tx_status_ext(hw, &status);
}
/*
@@ -4462,7 +5615,7 @@ linuxkpi_ieee80211_pspoll_get(struct ieee80211_hw *hw,
psp = skb_put_zero(skb, sizeof(*psp));
psp->i_fc[0] = IEEE80211_FC0_VERSION_0;
psp->i_fc[0] |= IEEE80211_FC0_SUBTYPE_PS_POLL | IEEE80211_FC0_TYPE_CTL;
- v = htole16(vif->bss_conf.aid | 1<<15 | 1<<16);
+ v = htole16(vif->cfg.aid | 1<<15 | 1<<16);
memcpy(&psp->i_aid, &v, sizeof(v));
IEEE80211_ADDR_COPY(psp->i_bssid, vap->iv_bss->ni_macaddr);
IEEE80211_ADDR_COPY(psp->i_ta, vif->addr);
@@ -4472,13 +5625,15 @@ linuxkpi_ieee80211_pspoll_get(struct ieee80211_hw *hw,
struct sk_buff *
linuxkpi_ieee80211_nullfunc_get(struct ieee80211_hw *hw,
- struct ieee80211_vif *vif, bool qos)
+ struct ieee80211_vif *vif, int linkid, bool qos)
{
struct lkpi_vif *lvif;
struct ieee80211vap *vap;
struct sk_buff *skb;
struct ieee80211_frame *nullf;
+ IMPROVE("linkid");
+
skb = dev_alloc_skb(hw->extra_tx_headroom + sizeof(*nullf));
if (skb == NULL)
return (NULL);
@@ -4528,10 +5683,8 @@ linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *vif)
nstate = IEEE80211_S_INIT;
arg = 0; /* Not a valid reason. */
-#ifdef LINUXKPI_DEBUG_80211
- if (linuxkpi_debug_80211 & D80211_TRACE)
- ic_printf(vap->iv_ic, "%s: vif %p\n", __func__, vif);
-#endif
+ ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
+ vif, vap, ieee80211_state_name[vap->iv_state]);
ieee80211_new_state(vap, nstate, arg);
}
@@ -4544,14 +5697,437 @@ linuxkpi_ieee80211_beacon_loss(struct ieee80211_vif *vif)
lvif = VIF_TO_LVIF(vif);
vap = LVIF_TO_VAP(lvif);
+ ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
+ vif, vap, ieee80211_state_name[vap->iv_state]);
+ ieee80211_beacon_miss(vap->iv_ic);
+}
+
+/* -------------------------------------------------------------------------- */
+
+void
+linuxkpi_ieee80211_stop_queue(struct ieee80211_hw *hw, int qnum)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ int ac_count, ac;
+
+ KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
+ __func__, qnum, hw->queues, hw));
+
+ lhw = wiphy_priv(hw->wiphy);
+
+ /* See lkpi_ic_vap_create(). */
+ if (hw->queues >= IEEE80211_NUM_ACS)
+ ac_count = IEEE80211_NUM_ACS;
+ else
+ ac_count = 1;
+
+ LKPI_80211_LHW_LVIF_LOCK(lhw);
+ TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
+
+ vif = LVIF_TO_VIF(lvif);
+ for (ac = 0; ac < ac_count; ac++) {
+ IMPROVE_TXQ("LOCKING");
+ if (qnum == vif->hw_queue[ac]) {
#ifdef LINUXKPI_DEBUG_80211
- if (linuxkpi_debug_80211 & D80211_TRACE || vap->iv_state != IEEE80211_S_RUN)
- ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
- vif, vap, ieee80211_state_name[vap->iv_state]);
+ /*
+ * For now log this to better understand
+ * how this is supposed to work.
+ */
+ if (lvif->hw_queue_stopped[ac] &&
+ (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) != 0)
+ ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
+ "lvif %p vif %p ac %d qnum %d already "
+ "stopped\n", __func__, __LINE__,
+ lhw, hw, lvif, vif, ac, qnum);
#endif
- ieee80211_beacon_miss(vap->iv_ic);
+ lvif->hw_queue_stopped[ac] = true;
+ }
+ }
+ }
+ LKPI_80211_LHW_LVIF_UNLOCK(lhw);
+}
+
+void
+linuxkpi_ieee80211_stop_queues(struct ieee80211_hw *hw)
+{
+ int i;
+
+ IMPROVE_TXQ("Locking; do we need further info?");
+ for (i = 0; i < hw->queues; i++)
+ linuxkpi_ieee80211_stop_queue(hw, i);
+}
+
+
+static void
+lkpi_ieee80211_wake_queues(struct ieee80211_hw *hw, int hwq)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_vif *lvif;
+ struct lkpi_sta *lsta;
+ int ac_count, ac, tid;
+
+ /* See lkpi_ic_vap_create(). */
+ if (hw->queues >= IEEE80211_NUM_ACS)
+ ac_count = IEEE80211_NUM_ACS;
+ else
+ ac_count = 1;
+
+ lhw = wiphy_priv(hw->wiphy);
+
+ IMPROVE_TXQ("Locking");
+ LKPI_80211_LHW_LVIF_LOCK(lhw);
+ TAILQ_FOREACH(lvif, &lhw->lvif_head, lvif_entry) {
+ struct ieee80211_vif *vif;
+
+ vif = LVIF_TO_VIF(lvif);
+ for (ac = 0; ac < ac_count; ac++) {
+
+ if (hwq == vif->hw_queue[ac]) {
+
+ /* XXX-BZ what about software scan? */
+
+#ifdef LINUXKPI_DEBUG_80211
+ /*
+ * For now log this to better understand
+ * how this is supposed to work.
+ */
+ if (!lvif->hw_queue_stopped[ac] &&
+ (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) != 0)
+ ic_printf(lhw->ic, "%s:%d: lhw %p hw %p "
+ "lvif %p vif %p ac %d hw_q not stopped\n",
+ __func__, __LINE__,
+ lhw, hw, lvif, vif, ac);
+#endif
+ lvif->hw_queue_stopped[ac] = false;
+
+ LKPI_80211_LVIF_LOCK(lvif);
+ TAILQ_FOREACH(lsta, &lvif->lsta_head, lsta_entry) {
+ struct ieee80211_sta *sta;
+
+ sta = LSTA_TO_STA(lsta);
+ for (tid = 0; tid < nitems(sta->txq); tid++) {
+ struct lkpi_txq *ltxq;
+
+ if (sta->txq[tid] == NULL)
+ continue;
+
+ if (sta->txq[tid]->ac != ac)
+ continue;
+
+ ltxq = TXQ_TO_LTXQ(sta->txq[tid]);
+ if (!ltxq->stopped)
+ continue;
+
+ ltxq->stopped = false;
+
+ /* XXX-BZ see when this explodes with all the locking. taskq? */
+ lkpi_80211_mo_wake_tx_queue(hw, sta->txq[tid]);
+ }
+ }
+ LKPI_80211_LVIF_UNLOCK(lvif);
+ }
+ }
+ }
+ LKPI_80211_LHW_LVIF_UNLOCK(lhw);
+}
+
+void
+linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
+{
+ int i;
+
+ IMPROVE_TXQ("Is this all/enough here?");
+ for (i = 0; i < hw->queues; i++)
+ lkpi_ieee80211_wake_queues(hw, i);
+}
+
+void
+linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
+{
+
+ KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
+ __func__, qnum, hw->queues, hw));
+
+ lkpi_ieee80211_wake_queues(hw, qnum);
}
+/* This is just hardware queues. */
+void
+linuxkpi_ieee80211_txq_schedule_start(struct ieee80211_hw *hw, uint8_t ac)
+{
+ struct lkpi_hw *lhw;
+
+ lhw = HW_TO_LHW(hw);
+
+ IMPROVE_TXQ("Are there reasons why we wouldn't schedule?");
+ IMPROVE_TXQ("LOCKING");
+ if (++lhw->txq_generation[ac] == 0)
+ lhw->txq_generation[ac]++;
+}
+
+struct ieee80211_txq *
+linuxkpi_ieee80211_next_txq(struct ieee80211_hw *hw, uint8_t ac)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_txq *txq;
+ struct lkpi_txq *ltxq;
+
+ lhw = HW_TO_LHW(hw);
+ txq = NULL;
+
+ IMPROVE_TXQ("LOCKING");
+
+ /* Check that we are scheduled. */
+ if (lhw->txq_generation[ac] == 0)
+ goto out;
+
+ ltxq = TAILQ_FIRST(&lhw->scheduled_txqs[ac]);
+ if (ltxq == NULL)
+ goto out;
+ if (ltxq->txq_generation == lhw->txq_generation[ac])
+ goto out;
+
+ ltxq->txq_generation = lhw->txq_generation[ac];
+ TAILQ_REMOVE(&lhw->scheduled_txqs[ac], ltxq, txq_entry);
+ txq = &ltxq->txq;
+ TAILQ_ELEM_INIT(ltxq, txq_entry);
+
+out:
+ return (txq);
+}
+
+void linuxkpi_ieee80211_schedule_txq(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq, bool withoutpkts)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_txq *ltxq;
+ bool ltxq_empty;
+
+ ltxq = TXQ_TO_LTXQ(txq);
+
+ IMPROVE_TXQ("LOCKING");
+
+ /* Only schedule if work to do or asked to anyway. */
+ LKPI_80211_LTXQ_LOCK(ltxq);
+ ltxq_empty = skb_queue_empty(&ltxq->skbq);
+ LKPI_80211_LTXQ_UNLOCK(ltxq);
+ if (!withoutpkts && ltxq_empty)
+ goto out;
+
+ /* Make sure we do not double-schedule. */
+ if (ltxq->txq_entry.tqe_next != NULL)
+ goto out;
+
+ lhw = HW_TO_LHW(hw);
+ TAILQ_INSERT_TAIL(&lhw->scheduled_txqs[txq->ac], ltxq, txq_entry);
+out:
+ return;
+}
+
+void
+linuxkpi_ieee80211_handle_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_txq *ntxq;
+ struct ieee80211_tx_control control;
+ struct sk_buff *skb;
+
+ lhw = HW_TO_LHW(hw);
+
+ LKPI_80211_LHW_TXQ_LOCK(lhw);
+ ieee80211_txq_schedule_start(hw, txq->ac);
+ do {
+ ntxq = ieee80211_next_txq(hw, txq->ac);
+ if (ntxq == NULL)
+ break;
+
+ memset(&control, 0, sizeof(control));
+ control.sta = ntxq->sta;
+ do {
+ skb = linuxkpi_ieee80211_tx_dequeue(hw, ntxq);
+ if (skb == NULL)
+ break;
+ lkpi_80211_mo_tx(hw, &control, skb);
+ } while(1);
+
+ ieee80211_return_txq(hw, ntxq, false);
+ } while (1);
+ ieee80211_txq_schedule_end(hw, txq->ac);
+ LKPI_80211_LHW_TXQ_UNLOCK(lhw);
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct lkpi_cfg80211_bss {
+ u_int refcnt;
+ struct cfg80211_bss bss;
+};
+
+struct lkpi_cfg80211_get_bss_iter_lookup {
+ struct wiphy *wiphy;
+ struct linuxkpi_ieee80211_channel *chan;
+ const uint8_t *bssid;
+ const uint8_t *ssid;
+ size_t ssid_len;
+ enum ieee80211_bss_type bss_type;
+ enum ieee80211_privacy privacy;
+
+ /*
+ * Something to store a copy of the result as the net80211 scan cache
+ * is not refoucnted so a scan entry might go away any time.
+ */
+ bool match;
+ struct cfg80211_bss *bss;
+};
+
+static void
+lkpi_cfg80211_get_bss_iterf(void *arg, const struct ieee80211_scan_entry *se)
+{
+ struct lkpi_cfg80211_get_bss_iter_lookup *lookup;
+ size_t ielen;
+
+ lookup = arg;
+
+ /* Do not try to find another match. */
+ if (lookup->match)
+ return;
+
+ /* Nothing to store result. */
+ if (lookup->bss == NULL)
+ return;
+
+ if (lookup->privacy != IEEE80211_PRIVACY_ANY) {
+ /* if (se->se_capinfo & IEEE80211_CAPINFO_PRIVACY) */
+ /* We have no idea what to compare to as the drivers only request ANY */
+ return;
+ }
+
+ if (lookup->bss_type != IEEE80211_BSS_TYPE_ANY) {
+ /* if (se->se_capinfo & (IEEE80211_CAPINFO_IBSS|IEEE80211_CAPINFO_ESS)) */
+ /* We have no idea what to compare to as the drivers only request ANY */
+ return;
+ }
+
+ if (lookup->chan != NULL) {
+ struct linuxkpi_ieee80211_channel *chan;
+
+ chan = linuxkpi_ieee80211_get_channel(lookup->wiphy,
+ se->se_chan->ic_freq);
+ if (chan == NULL || chan != lookup->chan)
+ return;
+ }
+
+ if (lookup->bssid && !IEEE80211_ADDR_EQ(lookup->bssid, se->se_bssid))
+ return;
+
+ if (lookup->ssid) {
+ if (lookup->ssid_len != se->se_ssid[1] ||
+ se->se_ssid[1] == 0)
+ return;
+ if (memcmp(lookup->ssid, se->se_ssid+2, lookup->ssid_len) != 0)
+ return;
+ }
+
+ ielen = se->se_ies.len;
+
+ lookup->bss->ies = malloc(sizeof(*lookup->bss->ies) + ielen,
+ M_LKPI80211, M_NOWAIT | M_ZERO);
+ if (lookup->bss->ies == NULL)
+ return;
+
+ lookup->bss->ies->data = (uint8_t *)lookup->bss->ies + sizeof(*lookup->bss->ies);
+ lookup->bss->ies->len = ielen;
+ if (ielen)
+ memcpy(lookup->bss->ies->data, se->se_ies.data, ielen);
+
+ lookup->match = true;
+}
+
+struct cfg80211_bss *
+linuxkpi_cfg80211_get_bss(struct wiphy *wiphy, struct linuxkpi_ieee80211_channel *chan,
+ const uint8_t *bssid, const uint8_t *ssid, size_t ssid_len,
+ enum ieee80211_bss_type bss_type, enum ieee80211_privacy privacy)
+{
+ struct lkpi_cfg80211_bss *lbss;
+ struct lkpi_cfg80211_get_bss_iter_lookup lookup;
+ struct lkpi_hw *lhw;
+ struct ieee80211vap *vap;
+
+ lhw = wiphy_priv(wiphy);
+
+ /* Let's hope we can alloc. */
+ lbss = malloc(sizeof(*lbss), M_LKPI80211, M_NOWAIT | M_ZERO);
+ if (lbss == NULL) {
+ ic_printf(lhw->ic, "%s: alloc failed.\n", __func__);
+ return (NULL);
+ }
+
+ lookup.wiphy = wiphy;
+ lookup.chan = chan;
+ lookup.bssid = bssid;
+ lookup.ssid = ssid;
+ lookup.ssid_len = ssid_len;
+ lookup.bss_type = bss_type;
+ lookup.privacy = privacy;
+ lookup.match = false;
+ lookup.bss = &lbss->bss;
+
+ IMPROVE("Iterate over all VAPs comparing perm_addr and addresses?");
+ vap = TAILQ_FIRST(&lhw->ic->ic_vaps);
+ ieee80211_scan_iterate(vap, lkpi_cfg80211_get_bss_iterf, &lookup);
+ if (!lookup.match) {
+ free(lbss, M_LKPI80211);
+ return (NULL);
+ }
+
+ refcount_init(&lbss->refcnt, 1);
+ return (&lbss->bss);
+}
+
+void
+linuxkpi_cfg80211_put_bss(struct wiphy *wiphy, struct cfg80211_bss *bss)
+{
+ struct lkpi_cfg80211_bss *lbss;
+
+ lbss = container_of(bss, struct lkpi_cfg80211_bss, bss);
+
+ /* Free everything again on refcount ... */
+ if (refcount_release(&lbss->refcnt)) {
+ free(lbss->bss.ies, M_LKPI80211);
+ free(lbss, M_LKPI80211);
+ }
+}
+
+void
+linuxkpi_cfg80211_bss_flush(struct wiphy *wiphy)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211com *ic;
+ struct ieee80211vap *vap;
+
+ lhw = wiphy_priv(wiphy);
+ ic = lhw->ic;
+
+ /*
+ * If we haven't called ieee80211_ifattach() yet
+ * or there is no VAP, there are no scans to flush.
+ */
+ if (ic == NULL ||
+ (lhw->sc_flags & LKPI_MAC80211_DRV_STARTED) == 0)
+ return;
+
+ /* Should only happen on the current one? Not seen it late enough. */
+ IEEE80211_LOCK(ic);
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ ieee80211_scan_flush(vap);
+ IEEE80211_UNLOCK(ic);
+}
+
+/* -------------------------------------------------------------------------- */
+
MODULE_VERSION(linuxkpi_wlan, 1);
MODULE_DEPEND(linuxkpi_wlan, linuxkpi, 1, 1, 1);
MODULE_DEPEND(linuxkpi_wlan, wlan, 1, 1, 1);