diff --git a/net/wireless/chan.c b/net/wireless/chan.c index 1071e823108b..c743b6fb7e30 100644 --- a/net/wireless/chan.c +++ b/net/wireless/chan.c @@ -1810,13 +1810,44 @@ bool cfg80211_reg_check_beaconing(struct wiphy *wiphy, } EXPORT_SYMBOL(cfg80211_reg_check_beaconing); +static bool cfg80211_can_set_monitor_channel(struct cfg80211_registered_device *rdev, + struct cfg80211_chan_def *chandef) +{ + struct wireless_dev *wdev; + int radio_idx; + + lockdep_assert_held(&rdev->wiphy.mtx); + + if (rdev->num_running_monitor_ifaces < 1) + return false; + + if (cfg80211_has_monitors_only(rdev)) + return true; + + radio_idx = cfg80211_get_radio_idx_by_chan(&rdev->wiphy, chandef->chan); + /* No defined radio covers this channel. Fall back on global behavior */ + if (radio_idx < 0) + return false; + + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + if (wdev->iftype == NL80211_IFTYPE_MONITOR) + continue; + if (!wdev->netdev) + continue; + if (rdev_get_radio_mask(rdev, wdev->netdev) & BIT(radio_idx)) + return false; + } + + return true; +} + int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev, struct net_device *dev, struct cfg80211_chan_def *chandef) { if (!rdev->ops->set_monitor_channel) return -EOPNOTSUPP; - if (!cfg80211_has_monitors_only(rdev)) + if (!cfg80211_can_set_monitor_channel(rdev, chandef)) return -EBUSY; return rdev_set_monitor_channel(rdev, dev, chandef);