mac80211 sets its monitor flag only for the virtual monitor, so a monitor that reaches the driver as its own vif, as every monitor on MT7927 does, is not treated as one and goes deaf after a scan. Count monitor vifs and treat one like the monitor flag. Start a new monitor's sniffer on the radio's current channel, and set it up again after a scan and when another interface goes down, where mac80211 would add the virtual monitor back. Signed-off-by: Devin Wittmayer --- .../wireless/mediatek/mt76/mt7925/debugfs.c | 2 +- .../net/wireless/mediatek/mt76/mt7925/main.c | 70 ++++++++++++++++++- .../wireless/mediatek/mt76/mt7925/testmode.c | 4 +- 3 files changed, 70 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c index d01ff49de47a..038ace1f0369 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/debugfs.c @@ -228,7 +228,7 @@ mt7925_deep_sleep_set(void *data, u64 val) { struct mt792x_dev *dev = data; struct mt76_connac_pm *pm = &dev->pm; - bool monitor = !!(dev->mphy.hw->conf.flags & IEEE80211_CONF_MONITOR); + bool monitor = mt792x_monitor_active(dev); bool enable = !!val; if (mt76_is_usb(&dev->mt76)) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 22cc5b49da0a..08d860f0ec70 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -451,6 +451,9 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, return ret; } +static void +mt7925_sniffer_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif); + static int mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -471,6 +474,15 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) if (ret < 0) goto out; + if (vif->type == NL80211_IFTYPE_MONITOR) { + dev->monitor_vifs++; + mt7925_sniffer_interface_iter(dev, NULL, vif); + mt7925_mcu_config_sniffer(mvif, NULL); + ieee80211_iterate_active_interfaces_mtx(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_sniffer_interface_iter, dev); + } + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER; if (phy->chip_cap & MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN) vif->driver_flags |= IEEE80211_VIF_SUPPORTS_CQM_RSSI; @@ -486,6 +498,49 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) return ret; } +static void +mt7925_monitor_collect_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct ieee80211_vif **mon = priv; + int i; + + if (vif->type != NL80211_IFTYPE_MONITOR) + return; + + for (i = 0; i < MT792x_MAX_INTERFACES; i++) { + if (!mon[i]) { + mon[i] = vif; + return; + } + } +} + +/* A scan takes the radio off the monitor's channel and the sniffer does + * not come back by itself, so set it up again when a scan completes or + * another interface goes down. The commands sleep, so collect the + * monitors first. Called with the device mutex held. + */ +static void mt7925_monitor_rearm(struct mt792x_dev *dev) +{ + struct ieee80211_vif *mon[MT792x_MAX_INTERFACES] = {}; + int i; + + if (!dev->monitor_vifs) + return; + + ieee80211_iterate_active_interfaces_atomic(mt76_hw(dev), + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_monitor_collect_iter, mon); + for (i = 0; i < MT792x_MAX_INTERFACES && mon[i]; i++) { + struct mt792x_vif *mvif = (struct mt792x_vif *)mon[i]->drv_priv; + struct ieee80211_chanctx_conf *ctx = mvif->bss_conf.mt76.ctx; + + mt7925_mcu_set_sniffer(dev, mon[i], true); + if (ctx) + mt7925_mcu_config_sniffer(mvif, ctx); + } +} + static void mt7925_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) { @@ -501,6 +556,13 @@ mt7925_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mconf = mt792x_link_conf_to_mconf(&vif->bss_conf); mt792x_mac_link_bss_remove(dev, mconf, &mvif->sta.deflink); + if (vif->type != NL80211_IFTYPE_MONITOR) + mt7925_monitor_rearm(dev); + else if (!--dev->monitor_vifs) + ieee80211_iterate_active_interfaces_mtx(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_sniffer_interface_iter, dev); + mt792x_mutex_release(dev); } @@ -798,9 +860,8 @@ static void mt7925_sniffer_interface_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) { struct mt792x_dev *dev = priv; - struct ieee80211_hw *hw = mt76_hw(dev); struct mt76_connac_pm *pm = &dev->pm; - bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + bool monitor = mt792x_monitor_active(dev); mt7925_mcu_set_sniffer(dev, vif, monitor); pm->enable = pm->enable_user && !monitor; @@ -816,7 +877,7 @@ void mt7925_set_runtime_pm(struct mt792x_dev *dev) { struct ieee80211_hw *hw = mt76_hw(dev); struct mt76_connac_pm *pm = &dev->pm; - bool monitor = !!(hw->conf.flags & IEEE80211_CONF_MONITOR); + bool monitor = mt792x_monitor_active(dev); pm->enable = pm->enable_user && !monitor; ieee80211_iterate_active_interfaces(hw, @@ -1593,6 +1654,9 @@ void mt7925_scan_work(struct work_struct *work) .aborted = false, }; ieee80211_scan_completed(phy->mt76->hw, &info); + mt792x_mutex_acquire(dev); + mt7925_monitor_rearm(dev); + mt792x_mutex_release(dev); } break; case UNI_EVENT_SCAN_DONE_CHNLINFO: diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c index 22a8f1ddc321..65bfcf8721fc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -127,7 +127,7 @@ int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int err; if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || - !(hw->conf.flags & IEEE80211_CONF_MONITOR)) + !mt792x_monitor_active(phy->dev)) return -ENOTCONN; err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, @@ -164,7 +164,7 @@ int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, int err; if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || - !(hw->conf.flags & IEEE80211_CONF_MONITOR) || + !mt792x_monitor_active(phy->dev) || !mt76_testmode_enabled(mphy)) return -ENOTCONN; -- 2.56.0