mirror of
https://github.com/Telecominfraproject/wlan-ap.git
synced 2025-12-20 10:51:27 +00:00
913 lines
28 KiB
Diff
913 lines
28 KiB
Diff
From f09803a38e857400355e005681b68a7303202303 Mon Sep 17 00:00:00 2001
|
|
From: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
|
|
Date: Sat, 25 Mar 2023 22:53:27 +0530
|
|
Subject: [PATCH 1/2] mac80211: add mlo monitor support
|
|
|
|
Add support to capture packets from multiple channels concurrently,
|
|
this will be capturing all the packets in a single netdev
|
|
|
|
To capture multiple channels in a single netdev execute following cmd
|
|
iw phy0 interface add mon0 type monitor
|
|
|
|
Either the channel can be set before IFUP or default channel will be
|
|
configured.
|
|
|
|
The below command helps to add multiple channels to monitor interface
|
|
iw dev mon0 add channel 11
|
|
iw dev mon0 add channel 36
|
|
iw dev mon0 add channel 49 6G
|
|
|
|
To stop capturing on a channel in mlo mode,
|
|
|
|
Channel can be deleted from the monitor interface using below command
|
|
iw dev mon0 del channel 36
|
|
|
|
Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
|
|
---
|
|
net/mac80211/cfg.c | 169 ++++++++++++++++++++++++++++++-------
|
|
net/mac80211/chan.c | 22 ++++-
|
|
net/mac80211/ieee80211_i.h | 5 +-
|
|
net/mac80211/iface.c | 158 +++++++++++++++++++++-------------
|
|
net/mac80211/main.c | 88 +++++++++++++------
|
|
net/mac80211/rx.c | 16 +++-
|
|
net/mac80211/tx.c | 9 +-
|
|
net/mac80211/util.c | 55 +++++++-----
|
|
8 files changed, 372 insertions(+), 150 deletions(-)
|
|
|
|
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
|
|
index 6caf3a7a3f06..bfc8f2110c86 100644
|
|
--- a/net/mac80211/cfg.c
|
|
+++ b/net/mac80211/cfg.c
|
|
@@ -86,6 +86,7 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
|
|
{
|
|
struct ieee80211_local *local = sdata->local;
|
|
struct ieee80211_sub_if_data *monitor_sdata;
|
|
+ int i;
|
|
|
|
/* check flags first */
|
|
if (params->flags && ieee80211_sdata_running(sdata)) {
|
|
@@ -104,18 +105,21 @@ static int ieee80211_set_mon_options(struct ieee80211_sub_if_data *sdata,
|
|
return -EBUSY;
|
|
}
|
|
|
|
- /* also validate MU-MIMO change */
|
|
- monitor_sdata = wiphy_dereference(local->hw.wiphy,
|
|
- local->monitor_sdata);
|
|
+ i = 0;
|
|
+ do {
|
|
+ /* also validate MU-MIMO change */
|
|
+ monitor_sdata = wiphy_dereference(local->hw.wiphy,
|
|
+ local->monitor_sdata[i]);
|
|
|
|
- if (!monitor_sdata &&
|
|
- (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
|
|
- return -EOPNOTSUPP;
|
|
+ if (!monitor_sdata &&
|
|
+ (params->vht_mumimo_groups || params->vht_mumimo_follow_addr))
|
|
+ return -EOPNOTSUPP;
|
|
|
|
- /* apply all changes now - no failures allowed */
|
|
+ /* apply all changes now - no failures allowed */
|
|
|
|
- if (monitor_sdata)
|
|
- ieee80211_set_mu_mimo_follow(monitor_sdata, params);
|
|
+ if (monitor_sdata)
|
|
+ ieee80211_set_mu_mimo_follow(monitor_sdata, params);
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
|
|
if (params->flags) {
|
|
if (ieee80211_sdata_running(sdata)) {
|
|
@@ -917,15 +921,66 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
|
|
{
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
- int ret = 0;
|
|
+ int ret = 0, i, def_idx = 0, hw_idx;
|
|
|
|
- if (cfg80211_chandef_identical(&local->monitor_chandef, chandef))
|
|
+ if (wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chandef->chan);
|
|
+ if (hw_idx >= 0)
|
|
+ def_idx = hw_idx;
|
|
+ } else if (cfg80211_chandef_identical(&local->monitor_chandef[def_idx], chandef))
|
|
return 0;
|
|
|
|
mutex_lock(&local->mtx);
|
|
if (local->use_chanctx) {
|
|
+ /* Clear all chanctx set only the user chandef */
|
|
+ i = 0;
|
|
+ do {
|
|
+ sdata = wiphy_dereference(local->hw.wiphy,
|
|
+ local->monitor_sdata[i]);
|
|
+ if (sdata)
|
|
+ ieee80211_link_release_channel(&sdata->deflink);
|
|
+ memset(&local->monitor_chandef[i], 0, sizeof(struct cfg80211_chan_def));
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
sdata = wiphy_dereference(local->hw.wiphy,
|
|
- local->monitor_sdata);
|
|
+ local->monitor_sdata[def_idx]);
|
|
+ if (sdata)
|
|
+ ret = ieee80211_link_use_channel(&sdata->deflink,
|
|
+ chandef,
|
|
+ IEEE80211_CHANCTX_EXCLUSIVE);
|
|
+ } else if (local->open_count == local->monitors) {
|
|
+ local->_oper_chandef = *chandef;
|
|
+ ieee80211_hw_config(local, 0);
|
|
+ }
|
|
+
|
|
+ if (ret == 0)
|
|
+ local->monitor_chandef[def_idx] = *chandef;
|
|
+ mutex_unlock(&local->mtx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int ieee80211_add_monitor_channel(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
+ struct ieee80211_sub_if_data *sdata;
|
|
+ int ret = 0, hw_idx = 0;
|
|
+
|
|
+ if (wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chandef->chan);
|
|
+ if (hw_idx < 0)
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ return -ENOTSUPP;
|
|
+ }
|
|
+
|
|
+ if (cfg80211_chandef_identical(&local->monitor_chandef[hw_idx], chandef))
|
|
+ return 0;
|
|
+
|
|
+ mutex_lock(&local->mtx);
|
|
+ if (local->use_chanctx) {
|
|
+ sdata = wiphy_dereference(local->hw.wiphy,
|
|
+ local->monitor_sdata[hw_idx]);
|
|
if (sdata) {
|
|
ieee80211_link_release_channel(&sdata->deflink);
|
|
ret = ieee80211_link_use_channel(&sdata->deflink,
|
|
@@ -938,12 +993,49 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
|
|
}
|
|
|
|
if (ret == 0)
|
|
- local->monitor_chandef = *chandef;
|
|
+ local->monitor_chandef[hw_idx] = *chandef;
|
|
mutex_unlock(&local->mtx);
|
|
|
|
return ret;
|
|
}
|
|
|
|
+static int ieee80211_del_monitor_channel(struct wiphy *wiphy,
|
|
+ struct cfg80211_chan_def *chandef)
|
|
+{
|
|
+ struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
+ struct ieee80211_sub_if_data *sdata;
|
|
+ int hw_idx = 0;
|
|
+
|
|
+ if (wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chandef->chan);
|
|
+ if (hw_idx < 0)
|
|
+ return -EINVAL;
|
|
+ } else {
|
|
+ return -ENOTSUPP;
|
|
+ }
|
|
+
|
|
+ /* check only channel and delete it, unlike
|
|
+ * add channel checks for chandef
|
|
+ */
|
|
+ if (!cfg80211_channel_identical(&local->monitor_chandef[hw_idx].chan, chandef->chan))
|
|
+ return -EINVAL;
|
|
+
|
|
+ mutex_lock(&local->mtx);
|
|
+ if (local->use_chanctx) {
|
|
+ sdata = wiphy_dereference(local->hw.wiphy,
|
|
+ local->monitor_sdata[hw_idx]);
|
|
+ if (sdata) {
|
|
+ ieee80211_link_release_channel(&sdata->deflink);
|
|
+ memset(&local->monitor_chandef[hw_idx], 0, sizeof(struct cfg80211_chan_def));
|
|
+ mutex_unlock(&local->mtx);
|
|
+ return 0;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&local->mtx);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
static int
|
|
ieee80211_set_probe_resp(struct ieee80211_sub_if_data *sdata,
|
|
const u8 *resp, size_t resp_len,
|
|
@@ -3112,6 +3204,7 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
|
|
bool update_txp_type = false;
|
|
bool has_monitor = false;
|
|
struct ieee80211_link_data *link_data;
|
|
+ int i, hw_idx = 0;
|
|
|
|
if (wdev) {
|
|
sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
|
|
@@ -3121,8 +3214,14 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
|
|
return -ENOLINK;
|
|
|
|
if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
|
|
+ if (wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(wiphy, link_data->conf->chandef.chan);
|
|
+ if (hw_idx < 0)
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
sdata = wiphy_dereference(local->hw.wiphy,
|
|
- local->monitor_sdata);
|
|
+ local->monitor_sdata[hw_idx]);
|
|
if (!sdata)
|
|
return -EOPNOTSUPP;
|
|
}
|
|
@@ -3164,7 +3263,6 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
|
|
break;
|
|
}
|
|
|
|
- /*TODO: monitor mode for MLO */
|
|
mutex_lock(&local->iflist_mtx);
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
|
if (sdata->vif.type == NL80211_IFTYPE_MONITOR) {
|
|
@@ -3184,16 +3282,19 @@ static int ieee80211_set_tx_power(struct wiphy *wiphy,
|
|
mutex_unlock(&local->iflist_mtx);
|
|
|
|
if (has_monitor) {
|
|
- sdata = wiphy_dereference(local->hw.wiphy,
|
|
- local->monitor_sdata);
|
|
- if (sdata) {
|
|
- sdata->deflink.user_power_level = local->user_power_level;
|
|
- if (txp_type != sdata->vif.bss_conf.txpower_type)
|
|
- update_txp_type = true;
|
|
- sdata->vif.bss_conf.txpower_type = txp_type;
|
|
-
|
|
- ieee80211_recalc_txpower(sdata, update_txp_type, 0);
|
|
- }
|
|
+ i = 0;
|
|
+ do {
|
|
+ sdata = wiphy_dereference(local->hw.wiphy,
|
|
+ local->monitor_sdata[i]);
|
|
+ if (sdata) {
|
|
+ sdata->deflink.user_power_level = local->user_power_level;
|
|
+ if (txp_type != sdata->vif.bss_conf.txpower_type)
|
|
+ update_txp_type = true;
|
|
+ sdata->vif.bss_conf.txpower_type = txp_type;
|
|
+
|
|
+ ieee80211_recalc_txpower(sdata, update_txp_type, link_id);
|
|
+ }
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
}
|
|
|
|
return 0;
|
|
@@ -4402,7 +4503,7 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
|
struct ieee80211_local *local = wiphy_priv(wiphy);
|
|
struct ieee80211_chanctx_conf *chanctx_conf;
|
|
struct ieee80211_link_data *link;
|
|
- int ret = -ENODATA;
|
|
+ int ret = -ENODATA, i;
|
|
|
|
rcu_read_lock();
|
|
link = rcu_dereference(sdata->link[link_id]);
|
|
@@ -4418,10 +4519,18 @@ static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
|
|
} else if (local->open_count > 0 &&
|
|
local->open_count == local->monitors &&
|
|
sdata->vif.type == NL80211_IFTYPE_MONITOR) {
|
|
- if (local->use_chanctx)
|
|
- *chandef = local->monitor_chandef;
|
|
- else
|
|
+ if (local->use_chanctx) {
|
|
+ i = 0;
|
|
+ do {
|
|
+ if (local->monitor_chandef[i].chan) {
|
|
+ /* Get a valid chan and return */
|
|
+ *chandef = local->monitor_chandef[i];
|
|
+ break;
|
|
+ }
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
+ } else {
|
|
*chandef = local->_oper_chandef;
|
|
+ }
|
|
ret = 0;
|
|
}
|
|
out:
|
|
@@ -5316,6 +5425,8 @@ const struct cfg80211_ops mac80211_config_ops = {
|
|
.change_bss = ieee80211_change_bss,
|
|
.set_txq_params = ieee80211_set_txq_params,
|
|
.set_monitor_channel = ieee80211_set_monitor_channel,
|
|
+ .add_monitor_channel = ieee80211_add_monitor_channel,
|
|
+ .del_monitor_channel = ieee80211_del_monitor_channel,
|
|
.suspend = ieee80211_suspend,
|
|
.resume = ieee80211_resume,
|
|
.scan = ieee80211_scan,
|
|
diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c
|
|
index 7d2631354a92..2354d38c1228 100644
|
|
--- a/net/mac80211/chan.c
|
|
+++ b/net/mac80211/chan.c
|
|
@@ -337,6 +337,7 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
enum nl80211_chan_width max_bw = NL80211_CHAN_WIDTH_20_NOHT;
|
|
+ int hw_idx = 0;
|
|
|
|
rcu_read_lock();
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
@@ -350,8 +351,16 @@ ieee80211_get_chanctx_max_required_bw(struct ieee80211_local *local,
|
|
max_bw = max(max_bw, width);
|
|
}
|
|
|
|
+ if (local->hw.wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, conf->def.chan);
|
|
+ if (hw_idx < 0) {
|
|
+ rcu_read_unlock();
|
|
+ return max_bw;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* use the configured bandwidth in case of monitor interface */
|
|
- sdata = rcu_dereference(local->monitor_sdata);
|
|
+ sdata = rcu_dereference(local->monitor_sdata[hw_idx]);
|
|
if (sdata &&
|
|
rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == conf)
|
|
max_bw = max(max_bw, conf->def.width);
|
|
@@ -927,6 +936,7 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
u8 rx_chains_static, rx_chains_dynamic;
|
|
+ int hw_idx = 0;
|
|
|
|
lockdep_assert_held(&local->chanctx_mtx);
|
|
|
|
@@ -990,8 +1000,16 @@ void ieee80211_recalc_smps_chanctx(struct ieee80211_local *local,
|
|
}
|
|
}
|
|
|
|
+ if (local->hw.wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, chanctx->conf.def.chan);
|
|
+ if (hw_idx < 0) {
|
|
+ rcu_read_unlock();
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* Disable SMPS for the monitor interface */
|
|
- sdata = rcu_dereference(local->monitor_sdata);
|
|
+ sdata = rcu_dereference(local->monitor_sdata[hw_idx]);
|
|
if (sdata &&
|
|
rcu_access_pointer(sdata->vif.bss_conf.chanctx_conf) == &chanctx->conf)
|
|
rx_chains_dynamic = rx_chains_static = local->rx_chains;
|
|
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
|
|
index c525789f808a..1400946c8e08 100644
|
|
--- a/net/mac80211/ieee80211_i.h
|
|
+++ b/net/mac80211/ieee80211_i.h
|
|
@@ -1588,8 +1588,9 @@ struct ieee80211_local {
|
|
struct ieee80211_sub_if_data __rcu *p2p_sdata;
|
|
|
|
/* virtual monitor interface */
|
|
- struct ieee80211_sub_if_data __rcu *monitor_sdata;
|
|
- struct cfg80211_chan_def monitor_chandef;
|
|
+ struct ieee80211_sub_if_data __rcu **monitor_sdata;
|
|
+ struct cfg80211_chan_def *monitor_chandef;
|
|
+ bool monitor_sdata_created;
|
|
|
|
struct mac80211_memory_stats memory_stats;
|
|
struct work_struct awgn_detected_work;
|
|
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
|
|
index 90fad651777d..1a342fb5a154 100644
|
|
--- a/net/mac80211/iface.c
|
|
+++ b/net/mac80211/iface.c
|
|
@@ -400,7 +400,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
|
|
unsigned long flags;
|
|
struct sk_buff *skb, *tmp;
|
|
u32 hw_reconf_flags = 0;
|
|
- int i, flushed;
|
|
+ int i, flushed, hw_idx = 0;
|
|
struct ps_data *ps;
|
|
struct cfg80211_chan_def chandef;
|
|
bool cancel_scan;
|
|
@@ -623,7 +623,14 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
|
|
*/
|
|
if (local->suspended) {
|
|
WARN_ON(local->wowlan);
|
|
- WARN_ON(rcu_access_pointer(local->monitor_sdata));
|
|
+
|
|
+ if (local->hw.wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, sdata->deflink.conf->chandef.chan);
|
|
+ if (hw_idx < 0)
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ WARN_ON(rcu_access_pointer(local->monitor_sdata[hw_idx]));
|
|
return;
|
|
}
|
|
|
|
@@ -1065,7 +1072,7 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
|
|
int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
- int ret;
|
|
+ int ret, i = 0;
|
|
|
|
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
|
|
return 0;
|
|
@@ -1073,99 +1080,128 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
|
|
ASSERT_RTNL();
|
|
lockdep_assert_wiphy(local->hw.wiphy);
|
|
|
|
- if (local->monitor_sdata)
|
|
+ if (local->monitor_sdata_created)
|
|
return 0;
|
|
|
|
- sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
|
|
- if (!sdata)
|
|
- return -ENOMEM;
|
|
+ do {
|
|
+ mutex_lock(&local->mtx);
|
|
+ if (!local->monitor_chandef[i].chan) {
|
|
+ if (i < local->hw.wiphy->num_hw) {
|
|
+ mutex_unlock(&local->mtx);
|
|
+ continue;
|
|
+ }
|
|
+ }
|
|
+ mutex_unlock(&local->mtx);
|
|
|
|
- /* set up data */
|
|
- sdata->vif.type = NL80211_IFTYPE_MONITOR;
|
|
- snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
|
|
- wiphy_name(local->hw.wiphy));
|
|
- sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
|
|
+ sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
|
|
+ if (!sdata)
|
|
+ return -ENOMEM;
|
|
|
|
- ieee80211_sdata_init(local, sdata);
|
|
+ /* set up data */
|
|
+ sdata->vif.type = NL80211_IFTYPE_MONITOR;
|
|
+ snprintf(sdata->name, IFNAMSIZ, "%s-monitor",
|
|
+ wiphy_name(local->hw.wiphy));
|
|
+ sdata->wdev.iftype = NL80211_IFTYPE_MONITOR;
|
|
|
|
- ieee80211_set_default_queues(sdata);
|
|
+ ieee80211_sdata_init(local, sdata);
|
|
|
|
- ret = drv_add_interface(local, sdata);
|
|
- if (WARN_ON(ret)) {
|
|
- /* ok .. stupid driver, it asked for this! */
|
|
- kfree(sdata);
|
|
- return ret;
|
|
- }
|
|
+ ieee80211_set_default_queues(sdata);
|
|
|
|
- set_bit(SDATA_STATE_RUNNING, &sdata->state);
|
|
+ ret = drv_add_interface(local, sdata);
|
|
+ if (WARN_ON(ret))
|
|
+ /* ok .. stupid driver, it asked for this! */
|
|
+ goto free;
|
|
|
|
- ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
|
|
- if (ret) {
|
|
- kfree(sdata);
|
|
- return ret;
|
|
- }
|
|
+ set_bit(SDATA_STATE_RUNNING, &sdata->state);
|
|
|
|
- mutex_lock(&local->iflist_mtx);
|
|
- rcu_assign_pointer(local->monitor_sdata, sdata);
|
|
- mutex_unlock(&local->iflist_mtx);
|
|
+ ret = ieee80211_check_queues(sdata, NL80211_IFTYPE_MONITOR);
|
|
+ if (ret)
|
|
+ goto free;
|
|
|
|
- mutex_lock(&local->mtx);
|
|
- ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef,
|
|
- IEEE80211_CHANCTX_EXCLUSIVE);
|
|
- mutex_unlock(&local->mtx);
|
|
- if (ret) {
|
|
mutex_lock(&local->iflist_mtx);
|
|
- RCU_INIT_POINTER(local->monitor_sdata, NULL);
|
|
+ rcu_assign_pointer(local->monitor_sdata[i], sdata);
|
|
+ local->monitor_sdata_created = true;
|
|
mutex_unlock(&local->iflist_mtx);
|
|
- synchronize_net();
|
|
- drv_remove_interface(local, sdata);
|
|
- kfree(sdata);
|
|
- return ret;
|
|
- }
|
|
|
|
- atomic_add(sizeof(*sdata) + local->hw.vif_data_size,
|
|
- &local->memory_stats.malloc_size);
|
|
+ mutex_lock(&local->mtx);
|
|
+ ret = ieee80211_link_use_channel(&sdata->deflink, &local->monitor_chandef[i],
|
|
+ IEEE80211_CHANCTX_EXCLUSIVE);
|
|
+ mutex_unlock(&local->mtx);
|
|
+ if (ret)
|
|
+ goto free;
|
|
|
|
- skb_queue_head_init(&sdata->skb_queue);
|
|
- skb_queue_head_init(&sdata->status_queue);
|
|
- INIT_WORK(&sdata->work, ieee80211_iface_work);
|
|
+ atomic_add(sizeof(*sdata) + local->hw.vif_data_size,
|
|
+ &local->memory_stats.malloc_size);
|
|
+
|
|
+ skb_queue_head_init(&sdata->skb_queue);
|
|
+ skb_queue_head_init(&sdata->status_queue);
|
|
+ INIT_WORK(&sdata->work, ieee80211_iface_work);
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
|
|
return 0;
|
|
+
|
|
+free:
|
|
+ mutex_lock(&local->iflist_mtx);
|
|
+ do {
|
|
+ RCU_INIT_POINTER(local->monitor_sdata[i], NULL);
|
|
+ synchronize_net();
|
|
+
|
|
+
|
|
+ sdata = rcu_dereference_protected(local->monitor_sdata[i],
|
|
+ lockdep_is_held(&local->iflist_mtx));
|
|
+ if (!sdata)
|
|
+ continue;
|
|
+
|
|
+ drv_remove_interface(local, sdata);
|
|
+ kfree(sdata);
|
|
+ } while (--i >= 0);
|
|
+ mutex_unlock(&local->iflist_mtx);
|
|
+ local->monitor_sdata_created = false;
|
|
+ return ret;
|
|
}
|
|
|
|
void ieee80211_del_virtual_monitor(struct ieee80211_local *local)
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
+ int i;
|
|
|
|
if (!ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF))
|
|
return;
|
|
|
|
+ if (!local->monitor_sdata_created)
|
|
+ return;
|
|
+
|
|
ASSERT_RTNL();
|
|
lockdep_assert_wiphy(local->hw.wiphy);
|
|
|
|
- mutex_lock(&local->iflist_mtx);
|
|
|
|
- sdata = rcu_dereference_protected(local->monitor_sdata,
|
|
- lockdep_is_held(&local->iflist_mtx));
|
|
- if (!sdata) {
|
|
+ i = 0;
|
|
+ do {
|
|
+ mutex_lock(&local->iflist_mtx);
|
|
+ sdata = rcu_dereference_protected(local->monitor_sdata[i],
|
|
+ lockdep_is_held(&local->iflist_mtx));
|
|
+ if (!sdata) {
|
|
+ mutex_unlock(&local->iflist_mtx);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ RCU_INIT_POINTER(local->monitor_sdata[i], NULL);
|
|
mutex_unlock(&local->iflist_mtx);
|
|
- return;
|
|
- }
|
|
|
|
- RCU_INIT_POINTER(local->monitor_sdata, NULL);
|
|
- mutex_unlock(&local->iflist_mtx);
|
|
+ synchronize_net();
|
|
|
|
- synchronize_net();
|
|
+ mutex_lock(&local->mtx);
|
|
+ ieee80211_link_release_channel(&sdata->deflink);
|
|
+ mutex_unlock(&local->mtx);
|
|
|
|
- mutex_lock(&local->mtx);
|
|
- ieee80211_link_release_channel(&sdata->deflink);
|
|
- mutex_unlock(&local->mtx);
|
|
+ drv_remove_interface(local, sdata);
|
|
|
|
- drv_remove_interface(local, sdata);
|
|
+ atomic_sub(sizeof(*sdata) + local->hw.vif_data_size,
|
|
+ &local->memory_stats.malloc_size);
|
|
+ kfree(sdata);
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
|
|
- atomic_sub(sizeof(*sdata) + local->hw.vif_data_size,
|
|
- &local->memory_stats.malloc_size);
|
|
- kfree(sdata);
|
|
+ local->monitor_sdata_created = false;
|
|
}
|
|
|
|
#ifdef CPTCFG_MAC80211_NSS_SUPPORT
|
|
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
|
|
index bf8802fe60f1..8a12504fbfce 100644
|
|
--- a/net/mac80211/main.c
|
|
+++ b/net/mac80211/main.c
|
|
@@ -987,7 +987,8 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
enum nl80211_band band;
|
|
int channels, max_bitrates;
|
|
bool supp_ht, supp_vht, supp_he, supp_eht;
|
|
- struct cfg80211_chan_def dflt_chandef = {};
|
|
+ size_t size = 0;
|
|
+ int hw_idx = 0, num_hw;
|
|
|
|
if (ieee80211_hw_check(hw, QUEUE_CONTROL) &&
|
|
(local->hw.offchannel_tx_hw_queue == IEEE80211_INVAL_HW_QUEUE ||
|
|
@@ -1133,35 +1134,56 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
supp_vht = false;
|
|
supp_he = false;
|
|
supp_eht = false;
|
|
+
|
|
+ if (local->hw.wiphy->num_hw)
|
|
+ num_hw = local->hw.wiphy->num_hw;
|
|
+ else
|
|
+ num_hw = 1;
|
|
+ size = sizeof(*local->monitor_chandef) * num_hw;
|
|
+ local->monitor_chandef = kzalloc(size, GFP_KERNEL);
|
|
+ if (!local->monitor_chandef)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ size = sizeof(**local->monitor_sdata) * num_hw;
|
|
+ local->monitor_sdata = kzalloc(size, GFP_KERNEL);
|
|
+ if (!local->monitor_sdata) {
|
|
+ kfree(local->monitor_chandef);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
for (band = 0; band < NUM_NL80211_BANDS; band++) {
|
|
struct ieee80211_supported_band *sband;
|
|
+ struct cfg80211_chan_def dflt_chandef = {};
|
|
|
|
sband = local->hw.wiphy->bands[band];
|
|
if (!sband)
|
|
continue;
|
|
|
|
- if (!dflt_chandef.chan) {
|
|
- /*
|
|
- * Assign the first enabled channel to dflt_chandef
|
|
- * from the list of channels
|
|
- */
|
|
- for (i = 0; i < sband->n_channels; i++)
|
|
- if (!(sband->channels[i].flags &
|
|
+ /*
|
|
+ * Assign the first enabled channel to dflt_chandef
|
|
+ * from the list of channels
|
|
+ */
|
|
+ for (i = 0; i < sband->n_channels; i++)
|
|
+ if (!(sband->channels[i].flags &
|
|
IEEE80211_CHAN_DISABLED))
|
|
- break;
|
|
- /* if none found then use the first anyway */
|
|
- if (i == sband->n_channels)
|
|
- i = 0;
|
|
- cfg80211_chandef_create(&dflt_chandef,
|
|
- &sband->channels[i],
|
|
- NL80211_CHAN_NO_HT);
|
|
- /* init channel we're on */
|
|
- if (!local->use_chanctx && !local->_oper_chandef.chan) {
|
|
- local->hw.conf.chandef = dflt_chandef;
|
|
- local->_oper_chandef = dflt_chandef;
|
|
- }
|
|
- local->monitor_chandef = dflt_chandef;
|
|
+ break;
|
|
+ /* if none found then use the first anyway */
|
|
+ if (i == sband->n_channels)
|
|
+ i = 0;
|
|
+ cfg80211_chandef_create(&dflt_chandef,
|
|
+ &sband->channels[i],
|
|
+ NL80211_CHAN_NO_HT);
|
|
+ /* init channel we're on */
|
|
+ if (!local->use_chanctx && !local->_oper_chandef.chan) {
|
|
+ local->hw.conf.chandef = dflt_chandef;
|
|
+ local->_oper_chandef = dflt_chandef;
|
|
}
|
|
+ hw_idx = cfg80211_get_hw_idx_by_chan(local->hw.wiphy, dflt_chandef.chan);
|
|
+ if (hw_idx < 0)
|
|
+ /* Resetting to use first index for non-MLO case */
|
|
+ hw_idx = 0;
|
|
+
|
|
+ local->monitor_chandef[hw_idx] = dflt_chandef;
|
|
|
|
channels += sband->n_channels;
|
|
|
|
@@ -1181,12 +1203,16 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
|
|
/* HT, VHT, HE, EHT require QoS, thus >= 4 queues */
|
|
if (WARN_ON(local->hw.queues < IEEE80211_NUM_ACS &&
|
|
- (supp_ht || supp_vht || supp_he || supp_eht)))
|
|
- return -EINVAL;
|
|
+ (supp_ht || supp_vht || supp_he || supp_eht))) {
|
|
+ result = -EINVAL;
|
|
+ goto fail_monitor_chan;
|
|
+ }
|
|
|
|
/* EHT requires HE support */
|
|
- if (WARN_ON(supp_eht && !supp_he))
|
|
- return -EINVAL;
|
|
+ if (WARN_ON(supp_eht && !supp_he)) {
|
|
+ result = -EINVAL;
|
|
+ goto fail_monitor_chan;
|
|
+ }
|
|
|
|
if (!sband->ht_cap.ht_supported)
|
|
continue;
|
|
@@ -1220,8 +1246,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
|
|
for (j = 0; j < c->n_limits; j++)
|
|
if ((c->limits[j].types & BIT(NL80211_IFTYPE_ADHOC)) &&
|
|
- c->limits[j].max > 1)
|
|
- return -EINVAL;
|
|
+ c->limits[j].max > 1) {
|
|
+ result = -EINVAL;
|
|
+ goto fail_monitor_chan;
|
|
+ }
|
|
}
|
|
|
|
local->int_scan_req = kzalloc(sizeof(*local->int_scan_req) +
|
|
@@ -1516,6 +1544,9 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
|
|
if (local->wiphy_ciphers_allocated)
|
|
kfree(local->hw.wiphy->cipher_suites);
|
|
kfree(local->int_scan_req);
|
|
+fail_monitor_chan:
|
|
+ kfree(local->monitor_chandef);
|
|
+ kfree(local->monitor_sdata);
|
|
return result;
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_register_hw);
|
|
@@ -1565,6 +1596,9 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
|
|
destroy_workqueue(local->workqueue);
|
|
ieee80211_led_exit(local);
|
|
kfree(local->int_scan_req);
|
|
+
|
|
+ kfree(local->monitor_chandef);
|
|
+ kfree(local->monitor_sdata);
|
|
}
|
|
EXPORT_SYMBOL(ieee80211_unregister_hw);
|
|
|
|
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
|
|
index 3053db03f6e5..227c323f663b 100644
|
|
--- a/net/mac80211/rx.c
|
|
+++ b/net/mac80211/rx.c
|
|
@@ -866,10 +866,9 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
|
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(origskb);
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct sk_buff *monskb = NULL;
|
|
- int present_fcs_len = 0;
|
|
+ int present_fcs_len = 0, hw_idx = 0;
|
|
unsigned int rtap_space = 0;
|
|
- struct ieee80211_sub_if_data *monitor_sdata =
|
|
- rcu_dereference(local->monitor_sdata);
|
|
+ struct ieee80211_sub_if_data *monitor_sdata;
|
|
bool only_monitor = false;
|
|
unsigned int min_head_len;
|
|
|
|
@@ -941,6 +940,17 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
|
|
rtap_space);
|
|
}
|
|
|
|
+ if (local->hw.wiphy->num_hw) {
|
|
+ hw_idx = cfg80211_get_hw_idx_by_freq(local->hw.wiphy, status->freq);
|
|
+ if (hw_idx < 0) {
|
|
+ WARN_ON_ONCE(1);
|
|
+ dev_kfree_skb(origskb);
|
|
+ return NULL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ monitor_sdata = rcu_dereference(local->monitor_sdata[hw_idx]);
|
|
+
|
|
ieee80211_handle_mu_mimo_mon(monitor_sdata, origskb, rtap_space);
|
|
|
|
list_for_each_entry_rcu(sdata, &local->mon_list, u.mntr.list) {
|
|
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
|
|
index 94a8785c9976..0e8988fa9768 100644
|
|
--- a/net/mac80211/tx.c
|
|
+++ b/net/mac80211/tx.c
|
|
@@ -1823,7 +1823,10 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
|
|
vif = &sdata->vif;
|
|
break;
|
|
}
|
|
- sdata = rcu_dereference(local->monitor_sdata);
|
|
+ /* Here we wont know the chan to tx except the band info
|
|
+ * so tx in first hw_idx
|
|
+ */
|
|
+ sdata = rcu_dereference(local->monitor_sdata[0]);
|
|
if (sdata) {
|
|
vif = &sdata->vif;
|
|
info->hw_queue =
|
|
@@ -2427,7 +2430,7 @@ netdev_tx_t ieee80211_monitor_start_xmit(struct sk_buff *skb,
|
|
|
|
chanctx_conf = rcu_dereference(sdata->vif.bss_conf.chanctx_conf);
|
|
if (!chanctx_conf) {
|
|
- tmp_sdata = rcu_dereference(local->monitor_sdata);
|
|
+ tmp_sdata = rcu_dereference(local->monitor_sdata[0]);
|
|
if (tmp_sdata)
|
|
chanctx_conf =
|
|
rcu_dereference(tmp_sdata->vif.bss_conf.chanctx_conf);
|
|
@@ -3984,7 +3987,7 @@ begin:
|
|
vif = &tx.sdata->vif;
|
|
break;
|
|
}
|
|
- tx.sdata = rcu_dereference(local->monitor_sdata);
|
|
+ tx.sdata = rcu_dereference(local->monitor_sdata[0]);
|
|
if (tx.sdata) {
|
|
vif = &tx.sdata->vif;
|
|
info->hw_queue =
|
|
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
|
|
index 0902c8319ed0..e5095f8153ab 100644
|
|
--- a/net/mac80211/util.c
|
|
+++ b/net/mac80211/util.c
|
|
@@ -759,6 +759,7 @@ static void __iterate_interfaces(struct ieee80211_local *local,
|
|
{
|
|
struct ieee80211_sub_if_data *sdata;
|
|
bool active_only = iter_flags & IEEE80211_IFACE_ITER_ACTIVE;
|
|
+ int i;
|
|
|
|
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
|
switch (sdata->vif.type) {
|
|
@@ -782,13 +783,16 @@ static void __iterate_interfaces(struct ieee80211_local *local,
|
|
&sdata->vif);
|
|
}
|
|
|
|
- sdata = rcu_dereference_check(local->monitor_sdata,
|
|
- lockdep_is_held(&local->iflist_mtx) ||
|
|
- lockdep_is_held(&local->hw.wiphy->mtx));
|
|
- if (sdata &&
|
|
- (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
|
|
- sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
|
- iterator(data, sdata->vif.addr, &sdata->vif);
|
|
+ i = 0;
|
|
+ do {
|
|
+ sdata = rcu_dereference_check(local->monitor_sdata[i],
|
|
+ lockdep_is_held(&local->iflist_mtx) ||
|
|
+ lockdep_is_held(&local->hw.wiphy->mtx));
|
|
+ if (sdata &&
|
|
+ (iter_flags & IEEE80211_IFACE_ITER_RESUME_ALL || !active_only ||
|
|
+ sdata->flags & IEEE80211_SDATA_IN_DRIVER))
|
|
+ iterator(data, sdata->vif.addr, &sdata->vif);
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
}
|
|
|
|
void ieee80211_iterate_interfaces(
|
|
@@ -2488,7 +2492,7 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|
struct ieee80211_sub_if_data *sdata;
|
|
struct ieee80211_chanctx *ctx;
|
|
struct sta_info *sta;
|
|
- int res;
|
|
+ int res, i;
|
|
bool reconfig_due_to_wowlan = false;
|
|
struct ieee80211_sub_if_data *sched_scan_sdata;
|
|
struct cfg80211_sched_scan_request *sched_scan_req;
|
|
@@ -2572,18 +2576,21 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|
ieee80211_mod_tpt_led_trig(local,
|
|
IEEE80211_TPT_LEDTRIG_FL_RADIO, 0);
|
|
|
|
- /* add interfaces */
|
|
- sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
|
|
- if (sdata) {
|
|
- /* in HW restart it exists already */
|
|
- WARN_ON(local->resuming);
|
|
- res = drv_add_interface(local, sdata);
|
|
- if (WARN_ON(res)) {
|
|
- RCU_INIT_POINTER(local->monitor_sdata, NULL);
|
|
- synchronize_net();
|
|
- kfree(sdata);
|
|
+ i = 0;
|
|
+ do {
|
|
+ /* add interfaces */
|
|
+ sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata[i]);
|
|
+ if (sdata) {
|
|
+ /* in HW restart it exists already */
|
|
+ WARN_ON(local->resuming);
|
|
+ res = drv_add_interface(local, sdata);
|
|
+ if (WARN_ON(res)) {
|
|
+ RCU_INIT_POINTER(local->monitor_sdata[i], NULL);
|
|
+ synchronize_net();
|
|
+ kfree(sdata);
|
|
+ }
|
|
}
|
|
- }
|
|
+ } while (++i < local->hw.wiphy->num_hw);;
|
|
|
|
list_for_each_entry(sdata, &local->interfaces, list) {
|
|
if ((sdata->vif.type != NL80211_IFTYPE_AP_VLAN ||
|
|
@@ -2620,11 +2627,13 @@ int ieee80211_reconfig(struct ieee80211_local *local)
|
|
WARN_ON(drv_add_chanctx(local, ctx));
|
|
mutex_unlock(&local->chanctx_mtx);
|
|
|
|
- sdata = wiphy_dereference(local->hw.wiphy,
|
|
- local->monitor_sdata);
|
|
+ do {
|
|
+ sdata = wiphy_dereference(local->hw.wiphy,
|
|
+ local->monitor_sdata[i]);
|
|
|
|
- if (sdata && ieee80211_sdata_running(sdata))
|
|
- ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
|
|
+ if (sdata && ieee80211_sdata_running(sdata))
|
|
+ ieee80211_assign_chanctx(local, sdata, &sdata->deflink);
|
|
+ } while (++i < local->hw.wiphy->num_hw);
|
|
}
|
|
|
|
/* reconfigure hardware */
|
|
--
|
|
2.38.0
|
|
|