summaryrefslogtreecommitdiff
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c46
1 files changed, 40 insertions, 6 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 0ca55b9655a7..4f04d95c19d4 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -741,8 +741,9 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, bool going_do
ieee80211_configure_filter(local);
ieee80211_hw_config(local, -1, hw_reconf_flags);
+ /* Passing NULL means an interface is picked for configuration */
if (local->virt_monitors == local->open_count)
- ieee80211_add_virtual_monitor(local);
+ ieee80211_add_virtual_monitor(local, NULL);
}
void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata)
@@ -1176,7 +1177,8 @@ static void ieee80211_sdata_init(struct ieee80211_local *local,
ieee80211_link_init(sdata, -1, &sdata->deflink, &sdata->vif.bss_conf);
}
-int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
+int ieee80211_add_virtual_monitor(struct ieee80211_local *local,
+ struct ieee80211_sub_if_data *creator_sdata)
{
struct ieee80211_sub_if_data *sdata;
int ret;
@@ -1184,10 +1186,14 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
ASSERT_RTNL();
lockdep_assert_wiphy(local->hw.wiphy);
- if (local->monitor_sdata ||
- ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
+ if (ieee80211_hw_check(&local->hw, NO_VIRTUAL_MONITOR))
return 0;
+ /* Already have a monitor set up, configure it */
+ sdata = wiphy_dereference(local->hw.wiphy, local->monitor_sdata);
+ if (sdata)
+ goto configure_monitor;
+
sdata = kzalloc(sizeof(*sdata) + local->hw.vif_data_size, GFP_KERNEL);
if (!sdata)
return -ENOMEM;
@@ -1240,6 +1246,32 @@ int ieee80211_add_virtual_monitor(struct ieee80211_local *local)
skb_queue_head_init(&sdata->status_queue);
wiphy_work_init(&sdata->work, ieee80211_iface_work);
+configure_monitor:
+ /* Copy in the MU-MIMO configuration if set */
+ if (!creator_sdata) {
+ struct ieee80211_sub_if_data *other;
+
+ list_for_each_entry(other, &local->mon_list, list) {
+ if (!other->vif.bss_conf.mu_mimo_owner)
+ continue;
+
+ creator_sdata = other;
+ break;
+ }
+ }
+
+ if (creator_sdata && creator_sdata->vif.bss_conf.mu_mimo_owner) {
+ sdata->vif.bss_conf.mu_mimo_owner = true;
+ memcpy(&sdata->vif.bss_conf.mu_group,
+ &creator_sdata->vif.bss_conf.mu_group,
+ sizeof(sdata->vif.bss_conf.mu_group));
+ memcpy(&sdata->u.mntr.mu_follow_addr,
+ creator_sdata->u.mntr.mu_follow_addr, ETH_ALEN);
+
+ ieee80211_link_info_change_notify(sdata, &sdata->deflink,
+ BSS_CHANGED_MU_GROUPS);
+ }
+
return 0;
}
@@ -1396,11 +1428,13 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
if (res)
goto err_stop;
} else {
- if (local->virt_monitors == 0 && local->open_count == 0) {
- res = ieee80211_add_virtual_monitor(local);
+ /* add/configure if there is no non-monitor interface */
+ if (local->virt_monitors == local->open_count) {
+ res = ieee80211_add_virtual_monitor(local, sdata);
if (res)
goto err_stop;
}
+
local->virt_monitors++;
/* must be before the call to ieee80211_configure_filter */