aboutsummaryrefslogtreecommitdiff
path: root/sys/compat/linuxkpi/common
diff options
context:
space:
mode:
Diffstat (limited to 'sys/compat/linuxkpi/common')
-rw-r--r--sys/compat/linuxkpi/common/include/acpi/acpi.h2
-rw-r--r--sys/compat/linuxkpi/common/include/linux/ieee80211.h26
-rw-r--r--sys/compat/linuxkpi/common/include/linux/netdevice.h9
-rw-r--r--sys/compat/linuxkpi/common/include/linux/string_helpers.h2
-rw-r--r--sys/compat/linuxkpi/common/include/net/cfg80211.h34
-rw-r--r--sys/compat/linuxkpi/common/include/net/mac80211.h18
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.c879
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211.h39
-rw-r--r--sys/compat/linuxkpi/common/src/linux_80211_macops.c2
9 files changed, 827 insertions, 184 deletions
diff --git a/sys/compat/linuxkpi/common/include/acpi/acpi.h b/sys/compat/linuxkpi/common/include/acpi/acpi.h
index 1e398d05ba20..016c7ede0f6e 100644
--- a/sys/compat/linuxkpi/common/include/acpi/acpi.h
+++ b/sys/compat/linuxkpi/common/include/acpi/acpi.h
@@ -37,7 +37,7 @@
/*
* LINUXKPI_WANT_LINUX_ACPI is a temporary workaround to allow drm-kmod
* to update all needed branches without breaking builds.
- * Once that happened and checks are implemented based on __FreeBSD_verison
+ * Once that happened and checks are implemented based on __FreeBSD_version
* we will remove these conditions again.
*/
diff --git a/sys/compat/linuxkpi/common/include/linux/ieee80211.h b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
index 5851ac08f083..33850359869f 100644
--- a/sys/compat/linuxkpi/common/include/linux/ieee80211.h
+++ b/sys/compat/linuxkpi/common/include/linux/ieee80211.h
@@ -147,9 +147,9 @@ enum ieee80211_vht_max_ampdu_len_exp {
enum wlan_ht_cap_sm_ps {
WLAN_HT_CAP_SM_PS_STATIC = 0,
- WLAN_HT_CAP_SM_PS_DYNAMIC,
- WLAN_HT_CAP_SM_PS_INVALID,
- WLAN_HT_CAP_SM_PS_DISABLED,
+ WLAN_HT_CAP_SM_PS_DYNAMIC = 1,
+ WLAN_HT_CAP_SM_PS_INVALID = 2,
+ WLAN_HT_CAP_SM_PS_DISABLED = 3
};
#define WLAN_MAX_KEY_LEN 32
@@ -524,24 +524,24 @@ struct ieee80211_mgmt {
uint16_t beacon_int;
uint16_t capab_info;
uint8_t variable[0];
- } beacon;
+ } __packed beacon;
/* 9.3.3.5 Association Request frame format */
struct {
uint16_t capab_info;
uint16_t listen_interval;
uint8_t variable[0];
- } assoc_req;
+ } __packed assoc_req;
/* 9.3.3.10 Probe Request frame format */
struct {
uint8_t variable[0];
- } probe_req;
+ } __packed probe_req;
/* 9.3.3.11 Probe Response frame format */
struct {
uint64_t timestamp;
uint16_t beacon_int;
uint16_t capab_info;
uint8_t variable[0];
- } probe_resp;
+ } __packed probe_resp;
/* 9.3.3.14 Action frame format */
struct {
/* 9.4.1.11 Action field */
@@ -557,7 +557,7 @@ struct ieee80211_mgmt {
uint8_t tpc_elem_length;
uint8_t tpc_elem_tx_power;
uint8_t tpc_elem_link_margin;
- } tpc_report;
+ } __packed tpc_report;
/* 9.6.8.33 Fine Timing Measurement frame format */
struct {
uint8_t dialog_token;
@@ -567,7 +567,7 @@ struct ieee80211_mgmt {
uint16_t tod_error;
uint16_t toa_error;
uint8_t variable[0];
- } ftm;
+ } __packed ftm;
/* 802.11-2016, 9.6.5.2 ADDBA Request frame format */
struct {
uint8_t action_code;
@@ -577,16 +577,16 @@ struct ieee80211_mgmt {
uint16_t start_seq_num;
/* Optional follows... */
uint8_t variable[0];
- } addba_req;
+ } __packed addba_req;
/* XXX */
struct {
uint8_t dialog_token;
- } wnm_timing_msr;
+ } __packed wnm_timing_msr;
} u;
- } action;
+ } __packed action;
DECLARE_FLEX_ARRAY(uint8_t, body);
} u;
-};
+} __packed __aligned(2);
struct ieee80211_cts { /* net80211::ieee80211_frame_cts */
__le16 frame_control;
diff --git a/sys/compat/linuxkpi/common/include/linux/netdevice.h b/sys/compat/linuxkpi/common/include/linux/netdevice.h
index cd7d23077a62..3b808a4a1749 100644
--- a/sys/compat/linuxkpi/common/include/linux/netdevice.h
+++ b/sys/compat/linuxkpi/common/include/linux/netdevice.h
@@ -4,7 +4,7 @@
* Copyright (c) 2010 Panasas, Inc.
* Copyright (c) 2013-2019 Mellanox Technologies, Ltd.
* All rights reserved.
- * Copyright (c) 2020-2021 The FreeBSD Foundation
+ * Copyright (c) 2020-2025 The FreeBSD Foundation
* Copyright (c) 2020-2022 Bjoern A. Zeeb
*
* Portions of this software were developed by Björn Zeeb
@@ -302,6 +302,13 @@ netdev_rss_key_fill(uint32_t *buf, size_t len)
get_random_bytes(buf, len);
}
+static inline void
+__hw_addr_init(struct netdev_hw_addr_list *list)
+{
+ list->count = 0;
+ INIT_LIST_HEAD(&list->addr_list);
+}
+
static inline int
netdev_hw_addr_list_count(struct netdev_hw_addr_list *list)
{
diff --git a/sys/compat/linuxkpi/common/include/linux/string_helpers.h b/sys/compat/linuxkpi/common/include/linux/string_helpers.h
index 1bdff2730361..2c6fe0b1708d 100644
--- a/sys/compat/linuxkpi/common/include/linux/string_helpers.h
+++ b/sys/compat/linuxkpi/common/include/linux/string_helpers.h
@@ -66,4 +66,6 @@ str_enable_disable(bool value)
return "disable";
}
+#define str_disable_enable(_v) str_enable_disable(!(_v))
+
#endif
diff --git a/sys/compat/linuxkpi/common/include/net/cfg80211.h b/sys/compat/linuxkpi/common/include/net/cfg80211.h
index 044f348ef08b..239b4a5ae7b8 100644
--- a/sys/compat/linuxkpi/common/include/net/cfg80211.h
+++ b/sys/compat/linuxkpi/common/include/net/cfg80211.h
@@ -57,8 +57,8 @@ extern int linuxkpi_debug_80211;
#endif
#define TODO(fmt, ...) if (linuxkpi_debug_80211 & D80211_TODO) \
printf("%s:%d: XXX LKPI80211 TODO " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
-#define IMPROVE(...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
- printf("%s:%d: XXX LKPI80211 IMPROVE\n", __func__, __LINE__)
+#define IMPROVE(fmt, ...) if (linuxkpi_debug_80211 & D80211_IMPROVE) \
+ printf("%s:%d: XXX LKPI80211 IMPROVE " fmt "\n", __func__, __LINE__, ##__VA_ARGS__)
enum rfkill_hard_block_reasons {
RFKILL_HARD_BLOCK_NOT_OWNER = BIT(0),
@@ -128,19 +128,24 @@ struct ieee80211_txrx_stypes {
uint16_t rx;
};
-/* XXX net80211 has an ieee80211_channel as well. */
+/*
+ * net80211 has an ieee80211_channel as well; we use the linuxkpi_ version
+ * interally in LinuxKPI and re-define ieee80211_channel for the drivers
+ * at the end of the file.
+ */
struct linuxkpi_ieee80211_channel {
- /* TODO FIXME */
- uint32_t hw_value; /* ic_ieee */
- uint32_t center_freq; /* ic_freq */
- enum ieee80211_channel_flags flags; /* ic_flags */
+ uint32_t center_freq;
+ uint16_t hw_value;
+ enum ieee80211_channel_flags flags;
enum nl80211_band band;
- int8_t max_power; /* ic_maxpower */
bool beacon_found;
- int max_antenna_gain, max_reg_power;
- int orig_flags;
- int dfs_cac_ms, dfs_state;
- int orig_mpwr;
+ enum nl80211_dfs_state dfs_state;
+ unsigned int dfs_cac_ms;
+ int max_antenna_gain;
+ int max_power;
+ int max_reg_power;
+ uint32_t orig_flags;
+ int orig_mpwr;
};
struct cfg80211_bitrate_mask {
@@ -1299,10 +1304,9 @@ reg_query_regdb_wmm(uint8_t *alpha2, uint32_t center_freq,
struct ieee80211_reg_rule *rule)
{
- /* ETSI has special rules. FreeBSD regdb needs to learn about them. */
- TODO();
+ IMPROVE("regdomain.xml needs to grow wmm information for at least ETSI");
- return (-ENXIO);
+ return (-ENODATA);
}
static __inline const u8 *
diff --git a/sys/compat/linuxkpi/common/include/net/mac80211.h b/sys/compat/linuxkpi/common/include/net/mac80211.h
index 2ed595095f9e..0106e6648bd4 100644
--- a/sys/compat/linuxkpi/common/include/net/mac80211.h
+++ b/sys/compat/linuxkpi/common/include/net/mac80211.h
@@ -87,6 +87,9 @@ enum mcast_filter_flags {
FIF_PSPOLL = BIT(5),
FIF_CONTROL = BIT(6),
FIF_MCAST_ACTION = BIT(7),
+
+ /* Must stay last. */
+ FIF_FLAGS_MASK = BIT(8)-1,
};
enum ieee80211_bss_changed {
@@ -1135,7 +1138,7 @@ extern const struct cfg80211_ops linuxkpi_mac80211cfgops;
struct ieee80211_hw *linuxkpi_ieee80211_alloc_hw(size_t,
const struct ieee80211_ops *);
void linuxkpi_ieee80211_iffree(struct ieee80211_hw *);
-void linuxkpi_set_ieee80211_dev(struct ieee80211_hw *, char *);
+void linuxkpi_set_ieee80211_dev(struct ieee80211_hw *);
int linuxkpi_ieee80211_ifattach(struct ieee80211_hw *);
void linuxkpi_ieee80211_ifdetach(struct ieee80211_hw *);
void linuxkpi_ieee80211_unregister_hw(struct ieee80211_hw *);
@@ -1255,7 +1258,7 @@ SET_IEEE80211_DEV(struct ieee80211_hw *hw, struct device *dev)
{
set_wiphy_dev(hw->wiphy, dev);
- linuxkpi_set_ieee80211_dev(hw, dev_name(dev));
+ linuxkpi_set_ieee80211_dev(hw);
IMPROVE();
}
@@ -1741,12 +1744,15 @@ ieee80211_request_smps(struct ieee80211_vif *vif, u_int link_id,
"SMPS_STATIC",
"SMPS_DYNAMIC",
"SMPS_AUTOMATIC",
- "SMPS_NUM_MODES"
};
- if (linuxkpi_debug_80211 & D80211_TODO)
- printf("%s:%d: XXX LKPI80211 TODO smps %d %s\n",
- __func__, __LINE__, smps, smps_mode_name[smps]);
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return;
+
+ if (smps >= nitems(smps_mode_name))
+ panic("%s: unsupported smps value: %d\n", __func__, smps);
+
+ IMPROVE("XXX LKPI80211 TODO smps %d %s\n", smps, smps_mode_name[smps]);
}
static __inline void
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.c b/sys/compat/linuxkpi/common/src/linux_80211.c
index a7d6003843ba..3f850653bc7c 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211.c
@@ -77,6 +77,8 @@
#include <linux/rculist.h>
#include "linux_80211.h"
+/* #define LKPI_80211_USE_SCANLIST */
+/* #define LKPI_80211_BGSCAN */
#define LKPI_80211_WME
#define LKPI_80211_HW_CRYPTO
#define LKPI_80211_HT
@@ -103,6 +105,10 @@ SYSCTL_DECL(_compat_linuxkpi);
SYSCTL_NODE(_compat_linuxkpi, OID_AUTO, 80211, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"LinuxKPI 802.11 compatibility layer");
+static bool lkpi_order_scanlist = false;
+SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, order_scanlist, CTLFLAG_RW,
+ &lkpi_order_scanlist, 0, "Enable LinuxKPI 802.11 scan list shuffeling");
+
#if defined(LKPI_80211_HW_CRYPTO)
static bool lkpi_hwcrypto = false;
SYSCTL_BOOL(_compat_linuxkpi_80211, OID_AUTO, hw_crypto, CTLFLAG_RDTUN,
@@ -167,6 +173,7 @@ const struct cfg80211_ops linuxkpi_mac80211cfgops = {
static struct lkpi_sta *lkpi_find_lsta_by_ni(struct lkpi_vif *,
struct ieee80211_node *);
#endif
+static void lkpi_sw_scan_task(void *, int);
static void lkpi_80211_txq_tx_one(struct lkpi_sta *, struct mbuf *);
static void lkpi_80211_txq_task(void *, int);
static void lkpi_80211_lhw_rxq_task(void *, int);
@@ -274,48 +281,40 @@ lkpi_nl80211_sta_info_to_str(struct sbuf *s, const char *prefix,
sbuf_printf(s, "\n");
}
-static int
-lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
+static void
+lkpi_80211_dump_lvif_stas(struct lkpi_vif *lvif, struct sbuf *s)
{
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 station_info sinfo;
- struct sbuf s;
int error;
- if (req->newptr)
- return (EPERM);
-
- lvif = (struct lkpi_vif *)arg1;
vif = LVIF_TO_VIF(lvif);
vap = LVIF_TO_VAP(lvif);
lhw = vap->iv_ic->ic_softc;
hw = LHW_TO_HW(lhw);
- sbuf_new_for_sysctl(&s, NULL, 1024, req);
-
wiphy_lock(hw->wiphy);
list_for_each_entry(lsta, &lvif->lsta_list, lsta_list) {
sta = LSTA_TO_STA(lsta);
- sbuf_putc(&s, '\n');
- sbuf_printf(&s, "lsta %p sta %p added_to_drv %d\n", lsta, sta, lsta->added_to_drv);
+ sbuf_putc(s, '\n');
+ sbuf_printf(s, "lsta %p sta %p added_to_drv %d\n", lsta, sta, lsta->added_to_drv);
memset(&sinfo, 0, sizeof(sinfo));
error = lkpi_80211_mo_sta_statistics(hw, vif, sta, &sinfo);
if (error == EEXIST) /* Not added to driver. */
continue;
if (error == ENOTSUPP) {
- sbuf_printf(&s, " sta_statistics not supported\n");
+ sbuf_printf(s, " sta_statistics not supported\n");
continue;
}
if (error != 0) {
- sbuf_printf(&s, " sta_statistics failed: %d\n", error);
+ sbuf_printf(s, " sta_statistics failed: %d\n", error);
continue;
}
@@ -325,51 +324,76 @@ lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
memcpy(&sinfo.rxrate, &lsta->sinfo.rxrate, sizeof(sinfo.rxrate));
sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
}
+ /* If no CHAIN_SIGNAL is reported, try to fill it in from the lsta sinfo. */
+ if ((sinfo.filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) == 0 &&
+ (lsta->sinfo.filled & BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL)) != 0) {
+ sinfo.chains = lsta->sinfo.chains;
+ memcpy(sinfo.chain_signal, lsta->sinfo.chain_signal,
+ sizeof(sinfo.chain_signal));
+ sinfo.filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
+ }
- lkpi_nl80211_sta_info_to_str(&s, " nl80211_sta_info (valid fields)", sinfo.filled);
- sbuf_printf(&s, " connected_time %u inactive_time %u\n",
+ lkpi_nl80211_sta_info_to_str(s, " nl80211_sta_info (valid fields)", sinfo.filled);
+ sbuf_printf(s, " connected_time %u inactive_time %u\n",
sinfo.connected_time, sinfo.inactive_time);
- sbuf_printf(&s, " rx_bytes %ju rx_packets %u rx_dropped_misc %u\n",
+ sbuf_printf(s, " rx_bytes %ju rx_packets %u rx_dropped_misc %u\n",
(uintmax_t)sinfo.rx_bytes, sinfo.rx_packets, sinfo.rx_dropped_misc);
- sbuf_printf(&s, " rx_duration %ju rx_beacon %u rx_beacon_signal_avg %d\n",
+ sbuf_printf(s, " rx_duration %ju rx_beacon %u rx_beacon_signal_avg %d\n",
(uintmax_t)sinfo.rx_duration, sinfo.rx_beacon, (int8_t)sinfo.rx_beacon_signal_avg);
- sbuf_printf(&s, " tx_bytes %ju tx_packets %u tx_failed %u\n",
+ sbuf_printf(s, " tx_bytes %ju tx_packets %u tx_failed %u\n",
(uintmax_t)sinfo.tx_bytes, sinfo.tx_packets, sinfo.tx_failed);
- sbuf_printf(&s, " tx_duration %ju tx_retries %u\n",
+ sbuf_printf(s, " tx_duration %ju tx_retries %u\n",
(uintmax_t)sinfo.tx_duration, sinfo.tx_retries);
- sbuf_printf(&s, " signal %d signal_avg %d ack_signal %d avg_ack_signal %d\n",
+ sbuf_printf(s, " signal %d signal_avg %d ack_signal %d avg_ack_signal %d\n",
sinfo.signal, sinfo.signal_avg, sinfo.ack_signal, sinfo.avg_ack_signal);
-
- sbuf_printf(&s, " generation %d assoc_req_ies_len %zu chains %d\n",
+ sbuf_printf(s, " generation %d assoc_req_ies_len %zu chains %#04x\n",
sinfo.generation, sinfo.assoc_req_ies_len, sinfo.chains);
- for (int i = 0; i < sinfo.chains && i < IEEE80211_MAX_CHAINS; i++) {
- sbuf_printf(&s, " chain[%d] signal %d signal_avg %d\n",
+ for (int i = 0; i < nitems(sinfo.chain_signal) && i < IEEE80211_MAX_CHAINS; i++) {
+ if (!(sinfo.chains & BIT(i)))
+ continue;
+ sbuf_printf(s, " chain[%d] signal %d signal_avg %d\n",
i, (int8_t)sinfo.chain_signal[i], (int8_t)sinfo.chain_signal_avg[i]);
}
/* assoc_req_ies, bss_param, sta_flags */
- sbuf_printf(&s, " rxrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
+ sbuf_printf(s, " rxrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
sinfo.rxrate.flags, CFG80211_RATE_INFO_FLAGS_BITS,
sinfo.rxrate.bw, lkpi_rate_info_bw_to_str(sinfo.rxrate.bw),
sinfo.rxrate.legacy * 100,
sinfo.rxrate.mcs, sinfo.rxrate.nss);
- sbuf_printf(&s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
+ sbuf_printf(s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
sinfo.rxrate.he_dcm, sinfo.rxrate.he_gi, sinfo.rxrate.he_ru_alloc,
sinfo.rxrate.eht_gi);
- sbuf_printf(&s, " txrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
+ sbuf_printf(s, " txrate: flags %b bw %u(%s) legacy %u kbit/s mcs %u nss %u\n",
sinfo.txrate.flags, CFG80211_RATE_INFO_FLAGS_BITS,
sinfo.txrate.bw, lkpi_rate_info_bw_to_str(sinfo.txrate.bw),
sinfo.txrate.legacy * 100,
sinfo.txrate.mcs, sinfo.txrate.nss);
- sbuf_printf(&s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
+ sbuf_printf(s, " he_dcm %u he_gi %u he_ru_alloc %u eht_gi %u\n",
sinfo.txrate.he_dcm, sinfo.txrate.he_gi, sinfo.txrate.he_ru_alloc,
sinfo.txrate.eht_gi);
}
wiphy_unlock(hw->wiphy);
+}
+
+static int
+lkpi_80211_dump_stas(SYSCTL_HANDLER_ARGS)
+{
+ struct lkpi_vif *lvif;
+ struct sbuf s;
+
+ if (req->newptr)
+ return (EPERM);
+
+ lvif = (struct lkpi_vif *)arg1;
+
+ sbuf_new_for_sysctl(&s, NULL, 1024, req);
+
+ lkpi_80211_dump_lvif_stas(lvif, &s);
sbuf_finish(&s);
sbuf_delete(&s);
@@ -938,6 +962,30 @@ lkpi_nl80211_band_to_net80211_band(enum nl80211_band band)
return (0x00);
}
+#ifdef LINUXKPI_DEBUG_80211
+static const char *
+lkpi_nl80211_band_name(enum nl80211_band band)
+{
+ switch (band) {
+ case NL80211_BAND_2GHZ:
+ return "2Ghz";
+ break;
+ case NL80211_BAND_5GHZ:
+ return "5Ghz";
+ break;
+ case NL80211_BAND_60GHZ:
+ return "60Ghz";
+ break;
+ case NL80211_BAND_6GHZ:
+ return "6Ghz";
+ break;
+ default:
+ panic("%s: unsupported band %u\n", __func__, band);
+ break;
+ }
+}
+#endif
+
#if 0
static enum ieee80211_ac_numbers
lkpi_ac_net_to_l80211(int ac)
@@ -1162,7 +1210,7 @@ lkpi_find_lkpi80211_chan(struct lkpi_hw *lhw,
channels = hw->wiphy->bands[band]->channels;
for (i = 0; i < nchans; i++) {
- if (channels[i].hw_value == c->ic_ieee)
+ if (channels[i].center_freq == c->ic_freq)
return (&channels[i]);
}
@@ -1302,6 +1350,7 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
lhw = ic->ic_softc;
hw = LHW_TO_HW(lhw);
lvif = VAP_TO_LVIF(vap);
+ vif = LVIF_TO_VIF(lvif);
/*
* Make sure we do not make it here without going through
@@ -1309,6 +1358,23 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
*/
lockdep_assert_wiphy(hw->wiphy);
+ /*
+ * While we are assoc we may still send packets. We cannot delete the
+ * keys as otherwise packets could go out unencrypted. Some firmware
+ * does not like this and will fire an assert.
+ * net80211 needs to drive this better but given we want the disassoc
+ * frame out and have to unlock we are open to a race currently.
+ * This check should prevent problems.
+ * How to test: run 800Mbit/s UDP traffic and during that restart your
+ * supplicant. You want to survive that.
+ */
+ if (vif->cfg.assoc) {
+ if (linuxkpi_debug_80211 & D80211_TRACE_HW_CRYPTO)
+ ic_printf(ic, "%d %lu %s: vif still assoc; not deleting keys\n",
+ curthread->td_tid, jiffies, __func__);
+ return (0);
+ }
+
if (IEEE80211_KEY_UNDEFINED(k)) {
ic_printf(ic, "%s: vap %p key %p is undefined: %p %u\n",
__func__, vap, k, k->wk_cipher, k->wk_keyix);
@@ -1353,7 +1419,6 @@ lkpi_iv_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k)
kc->keyidx, kc->hw_key_idx, kc->flags, IEEE80211_KEY_FLAG_BITS);
#endif
- vif = LVIF_TO_VIF(lvif);
error = lkpi_80211_mo_set_key(hw, DISABLE_KEY, vif, sta, kc);
if (error != 0) {
ic_printf(ic, "%d %lu %s: set_key cmd %d(%s) for sta %6D failed: %d\n",
@@ -1700,6 +1765,24 @@ lkpi_iv_key_update_end(struct ieee80211vap *vap)
}
#endif
+static void
+lkpi_cleanup_mcast_list_locked(struct lkpi_hw *lhw)
+{
+ struct list_head *le, *next;
+ struct netdev_hw_addr *addr;
+
+ if (lhw->mc_list.count != 0) {
+ list_for_each_safe(le, next, &lhw->mc_list.addr_list) {
+ addr = list_entry(le, struct netdev_hw_addr, addr_list);
+ list_del(le);
+ lhw->mc_list.count--;
+ free(addr, M_LKPI80211);
+ }
+ }
+ KASSERT(lhw->mc_list.count == 0, ("%s: mc_list %p count %d != 0\n",
+ __func__, &lhw->mc_list, lhw->mc_list.count));
+}
+
static u_int
lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
@@ -1736,16 +1819,13 @@ lkpi_ic_update_mcast_copy(void *arg, struct sockaddr_dl *sdl, u_int cnt)
}
static void
-lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
+lkpi_update_mcast_filter(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
struct ieee80211_hw *hw;
- struct netdev_hw_addr_list mc_list;
- struct list_head *le, *next;
- struct netdev_hw_addr *addr;
- struct ieee80211vap *vap;
u64 mc;
- unsigned int changed_flags, total_flags;
+ unsigned int changed_flags, flags;
+ bool scanning;
lhw = ic->ic_softc;
@@ -1753,44 +1833,32 @@ lkpi_update_mcast_filter(struct ieee80211com *ic, bool force)
lhw->ops->configure_filter == NULL)
return;
- if (!lhw->update_mc && !force)
- return;
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ scanning = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
- changed_flags = total_flags = 0;
- mc_list.count = 0;
- INIT_LIST_HEAD(&mc_list.addr_list);
- if (ic->ic_allmulti == 0) {
- TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
- if_foreach_llmaddr(vap->iv_ifp,
- lkpi_ic_update_mcast_copy, &mc_list);
- } else {
- changed_flags |= FIF_ALLMULTI;
- }
+ LKPI_80211_LHW_MC_LOCK(lhw);
+
+ flags = 0;
+ if (scanning)
+ flags |= FIF_BCN_PRBRESP_PROMISC;
+ if (lhw->mc_all_multi)
+ flags |= FIF_ALLMULTI;
hw = LHW_TO_HW(lhw);
- mc = lkpi_80211_mo_prepare_multicast(hw, &mc_list);
- /*
- * XXX-BZ make sure to get this sorted what is a change,
- * what gets all set; what was already set?
- */
- total_flags = changed_flags;
- lkpi_80211_mo_configure_filter(hw, changed_flags, &total_flags, mc);
+ mc = lkpi_80211_mo_prepare_multicast(hw, &lhw->mc_list);
+
+ changed_flags = (lhw->mc_flags ^ flags) & FIF_FLAGS_MASK;
+ lkpi_80211_mo_configure_filter(hw, changed_flags, &flags, mc);
+ lhw->mc_flags = flags;
#ifdef LINUXKPI_DEBUG_80211
if (linuxkpi_debug_80211 & D80211_TRACE)
- printf("%s: changed_flags %#06x count %d total_flags %#010x\n",
- __func__, changed_flags, mc_list.count, total_flags);
+ printf("%s: changed_flags %#06x count %d mc_flags %#010x\n",
+ __func__, changed_flags, lhw->mc_list.count, lhw->mc_flags);
#endif
- if (mc_list.count != 0) {
- list_for_each_safe(le, next, &mc_list.addr_list) {
- addr = list_entry(le, struct netdev_hw_addr, addr_list);
- free(addr, M_LKPI80211);
- mc_list.count--;
- }
- }
- KASSERT(mc_list.count == 0, ("%s: mc_list %p count %d != 0\n",
- __func__, &mc_list, mc_list.count));
+ LKPI_80211_LHW_MC_UNLOCK(lhw);
}
static enum ieee80211_bss_changed
@@ -1822,13 +1890,13 @@ lkpi_update_dtim_tsf(struct ieee80211_vif *vif, struct ieee80211_node *ni,
vif->bss_conf.beacon_int = 16;
bss_changed |= BSS_CHANGED_BEACON_INT;
}
- if (vif->bss_conf.dtim_period != vap->iv_dtim_period &&
- vap->iv_dtim_period > 0) {
- vif->bss_conf.dtim_period = vap->iv_dtim_period;
+ if (vif->bss_conf.dtim_period != ni->ni_dtim_period &&
+ ni->ni_dtim_period > 0) {
+ vif->bss_conf.dtim_period = ni->ni_dtim_period;
bss_changed |= BSS_CHANGED_BEACON_INFO;
}
- vif->bss_conf.sync_dtim_count = vap->iv_dtim_count;
+ vif->bss_conf.sync_dtim_count = ni->ni_dtim_count;
vif->bss_conf.sync_tsf = le64toh(ni->ni_tstamp.tsf);
/* vif->bss_conf.sync_device_ts = set in linuxkpi_ieee80211_rx. */
@@ -1856,6 +1924,8 @@ lkpi_stop_hw_scan(struct lkpi_hw *lhw, struct ieee80211_vif *vif)
int error;
bool cancel;
+ TRACE_SCAN(lhw->ic, "scan_flags %b", lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
cancel = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -1909,19 +1979,19 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
struct lkpi_hw *lhw)
{
enum ieee80211_bss_changed changed;
+ struct lkpi_vif *lvif;
changed = 0;
sta->aid = 0;
if (vif->cfg.assoc) {
- lhw->update_mc = true;
- lkpi_update_mcast_filter(lhw->ic, true);
-
vif->cfg.assoc = false;
vif->cfg.aid = 0;
changed |= BSS_CHANGED_ASSOC;
IMPROVE();
+ lkpi_update_mcast_filter(lhw->ic);
+
/*
* Executing the bss_info_changed(BSS_CHANGED_ASSOC) with
* assoc = false right away here will remove the sta from
@@ -1932,6 +2002,9 @@ lkpi_disassoc(struct ieee80211_sta *sta, struct ieee80211_vif *vif,
* bss_info_changed() update.
* See lkpi_sta_run_to_init() for more detailed comment.
*/
+
+ lvif = VIF_TO_LVIF(vif);
+ lvif->beacons = 0;
}
return (changed);
@@ -2202,6 +2275,7 @@ lkpi_sta_scan_to_auth(struct ieee80211vap *vap, enum ieee80211_state nstate, int
/* vif->bss_conf.basic_rates ? Where exactly? */
+ lvif->beacons = 0;
/* Should almost assert it is this. */
vif->cfg.assoc = false;
vif->cfg.aid = 0;
@@ -2391,6 +2465,7 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
struct lkpi_sta *lsta;
struct ieee80211_sta *sta;
struct ieee80211_prep_tx_info prep_tx_info;
+ enum ieee80211_bss_changed bss_changed;
int error;
lhw = vap->iv_ic->ic_softc;
@@ -2462,6 +2537,11 @@ lkpi_sta_auth_to_scan(struct ieee80211vap *vap, enum ieee80211_state nstate, int
lsta->added_to_drv = false; /* mo manages. */
#endif
+ bss_changed = 0;
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
LKPI_80211_LVIF_LOCK(lvif);
@@ -2768,6 +2848,14 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
bss_changed = 0;
bss_changed |= lkpi_disassoc(sta, vif, lhw);
+#ifdef LKPI_80211_HW_CRYPTO
+ /*
+ * In theory we remove keys here but there must not exist any for this
+ * state change until we clean them up again into small steps and no
+ * code duplication.
+ */
+#endif
+
lkpi_lsta_dump(lsta, ni, __func__, __LINE__);
/* Adjust sta and change state (from NONE) to NOTEXIST. */
@@ -2790,6 +2878,8 @@ _lkpi_sta_assoc_to_down(struct ieee80211vap *vap, enum ieee80211_state nstate, i
vif->cfg.ssid_len = 0;
memset(vif->cfg.ssid, '\0', sizeof(vif->cfg.ssid));
bss_changed |= BSS_CHANGED_BSSID;
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
LKPI_80211_LVIF_LOCK(lvif);
@@ -2922,6 +3012,7 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed |= lkpi_wme_update(lhw, vap, true);
#endif
if (!vif->cfg.assoc || vif->cfg.aid != IEEE80211_NODE_AID(ni)) {
+ lvif->beacons = 0;
vif->cfg.assoc = true;
vif->cfg.aid = IEEE80211_NODE_AID(ni);
bss_changed |= BSS_CHANGED_ASSOC;
@@ -2970,9 +3061,6 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* - set_key (?)
* - ipv6_addr_change (?)
*/
- /* Prepare_multicast && configure_filter. */
- lhw->update_mc = true;
- lkpi_update_mcast_filter(vap->iv_ic, true);
if (!ieee80211_node_is_authorized(ni)) {
IMPROVE("net80211 does not consider node authorized");
@@ -3011,6 +3099,9 @@ lkpi_sta_assoc_to_run(struct ieee80211vap *vap, enum ieee80211_state nstate, int
bss_changed |= lkpi_update_dtim_tsf(vif, ni, vap, __func__, __LINE__);
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+ /* Prepare_multicast && configure_filter. */
+ lkpi_update_mcast_filter(vap->iv_ic);
+
out:
wiphy_unlock(hw->wiphy);
IEEE80211_LOCK(vap->iv_ic);
@@ -3300,6 +3391,16 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
#ifdef LKPI_80211_HW_CRYPTO
if (lkpi_hwcrypto) {
+ /*
+ * In theory we only need to do this if we changed assoc.
+ * If we were not assoc, there should be no keys and we
+ * should not be here.
+ */
+#ifdef notyet
+ KASSERT((bss_changed & BSS_CHANGED_ASSOC) != 0, ("%s: "
+ "trying to remove keys but were not assoc: %#010jx, lvif %p\n",
+ __func__, (uintmax_t)bss_changed, lvif));
+#endif
error = lkpi_sta_del_keys(hw, vif, lsta);
if (error != 0) {
ic_printf(vap->iv_ic, "%s:%d: lkpi_sta_del_keys "
@@ -3361,6 +3462,9 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
* 4) call unassign_vif_chanctx
* 5) call lkpi_hw_conf_idle
* 6) call remove_chanctx
+ *
+ * Note: vif->driver_flags & IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC
+ * might change this.
*/
bss_changed |= lkpi_disassoc(sta, vif, lhw);
@@ -3391,6 +3495,8 @@ lkpi_sta_run_to_init(struct ieee80211vap *vap, enum ieee80211_state nstate, int
vif->bss_conf.use_short_preamble = false;
vif->bss_conf.qos = false;
/* XXX BSS_CHANGED_???? */
+ vif->bss_conf.dtim_period = 0; /* go back to 0. */
+ bss_changed |= BSS_CHANGED_BEACON_INFO;
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
LKPI_80211_LVIF_LOCK(lvif);
@@ -3510,7 +3616,7 @@ lkpi_iv_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg)
vif = LVIF_TO_VIF(lvif);
/* No need to replicate this in most state handlers. */
- if (ostate == IEEE80211_S_SCAN && nstate != IEEE80211_S_SCAN)
+ if (nstate > IEEE80211_S_SCAN)
lkpi_stop_hw_scan(lhw, vif);
s = sta_state_fsm;
@@ -3693,6 +3799,42 @@ lkpi_ic_wme_update(struct ieee80211com *ic)
return (0); /* unused */
}
+static void
+lkpi_iv_sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0,
+ int subtype, const struct ieee80211_rx_stats *rxs, int rssi, int nf)
+{
+ struct lkpi_hw *lhw;
+ struct ieee80211_hw *hw;
+ struct lkpi_vif *lvif;
+ struct ieee80211_vif *vif;
+ enum ieee80211_bss_changed bss_changed;
+
+ lvif = VAP_TO_LVIF(ni->ni_vap);
+
+ lvif->iv_recv_mgmt(ni, m0, subtype, rxs, rssi, nf);
+
+ switch (subtype) {
+ case IEEE80211_FC0_SUBTYPE_PROBE_RESP:
+ break;
+ case IEEE80211_FC0_SUBTYPE_BEACON:
+ lvif->beacons++;
+ break;
+ default:
+ return;
+ }
+
+ vif = LVIF_TO_VIF(lvif);
+ lhw = ni->ni_ic->ic_softc;
+ hw = LHW_TO_HW(lhw);
+
+ /*
+ * If this direct call to mo_bss_info_changed will not work due to
+ * locking, see if queue_work() is fast enough.
+ */
+ bss_changed = lkpi_update_dtim_tsf(vif, ni, ni->ni_vap, __func__, __LINE__);
+ lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, bss_changed);
+}
+
/*
* Change link-layer address on the vif (if the vap is not started/"UP").
* This can happen if a user changes 'ether' using ifconfig.
@@ -3753,6 +3895,7 @@ 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);
+ TASK_INIT(&lvif->sw_scan_task, 0, lkpi_sw_scan_task, lvif);
INIT_LIST_HEAD(&lvif->lsta_list);
lvif->lvif_bss = NULL;
refcount_init(&lvif->nt_unlocked, 0);
@@ -3826,8 +3969,10 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
IMPROVE();
+ wiphy_lock(hw->wiphy);
error = lkpi_80211_mo_start(hw);
if (error != 0) {
+ wiphy_unlock(hw->wiphy);
ic_printf(ic, "%s: failed to start hw: %d\n", __func__, error);
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
@@ -3837,11 +3982,13 @@ 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()? */
+ wiphy_unlock(hw->wiphy);
ic_printf(ic, "%s: failed to add interface: %d\n", __func__, error);
mtx_destroy(&lvif->mtx);
free(lvif, M_80211_VAP);
return (NULL);
}
+ wiphy_unlock(hw->wiphy);
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_INSERT_TAIL(&lhw->lvif_head, lvif, lvif_entry);
@@ -3871,17 +4018,21 @@ lkpi_ic_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ],
lkpi_80211_mo_bss_info_changed(hw, vif, &vif->bss_conf, changed);
/* Force MC init. */
- lkpi_update_mcast_filter(ic, true);
-
- IMPROVE();
+ lkpi_update_mcast_filter(ic);
ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid);
+ /* Now we have a valid vap->iv_ifp. Any checksum offloading goes below. */
+
+ IMPROVE();
+
/* Override with LinuxKPI method so we can drive mac80211/cfg80211. */
lvif->iv_newstate = vap->iv_newstate;
vap->iv_newstate = lkpi_iv_newstate;
lvif->iv_update_bss = vap->iv_update_bss;
vap->iv_update_bss = lkpi_iv_update_bss;
+ lvif->iv_recv_mgmt = vap->iv_recv_mgmt;
+ vap->iv_recv_mgmt = lkpi_iv_sta_recv_mgmt;
#ifdef LKPI_80211_HW_CRYPTO
/* Key management. */
@@ -3983,6 +4134,8 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
/* Clear up per-VIF/VAP sysctls. */
sysctl_ctx_free(&lvif->sysctl_ctx);
+ ieee80211_draintask(ic, &lvif->sw_scan_task);
+
LKPI_80211_LHW_LVIF_LOCK(lhw);
TAILQ_REMOVE(&lhw->lvif_head, lvif, lvif_entry);
LKPI_80211_LHW_LVIF_UNLOCK(lhw);
@@ -4004,8 +4157,30 @@ lkpi_ic_vap_delete(struct ieee80211vap *vap)
static void
lkpi_ic_update_mcast(struct ieee80211com *ic)
{
+ struct ieee80211vap *vap;
+ struct lkpi_hw *lhw;
- lkpi_update_mcast_filter(ic, false);
+ lhw = ic->ic_softc;
+ if (lhw->ops->prepare_multicast == NULL ||
+ lhw->ops->configure_filter == NULL)
+ return;
+
+ LKPI_80211_LHW_MC_LOCK(lhw);
+ /* Cleanup anything on the current list. */
+ lkpi_cleanup_mcast_list_locked(lhw);
+
+ /* Build up the new list (or allmulti). */
+ if (ic->ic_allmulti == 0) {
+ TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next)
+ if_foreach_llmaddr(vap->iv_ifp,
+ lkpi_ic_update_mcast_copy, &lhw->mc_list);
+ lhw->mc_all_multi = false;
+ } else {
+ lhw->mc_all_multi = true;
+ }
+ LKPI_80211_LHW_MC_UNLOCK(lhw);
+
+ lkpi_update_mcast_filter(ic);
TRACEOK();
}
@@ -4202,6 +4377,113 @@ lkpi_scan_ies_add(uint8_t *p, struct ieee80211_scan_ies *scan_ies,
}
static void
+lkpi_enable_hw_scan(struct lkpi_hw *lhw)
+{
+
+ if (lhw->ops->hw_scan) {
+ /*
+ * Advertise full-offload scanning.
+ *
+ * Not limiting to SINGLE_SCAN_ON_ALL_BANDS here as otherwise
+ * we essentially disable hw_scan for all drivers not setting
+ * the flag.
+ */
+ lhw->ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
+ lhw->scan_flags |= LKPI_LHW_SCAN_HW;
+ }
+}
+
+#ifndef LKPI_80211_USE_SCANLIST
+static const uint32_t chan_pri[] = {
+ 5180, 5500, 5745,
+ 5260, 5580, 5660, 5825,
+ 5220, 5300, 5540, 5620, 5700, 5785, 5865,
+ 2437, 2412, 2422, 2462, 2472, 2432, 2452
+};
+
+static int
+lkpi_scan_chan_list_idx(const struct linuxkpi_ieee80211_channel *lc)
+{
+ int i;
+
+ for (i = 0; i < nitems(chan_pri); i++) {
+ if (lc->center_freq == chan_pri[i])
+ return (i);
+ }
+
+ return (-1);
+}
+
+static int
+lkpi_scan_chan_list_comp(const struct linuxkpi_ieee80211_channel *lc1,
+ const struct linuxkpi_ieee80211_channel *lc2)
+{
+ int idx1, idx2;
+
+ /* Find index in list. */
+ idx1 = lkpi_scan_chan_list_idx(lc1);
+ idx2 = lkpi_scan_chan_list_idx(lc2);
+
+ if (idx1 == -1 && idx2 != -1)
+ return (1);
+ if (idx1 != -1 && idx2 == -1)
+ return (-1);
+
+ /* Neither on the list, use center_freq. */
+ if (idx1 == -1 && idx2 == -1)
+ return (lc1->center_freq - lc2->center_freq);
+
+ /* Whichever is first in the list. */
+ return (idx1 - idx2);
+}
+
+static void
+lkpi_scan_chan_list_resort(struct linuxkpi_ieee80211_channel **cpp, size_t nchan)
+{
+ struct linuxkpi_ieee80211_channel *lc, *nc;
+ size_t i, j;
+ int rc;
+
+ for (i = (nchan - 1); i > 0; i--) {
+ for (j = i; j > 0 ; j--) {
+ lc = *(cpp + j);
+ nc = *(cpp + j - 1);
+ rc = lkpi_scan_chan_list_comp(lc, nc);
+ if (rc < 0) {
+ *(cpp + j) = nc;
+ *(cpp + j - 1) = lc;
+ }
+ }
+ }
+}
+
+static bool
+lkpi_scan_chan(struct linuxkpi_ieee80211_channel *c,
+ struct ieee80211com *ic, bool log)
+{
+
+ if ((c->flags & IEEE80211_CHAN_DISABLED) != 0) {
+ if (log)
+ TRACE_SCAN(ic, "Skipping disabled chan "
+ "on band %s [%#x/%u/%#x]",
+ lkpi_nl80211_band_name(c->band), c->hw_value,
+ c->center_freq, c->flags);
+ return (false);
+ }
+ if (isclr(ic->ic_chan_active, ieee80211_mhz2ieee(c->center_freq,
+ lkpi_nl80211_band_to_net80211_band(c->band)))) {
+ if (log)
+ TRACE_SCAN(ic, "Skipping !active chan "
+ "on band %s [%#x/%u/%#x]",
+ lkpi_nl80211_band_name(c->band), c->hw_value,
+ c->center_freq, c->flags);
+ return (false);
+ }
+ return (true);
+}
+#endif
+
+static void
lkpi_ic_scan_start(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
@@ -4214,33 +4496,52 @@ lkpi_ic_scan_start(struct ieee80211com *ic)
bool is_hw_scan;
lhw = ic->ic_softc;
+ ss = ic->ic_scan;
+ vap = ss->ss_vap;
+ TRACE_SCAN(ic, "scan_flags %b", lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
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);
+ TRACE_SCAN(ic, "Trying to start new scan while still running; "
+ "cancelling new net80211 scan; scan_flags %b",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+ ieee80211_cancel_scan(vap);
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;
+#if 0
if (vap->iv_state != IEEE80211_S_SCAN) {
- IMPROVE("We need to be able to scan if not in S_SCAN");
+ TODO("We need to be able to scan if not in S_SCAN");
+ TRACE_SCAN(ic, "scan_flags %b iv_state %d",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS, vap->iv_state);
+ ieee80211_cancel_scan(vap);
return;
}
+#endif
hw = LHW_TO_HW(lhw);
if (!is_hw_scan) {
/* If hw_scan is cleared clear FEXT_SCAN_OFFLOAD too. */
vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
-sw_scan:
+
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
if (vap->iv_state == IEEE80211_S_SCAN)
lkpi_hw_conf_idle(hw, false);
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ lhw->scan_flags |= LKPI_LHW_SCAN_RUNNING;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+
+ lkpi_update_mcast_filter(ic);
+
+ TRACE_SCAN(vap->iv_ic, "Starting SW_SCAN: scan_flags %b",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS);
lkpi_80211_mo_sw_scan_start(hw, vif, vif->addr);
/* net80211::scan_start() handled PS for us. */
IMPROVE();
@@ -4255,6 +4556,9 @@ sw_scan:
struct cfg80211_scan_6ghz_params *s6gp;
size_t chan_len, nchan, ssids_len, s6ghzlen;
int band, i, ssid_count, common_ie_len;
+#ifndef LKPI_80211_USE_SCANLIST
+ int n;
+#endif
uint32_t band_mask;
uint8_t *ie, *ieend;
bool running;
@@ -4266,7 +4570,8 @@ sw_scan:
band_mask = 0;
nchan = 0;
if (ieee80211_hw_check(hw, SINGLE_SCAN_ON_ALL_BANDS)) {
-#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */
+#ifdef LKPI_80211_USE_SCANLIST
+ /* Avoid net80211 scan lists until it has proper scan offload support. */
for (i = ss->ss_next; i < ss->ss_last; i++) {
nchan++;
band = lkpi_net80211_chan_to_nl80211_band(
@@ -4284,8 +4589,17 @@ sw_scan:
continue;
}
if (hw->wiphy->bands[band] != NULL) {
- nchan += hw->wiphy->bands[band]->n_channels;
+ struct linuxkpi_ieee80211_channel *channels;
+ int n;
+
band_mask |= (1 << band);
+
+ channels = hw->wiphy->bands[band]->channels;
+ n = hw->wiphy->bands[band]->n_channels;
+ for (i = 0; i < n; i++) {
+ if (lkpi_scan_chan(&channels[i], ic, true))
+ nchan++;
+ }
}
}
#endif
@@ -4324,11 +4638,32 @@ sw_scan:
/* hw_req->req.wdev */
hw_req->req.wiphy = hw->wiphy;
hw_req->req.no_cck = false; /* XXX */
-#if 0
- /* This seems to pessimise default scanning behaviour. */
- hw_req->req.duration_mandatory = TICKS_2_USEC(ss->ss_mindwell);
- hw_req->req.duration = TICKS_2_USEC(ss->ss_maxdwell);
-#endif
+
+ /*
+ * In general setting duration[_mandatory] seems to pessimise
+ * default scanning behaviour. We only use it for BGSCANnig
+ * to keep the dwell times small.
+ * Setting duration_mandatory makes this the maximum dwell
+ * time (otherwise may be shorter). Duration is in TU.
+ */
+ if ((ic->ic_flags_ext & IEEE80211_FEXT_BGSCAN) != 0) {
+ unsigned long dwell;
+
+ if ((ic->ic_caps & IEEE80211_C_BGSCAN) == 0 ||
+ (vap->iv_flags & IEEE80211_F_BGSCAN) == 0)
+ ic_printf(ic, "BGSCAN despite off: %b, %b, %b\n",
+ ic->ic_flags_ext, IEEE80211_FEXT_BITS,
+ vap->iv_flags, IEEE80211_F_BITS,
+ ic->ic_caps, IEEE80211_C_BITS);
+
+ dwell = ss->ss_mindwell;
+ if (dwell == 0)
+ dwell = msecs_to_ticks(20);
+
+ hw_req->req.duration_mandatory = true;
+ hw_req->req.duration = TICKS_2_USEC(dwell) / 1024;
+ }
+
#ifdef __notyet__
hw_req->req.flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
memcpy(hw_req->req.mac_addr, xxx, IEEE80211_ADDR_LEN);
@@ -4339,16 +4674,16 @@ sw_scan:
hw_req->req.n_channels = nchan;
cpp = (struct linuxkpi_ieee80211_channel **)(hw_req + 1);
lc = (struct linuxkpi_ieee80211_channel *)(cpp + nchan);
+#ifdef LKPI_80211_USE_SCANLIST
for (i = 0; i < nchan; i++) {
*(cpp + i) =
(struct linuxkpi_ieee80211_channel *)(lc + i);
}
-#if 0 /* Avoid net80211 scan lists until it has proper scan offload support. */
+ /* Avoid net80211 scan lists until it has proper scan offload support. */
for (i = 0; i < nchan; i++) {
struct ieee80211_channel *c;
c = ss->ss_chans[ss->ss_next + i];
- lc->hw_value = c->ic_ieee;
lc->center_freq = c->ic_freq; /* XXX */
/* lc->flags */
lc->band = lkpi_net80211_chan_to_nl80211_band(c);
@@ -4357,7 +4692,9 @@ sw_scan:
lc++;
}
#else
- for (band = 0; band < NUM_NL80211_BANDS; band++) {
+ /* Add bands in reverse order for scanning. */
+ n = 0;
+ for (band = NUM_NL80211_BANDS - 1; band >= 0; band--) {
struct ieee80211_supported_band *supband;
struct linuxkpi_ieee80211_channel *channels;
@@ -4372,9 +4709,27 @@ sw_scan:
channels = supband->channels;
for (i = 0; i < supband->n_channels; i++) {
- *lc = channels[i];
- lc++;
+ if (lkpi_scan_chan(&channels[i], ic, false))
+ *(cpp + n++) = &channels[i];
+ }
+ }
+ if (lkpi_order_scanlist)
+ lkpi_scan_chan_list_resort(cpp, nchan);
+
+ if ((linuxkpi_debug_80211 & D80211_SCAN) != 0) {
+ printf("%s:%d: %s SCAN Channel List (nchan=%zu): ",
+ __func__, __LINE__, ic->ic_name, nchan);
+ for (i = 0; i < nchan; i++) {
+ struct linuxkpi_ieee80211_channel *xc;
+
+ xc = *(cpp + i);
+ printf(" %d(%d)",
+ ieee80211_mhz2ieee(xc->center_freq,
+ lkpi_nl80211_band_to_net80211_band(
+ xc->band)),
+ xc->center_freq);
}
+ printf("\n");
}
#endif
@@ -4404,6 +4759,7 @@ sw_scan:
ieend = lkpi_scan_ies_add(ie, &hw_req->ies, band_mask, vap, hw);
hw_req->req.ie = ie;
hw_req->req.ie_len = ieend - ie;
+ hw_req->req.scan_start = jiffies;
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
@@ -4421,11 +4777,30 @@ sw_scan:
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
if (running) {
free(hw_req, M_LKPI80211);
+ TRACE_SCAN(ic, "Trying to start new scan while still "
+ "running (2); cancelling new net80211 scan; "
+ "scan_flags %b",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+ ieee80211_cancel_scan(vap);
return;
}
+ lkpi_update_mcast_filter(ic);
+ TRACE_SCAN(ic, "Starting HW_SCAN: scan_flags %b, "
+ "ie_len %d, n_ssids %d, n_chan %d, common_ie_len %d [%d, %d]",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS, hw_req->req.ie_len,
+ hw_req->req.n_ssids, hw_req->req.n_channels,
+ hw_req->ies.common_ie_len,
+ hw_req->ies.len[NL80211_BAND_2GHZ],
+ hw_req->ies.len[NL80211_BAND_5GHZ]);
+
error = lkpi_80211_mo_hw_scan(hw, vif, hw_req);
if (error != 0) {
+ bool scan_done;
+ int e;
+
+ TRACE_SCAN(ic, "hw_scan failed; scan_flags %b, error %d",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS, error);
ieee80211_cancel_scan(vap);
/*
@@ -4442,13 +4817,35 @@ sw_scan:
* So we cannot rely on that behaviour and have to check
* and balance between both code paths.
*/
+ e = 0;
+ scan_done = true;
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;
+ /*
+ * The ieee80211_cancel_scan() above runs in a
+ * taskq and it may take ages for the previous
+ * scan to clear; starting a new one right away
+ * we run into the problem that the old one is
+ * still active.
+ */
+ e = msleep(lhw, &lhw->scan_mtx, 0, "lhwscanstop", hz);
+ scan_done = (lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) != 0;
+
+ /*
+ * Now we can clear running if no one else did.
+ */
lhw->scan_flags &= ~LKPI_LHW_SCAN_RUNNING;
}
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+ lkpi_update_mcast_filter(ic);
+ if (!scan_done) {
+ ic_printf(ic, "ERROR: %s: timeout/error to wait "
+ "for ieee80211_cancel_scan: %d\n", __func__, e);
+ return;
+ }
/*
* XXX-SIGH magic number.
@@ -4456,24 +4853,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?
+ * We need to put this into some defered context
+ * the net80211 scan may not be done yet
+ * (ic_flags & IEEE80211_F_SCAN) and we cannot
+ * wait here; if we do scan_curchan_task always
+ * runs after our timeout to finalize the scan.
*/
- vap->iv_flags_ext &= ~IEEE80211_FEXT_SCAN_OFFLOAD;
- ieee80211_start_scan(vap,
- IEEE80211_SCAN_ACTIVE |
- IEEE80211_SCAN_NOPICK |
- IEEE80211_SCAN_ONCE,
- IEEE80211_SCAN_FOREVER,
- ss->ss_mindwell ? ss->ss_mindwell : msecs_to_ticks(20),
- ss->ss_maxdwell ? ss->ss_maxdwell : msecs_to_ticks(200),
- vap->iv_des_nssid, vap->iv_des_ssid);
- goto sw_scan;
+ ieee80211_runtask(ic, &lvif->sw_scan_task);
+ return;
}
ic_printf(ic, "ERROR: %s: hw_scan returned %d\n",
@@ -4483,12 +4871,50 @@ sw_scan:
}
static void
+lkpi_sw_scan_task(void *arg, int pending __unused)
+{
+ struct lkpi_hw *lhw;
+ struct lkpi_vif *lvif;
+ struct ieee80211vap *vap;
+ struct ieee80211_scan_state *ss;
+
+ lvif = arg;
+ vap = LVIF_TO_VAP(lvif);
+ lhw = vap->iv_ic->ic_softc;
+ ss = vap->iv_ic->ic_scan;
+
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ /*
+ * We will re-enable this at scan_end calling lkpi_enable_hw_scan().
+ * IEEE80211_FEXT_SCAN_OFFLOAD will be cleared by lkpi_ic_scan_start.
+ */
+ lhw->scan_flags &= ~LKPI_LHW_SCAN_HW;
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
+
+ TRACE_SCAN(vap->iv_ic, "Triggering SW_SCAN: pending %d, scan_flags %b",
+ pending, lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
+ /*
+ * This will call ic_scan_start() and we will get into the right path
+ * unless other scans started in between.
+ */
+ ieee80211_start_scan(vap,
+ IEEE80211_SCAN_ONCE,
+ msecs_to_ticks(10000), /* 10000 ms (=~ 50 chan * 200 ms) */
+ ss->ss_mindwell ? ss->ss_mindwell : msecs_to_ticks(20),
+ ss->ss_maxdwell ? ss->ss_maxdwell : msecs_to_ticks(200),
+ vap->iv_des_nssid, vap->iv_des_ssid);
+}
+
+static void
lkpi_ic_scan_end(struct ieee80211com *ic)
{
struct lkpi_hw *lhw;
bool is_hw_scan;
lhw = ic->ic_softc;
+ TRACE_SCAN(ic, "scan_flags %b", lhw->scan_flags, LKPI_LHW_SCAN_BITS);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
if ((lhw->scan_flags & LKPI_LHW_SCAN_RUNNING) == 0) {
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -4517,6 +4943,16 @@ lkpi_ic_scan_end(struct ieee80211com *ic)
if (vap->iv_state == IEEE80211_S_SCAN)
lkpi_hw_conf_idle(hw, true);
}
+
+ /*
+ * In case we disabled the hw_scan in lkpi_ic_scan_start() and
+ * switched to swscan, re-enable hw_scan if available.
+ */
+ lkpi_enable_hw_scan(lhw);
+
+ LKPI_80211_LHW_SCAN_LOCK(lhw);
+ wakeup(lhw);
+ LKPI_80211_LHW_SCAN_UNLOCK(lhw);
}
static void
@@ -4527,6 +4963,10 @@ lkpi_ic_scan_curchan(struct ieee80211_scan_state *ss,
bool is_hw_scan;
lhw = ss->ss_ic->ic_softc;
+ TRACE_SCAN(ss->ss_ic, "scan_flags %b chan %d maxdwell %lu",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ ss->ss_ic->ic_curchan->ic_ieee, maxdwell);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -4541,6 +4981,10 @@ lkpi_ic_scan_mindwell(struct ieee80211_scan_state *ss)
bool is_hw_scan;
lhw = ss->ss_ic->ic_softc;
+ TRACE_SCAN(ss->ss_ic, "scan_flags %b chan %d mindwell %lu",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ ss->ss_ic->ic_curchan->ic_ieee, ss->ss_mindwell);
+
LKPI_80211_LHW_SCAN_LOCK(lhw);
is_hw_scan = (lhw->scan_flags & LKPI_LHW_SCAN_HW) != 0;
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
@@ -5571,6 +6015,12 @@ lkpi_ic_ampdu_rx_start(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap
return (-ENXIO);
}
+ if (lsta->state != IEEE80211_STA_AUTHORIZED) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p state %d not AUTHORIZED\n",
+ __func__, lsta, ni, vap, sta, lsta->state);
+ return (-ENXIO);
+ }
+
params.sta = sta;
params.action = IEEE80211_AMPDU_RX_START;
params.buf_size = _IEEE80211_MASKSHIFT(le16toh(baparamset), IEEE80211_BAPS_BUFSIZ);
@@ -5647,13 +6097,35 @@ lkpi_ic_ampdu_rx_stop(struct ieee80211_node *ni, struct ieee80211_rx_ampdu *rap)
lvif = VAP_TO_LVIF(vap);
vif = LVIF_TO_VIF(lvif);
lsta = ni->ni_drv_data;
+ if (lsta == NULL) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, lsta is NULL\n",
+ __func__, lsta, ni, vap);
+ goto net80211_only;
+ }
sta = LSTA_TO_STA(lsta);
+ if (!lsta->added_to_drv) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p not added to firmware\n",
+ __func__, lsta, ni, vap, sta);
+ goto net80211_only;
+ }
+
+ if (lsta->state != IEEE80211_STA_AUTHORIZED) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p state %d not AUTHORIZED\n",
+ __func__, lsta, ni, vap, sta, lsta->state);
+ goto net80211_only;
+ }
+
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;
}
+ if (tid == WME_NUM_TID) {
+ ic_printf(ic, "%s: lsta %p ni %p vap %p, sta %p TID not found\n",
+ __func__, lsta, ni, vap, sta);
+ goto net80211_only;
+ }
params.sta = sta;
params.action = IEEE80211_AMPDU_RX_STOP;
@@ -5788,8 +6260,9 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
cflags &= ~NET80211_CBW_FLAG_HT40;
error = ieee80211_add_channel_cbw(c, maxchan, n,
- channels[i].hw_value, channels[i].center_freq,
- channels[i].max_power,
+ ieee80211_mhz2ieee(channels[i].center_freq,
+ lkpi_nl80211_band_to_net80211_band(channels[i].band)),
+ channels[i].center_freq, channels[i].max_power,
nflags, bands, cflags);
/* net80211::ENOBUFS: *n >= maxchans */
if (error != 0 && error != ENOBUFS)
@@ -5860,8 +6333,9 @@ lkpi_ic_getradiocaps(struct ieee80211com *ic, int maxchan,
cflags &= ~NET80211_CBW_FLAG_HT40;
error = ieee80211_add_channel_cbw(c, maxchan, n,
- channels[i].hw_value, channels[i].center_freq,
- channels[i].max_power,
+ ieee80211_mhz2ieee(channels[i].center_freq,
+ lkpi_nl80211_band_to_net80211_band(channels[i].band)),
+ channels[i].center_freq, channels[i].max_power,
nflags, bands, cflags);
/* net80211::ENOBUFS: *n >= maxchans */
if (error != 0 && error != ENOBUFS)
@@ -5908,8 +6382,11 @@ linuxkpi_ieee80211_alloc_hw(size_t priv_len, const struct ieee80211_ops *ops)
LKPI_80211_LHW_SCAN_LOCK_INIT(lhw);
LKPI_80211_LHW_TXQ_LOCK_INIT(lhw);
+ spin_lock_init(&lhw->txq_lock);
sx_init_flags(&lhw->lvif_sx, "lhw-lvif", SX_RECURSE | SX_DUPOK);
+ LKPI_80211_LHW_MC_LOCK_INIT(lhw);
TAILQ_INIT(&lhw->lvif_head);
+ __hw_addr_init(&lhw->mc_list);
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
lhw->txq_generation[ac] = 1;
TAILQ_INIT(&lhw->scheduled_txqs[ac]);
@@ -6006,25 +6483,44 @@ linuxkpi_ieee80211_iffree(struct ieee80211_hw *hw)
}
}
+ LKPI_80211_LHW_MC_LOCK(lhw);
+ lkpi_cleanup_mcast_list_locked(lhw);
+ LKPI_80211_LHW_MC_UNLOCK(lhw);
+
/* Cleanup more of lhw here or in wiphy_free()? */
+ spin_lock_destroy(&lhw->txq_lock);
LKPI_80211_LHW_TXQ_LOCK_DESTROY(lhw);
LKPI_80211_LHW_SCAN_LOCK_DESTROY(lhw);
sx_destroy(&lhw->lvif_sx);
+ LKPI_80211_LHW_MC_LOCK_DESTROY(lhw)
IMPROVE();
}
void
-linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw, char *name)
+linuxkpi_set_ieee80211_dev(struct ieee80211_hw *hw)
{
struct lkpi_hw *lhw;
struct ieee80211com *ic;
+ struct device *dev;
lhw = HW_TO_LHW(hw);
ic = lhw->ic;
- /* Now set a proper name before ieee80211_ifattach(). */
+ /* Save the backpointer from net80211 to LinuxKPI. */
ic->ic_softc = lhw;
- ic->ic_name = name;
+
+ /*
+ * Set a proper name before ieee80211_ifattach() if dev is set.
+ * ath1xk also unset the dev so we need to check.
+ */
+ dev = wiphy_dev(hw->wiphy);
+ if (dev != NULL) {
+ ic->ic_name = dev_name(dev);
+ } else {
+ TODO("adjust arguments to still have the old dev or go through "
+ "the hoops of getting the bsddev from hw and detach; "
+ "or do in XXX; check ath1kx drivers");
+ }
/* XXX-BZ do we also need to set wiphy name? */
}
@@ -6101,21 +6597,13 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
IEEE80211_C_SHSLOT | /* short slot time supported */
IEEE80211_C_SHPREAMBLE /* short preamble supported */
;
-#if 0
- /* Scanning is a different kind of beast to re-work. */
- ic->ic_caps |= IEEE80211_C_BGSCAN;
+
+#ifdef LKPI_80211_BGSCAN
+ if (lhw->ops->hw_scan)
+ ic->ic_caps |= IEEE80211_C_BGSCAN;
#endif
- if (lhw->ops->hw_scan) {
- /*
- * Advertise full-offload scanning.
- *
- * Not limiting to SINGLE_SCAN_ON_ALL_BANDS here as otherwise
- * we essentially disable hw_scan for all drivers not setting
- * the flag.
- */
- ic->ic_flags_ext |= IEEE80211_FEXT_SCAN_OFFLOAD;
- lhw->scan_flags |= LKPI_LHW_SCAN_HW;
- }
+
+ lkpi_enable_hw_scan(lhw);
/* Does HW support Fragmentation offload? */
if (ieee80211_hw_check(hw, SUPPORTS_TX_FRAG))
@@ -6332,8 +6820,10 @@ linuxkpi_ieee80211_ifattach(struct ieee80211_hw *hw)
hw->wiphy->max_scan_ie_len -= lhw->scan_ie_len;
}
- if (bootverbose)
+ if (bootverbose) {
+ ic_printf(ic, "netdev_features %b\n", hw->netdev_features, NETIF_F_BITS);
ieee80211_announce(ic);
+ }
return (0);
err:
@@ -6570,13 +7060,19 @@ linuxkpi_ieee80211_scan_completed(struct ieee80211_hw *hw,
ic = lhw->ic;
ss = ic->ic_scan;
+ TRACE_SCAN(ic, "scan_flags %b info { %ju, %6D, aborted %d }",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ (uintmax_t)info->scan_start_tsf, info->tsf_bssid, ":",
+ info->aborted);
+
ieee80211_scan_done(ss->ss_vap);
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);
+ /* The wakeup(lhw) will be called from lkpi_ic_scan_end(). */
+ /* wakeup(lhw); */
LKPI_80211_LHW_SCAN_UNLOCK(lhw);
return;
@@ -6832,12 +7328,77 @@ lkpi_convert_rx_status(struct ieee80211_hw *hw, struct lkpi_sta *lsta,
rx_stats->c_pktflags |= IEEE80211_RX_F_FAIL_FCSCRC;
#endif
+ /* Fill in some sinfo bits to fill gaps not reported byt the driver. */
if (lsta != NULL) {
memcpy(&lsta->sinfo.rxrate, &rxrate, sizeof(rxrate));
lsta->sinfo.filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
+
+ if (rx_status->chains != 0) {
+ lsta->sinfo.chains = rx_status->chains;
+ memcpy(lsta->sinfo.chain_signal, rx_status->chain_signal,
+ sizeof(lsta->sinfo.chain_signal));
+ lsta->sinfo.filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
+ }
}
}
+#ifdef LINUXKPI_DEBUG_80211
+static void
+lkpi_rx_log_beacon(struct mbuf *m, struct lkpi_hw *lhw,
+ struct ieee80211_rx_status *rx_status)
+{
+ struct ieee80211_mgmt *f;
+ uint8_t *e;
+ char ssid[IEEE80211_NWID_LEN * 4 + 1];
+
+ memset(ssid, '\0', sizeof(ssid));
+
+ f = mtod(m, struct ieee80211_mgmt *);
+ e = f->u.beacon.variable;
+ /*
+ * Usually SSID is right after the fixed part and for debugging we will
+ * be fine should we miss it if it is not.
+ */
+ while ((e - (uint8_t *)f) < m->m_len) {
+ if (*e == IEEE80211_ELEMID_SSID)
+ break;
+ e += (2 + *(e + 1));
+ }
+ if (*e == IEEE80211_ELEMID_SSID) {
+ int i, len;
+ char *p;
+
+ p = ssid;
+ len = m->m_len - ((e + 2) - (uint8_t *)f);
+ if (len > *(e + 1))
+ len = *(e + 1);
+ e += 2;
+ for (i = 0; i < len; i++) {
+ /* Printable character? */
+ if (*e >= 0x20 && *e < 0x7f) {
+ *p++ = *e++;
+ } else {
+ snprintf(p, 5, "%#04x", *e++);
+ p += 4;
+ }
+ }
+ *p = '\0';
+ }
+
+ /* We print skb, skb->data, m as we are seeing 'ghost beacons'. */
+ TRACE_SCAN_BEACON(lhw->ic, "Beacon: scan_flags %b, band %s freq %u chan %-4d "
+ "len %d { %#06x %#06x %6D %6D %6D %#06x %ju %u %#06x SSID '%s' }",
+ lhw->scan_flags, LKPI_LHW_SCAN_BITS,
+ lkpi_nl80211_band_name(rx_status->band), rx_status->freq,
+ linuxkpi_ieee80211_frequency_to_channel(rx_status->freq, 0),
+ m->m_pkthdr.len, f->frame_control, f->duration_id,
+ f->da, ":", f->sa, ":", f->bssid, ":", f->seq_ctrl,
+ (uintmax_t)le64_to_cpu(f->u.beacon.timestamp),
+ le16_to_cpu(f->u.beacon.beacon_int),
+ le16_to_cpu(f->u.beacon.capab_info), ssid);
+}
+#endif
+
/* For %list see comment towards the end of the function. */
void
linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
@@ -6894,7 +7455,15 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
is_beacon = ieee80211_is_beacon(hdr->frame_control);
#ifdef LINUXKPI_DEBUG_80211
- if (is_beacon && (linuxkpi_debug_80211 & D80211_TRACE_RX_BEACONS) == 0)
+ /*
+ * We use the mbuf here as otherwise the variable part might
+ * be in skb frags.
+ */
+ if (is_beacon && ((linuxkpi_debug_80211 & D80211_SCAN_BEACON) != 0))
+ lkpi_rx_log_beacon(m, lhw, rx_status);
+
+ if (is_beacon && (linuxkpi_debug_80211 & D80211_TRACE_RX_BEACONS) == 0 &&
+ (linuxkpi_debug_80211 & D80211_SCAN_BEACON) == 0)
goto no_trace_beacons;
if (linuxkpi_debug_80211 & D80211_TRACE_RX)
@@ -6909,7 +7478,8 @@ linuxkpi_ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb,
hexdump(mtod(m, const void *), m->m_len, "RX (raw) ", 0);
/* Implement a dump_rxcb() !!! */
- if (linuxkpi_debug_80211 & D80211_TRACE_RX)
+ if ((linuxkpi_debug_80211 & D80211_TRACE_RX) != 0 ||
+ (linuxkpi_debug_80211 & D80211_SCAN_BEACON) != 0)
printf("TRACE-RX: %s: RXCB: %ju %ju %u, %b, %u, %#0x, %#0x, "
"%u band %u, %u { %d %d %d %d }, %d, %#x %#x %#x %#x %u %u %u\n",
__func__,
@@ -7791,8 +8361,11 @@ linuxkpi_ieee80211_connection_loss(struct ieee80211_vif *vif)
nstate = IEEE80211_S_INIT;
arg = 0; /* Not a valid reason. */
- ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s\n", __func__,
- vif, vap, ieee80211_state_name[vap->iv_state]);
+ ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s (synched %d, assoc %d "
+ "beacons %d dtim_period %d)\n", __func__, vif, vap,
+ ieee80211_state_name[vap->iv_state],
+ lvif->lvif_bss_synched, vif->cfg.assoc, lvif->beacons,
+ vif->bss_conf.dtim_period);
ieee80211_new_state(vap, nstate, arg);
}
@@ -7805,8 +8378,11 @@ 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]);
+ ic_printf(vap->iv_ic, "%s: vif %p vap %p state %s (synched %d, assoc %d "
+ "beacons %d dtim_period %d)\n", __func__, vif, vap,
+ ieee80211_state_name[vap->iv_state],
+ lvif->lvif_bss_synched, vif->cfg.assoc, lvif->beacons,
+ vif->bss_conf.dtim_period);
ieee80211_beacon_miss(vap->iv_ic);
}
@@ -7954,21 +8530,30 @@ lkpi_ieee80211_wake_queues_locked(struct ieee80211_hw *hw)
void
linuxkpi_ieee80211_wake_queues(struct ieee80211_hw *hw)
{
- wiphy_lock(hw->wiphy);
+ struct lkpi_hw *lhw;
+ unsigned long flags;
+
+ lhw = HW_TO_LHW(hw);
+
+ spin_lock_irqsave(&lhw->txq_lock, flags);
lkpi_ieee80211_wake_queues_locked(hw);
- wiphy_unlock(hw->wiphy);
+ spin_unlock_irqrestore(&lhw->txq_lock, flags);
}
void
linuxkpi_ieee80211_wake_queue(struct ieee80211_hw *hw, int qnum)
{
+ struct lkpi_hw *lhw;
+ unsigned long flags;
KASSERT(qnum < hw->queues, ("%s: qnum %d >= hw->queues %d, hw %p\n",
__func__, qnum, hw->queues, hw));
- wiphy_lock(hw->wiphy);
+ lhw = HW_TO_LHW(hw);
+
+ spin_lock_irqsave(&lhw->txq_lock, flags);
lkpi_ieee80211_wake_queues(hw, qnum);
- wiphy_unlock(hw->wiphy);
+ spin_unlock_irqrestore(&lhw->txq_lock, flags);
}
/* This is just hardware queues. */
diff --git a/sys/compat/linuxkpi/common/src/linux_80211.h b/sys/compat/linuxkpi/common/src/linux_80211.h
index 89afec1235bd..0dfcd7646c34 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211.h
+++ b/sys/compat/linuxkpi/common/src/linux_80211.h
@@ -59,6 +59,8 @@
#define D80211_IMPROVE_TXQ 0x00000004
#define D80211_TRACE 0x00000010
#define D80211_TRACEOK 0x00000020
+#define D80211_SCAN 0x00000040
+#define D80211_SCAN_BEACON 0x00000080
#define D80211_TRACE_TX 0x00000100
#define D80211_TRACE_TX_DUMP 0x00000200
#define D80211_TRACE_RX 0x00001000
@@ -75,6 +77,20 @@
#define D80211_TRACE_MODE_HE 0x04000000
#define D80211_TRACE_MODE_EHT 0x08000000
+#ifdef LINUXKPI_DEBUG_80211
+#define TRACE_SCAN(ic, fmt, ...) \
+ if (linuxkpi_debug_80211 & D80211_SCAN) \
+ printf("%s:%d: %s SCAN " fmt "\n", \
+ __func__, __LINE__, ic->ic_name, ##__VA_ARGS__)
+#define TRACE_SCAN_BEACON(ic, fmt, ...) \
+ if (linuxkpi_debug_80211 & D80211_SCAN_BEACON) \
+ printf("%s:%d: %s SCAN " fmt "\n", \
+ __func__, __LINE__, ic->ic_name, ##__VA_ARGS__)
+#else
+#define TRACE_SCAN(...) do {} while (0)
+#define TRACE_SCAN_BEACON(...) do {} while (0)
+#endif
+
#define IMPROVE_TXQ(...) \
if (linuxkpi_debug_80211 & D80211_IMPROVE_TXQ) \
printf("%s:%d: XXX LKPI80211 IMPROVE_TXQ\n", __func__, __LINE__)
@@ -187,6 +203,12 @@ struct lkpi_vif {
enum ieee80211_state, int);
struct ieee80211_node * (*iv_update_bss)(struct ieee80211vap *,
struct ieee80211_node *);
+ void (*iv_recv_mgmt)(struct ieee80211_node *,
+ struct mbuf *, int,
+ const struct ieee80211_rx_stats *,
+ int, int);
+ struct task sw_scan_task;
+
struct list_head lsta_list;
struct lkpi_sta *lvif_bss;
@@ -194,6 +216,7 @@ struct lkpi_vif {
struct ieee80211_node *key_update_iv_bss;
int ic_unlocked; /* Count of ic unlocks pending (*mo_set_key) */
int nt_unlocked; /* Count of nt unlocks pending (*mo_set_key) */
+ int beacons; /* # of beacons since assoc */
bool lvif_bss_synched;
bool added_to_drv; /* Driver knows; i.e. we called add_interface(). */
@@ -223,10 +246,14 @@ struct lkpi_hw { /* name it mac80211_sc? */
struct sx lvif_sx;
struct list_head lchanctx_list;
+ struct netdev_hw_addr_list mc_list;
+ unsigned int mc_flags;
+ struct sx mc_sx;
struct mtx txq_mtx;
uint32_t txq_generation[IEEE80211_NUM_ACS];
TAILQ_HEAD(, lkpi_txq) scheduled_txqs[IEEE80211_NUM_ACS];
+ spinlock_t txq_lock;
/* Deferred RX path. */
struct task rxq_task;
@@ -279,7 +306,7 @@ struct lkpi_hw { /* name it mac80211_sc? */
int max_rates; /* Maximum number of bitrates supported in any channel. */
int scan_ie_len; /* Length of common per-band scan IEs. */
- bool update_mc;
+ bool mc_all_multi;
bool update_wme;
bool rxq_stopped;
@@ -289,6 +316,9 @@ struct lkpi_hw { /* name it mac80211_sc? */
#define LHW_TO_HW(_lhw) (&(_lhw)->hw)
#define HW_TO_LHW(_hw) container_of(_hw, struct lkpi_hw, hw)
+#define LKPI_LHW_SCAN_BITS \
+ "\010\1RUNING\2HW"
+
struct lkpi_chanctx {
struct list_head entry;
@@ -369,6 +399,13 @@ struct lkpi_wiphy {
#define LKPI_80211_LHW_LVIF_LOCK(_lhw) sx_xlock(&(_lhw)->lvif_sx)
#define LKPI_80211_LHW_LVIF_UNLOCK(_lhw) sx_xunlock(&(_lhw)->lvif_sx)
+#define LKPI_80211_LHW_MC_LOCK_INIT(_lhw) \
+ sx_init_flags(&lhw->mc_sx, "lhw-mc", 0);
+#define LKPI_80211_LHW_MC_LOCK_DESTROY(_lhw) \
+ sx_destroy(&lhw->mc_sx);
+#define LKPI_80211_LHW_MC_LOCK(_lhw) sx_xlock(&(_lhw)->mc_sx)
+#define LKPI_80211_LHW_MC_UNLOCK(_lhw) sx_xunlock(&(_lhw)->mc_sx)
+
#define LKPI_80211_LVIF_LOCK(_lvif) mtx_lock(&(_lvif)->mtx)
#define LKPI_80211_LVIF_UNLOCK(_lvif) mtx_unlock(&(_lvif)->mtx)
diff --git a/sys/compat/linuxkpi/common/src/linux_80211_macops.c b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
index 78b2120f2d8c..1046b753574f 100644
--- a/sys/compat/linuxkpi/common/src/linux_80211_macops.c
+++ b/sys/compat/linuxkpi/common/src/linux_80211_macops.c
@@ -53,6 +53,8 @@ lkpi_80211_mo_start(struct ieee80211_hw *hw)
struct lkpi_hw *lhw;
int error;
+ lockdep_assert_wiphy(hw->wiphy);
+
lhw = HW_TO_LHW(hw);
if (lhw->ops->start == NULL) {
error = EOPNOTSUPP;