diff options
-rw-r--r-- | include/net/cfg80211.h | 4 | ||||
-rw-r--r-- | net/wireless/core.c | 24 | ||||
-rw-r--r-- | net/wireless/core.h | 14 | ||||
-rw-r--r-- | net/wireless/util.c | 5 |
4 files changed, 47 insertions, 0 deletions
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index e030c6af86dd..f0d213dd8fe7 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h | |||
@@ -1499,6 +1499,8 @@ struct cfg80211_gtk_rekey_data { | |||
1499 | * interfaces are active this callback should reject the configuration. | 1499 | * interfaces are active this callback should reject the configuration. |
1500 | * If no interfaces are active or the device is down, the channel should | 1500 | * If no interfaces are active or the device is down, the channel should |
1501 | * be stored for when a monitor interface becomes active. | 1501 | * be stored for when a monitor interface becomes active. |
1502 | * @set_monitor_enabled: Notify driver that there are only monitor | ||
1503 | * interfaces running. | ||
1502 | * @get_channel: Get the current operating channel, should return %NULL if | 1504 | * @get_channel: Get the current operating channel, should return %NULL if |
1503 | * there's no single defined operating channel if for example the | 1505 | * there's no single defined operating channel if for example the |
1504 | * device implements channel hopping for multi-channel virtual interfaces. | 1506 | * device implements channel hopping for multi-channel virtual interfaces. |
@@ -1817,6 +1819,8 @@ struct cfg80211_ops { | |||
1817 | struct ethtool_stats *stats, u64 *data); | 1819 | struct ethtool_stats *stats, u64 *data); |
1818 | void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, | 1820 | void (*get_et_strings)(struct wiphy *wiphy, struct net_device *dev, |
1819 | u32 sset, u8 *data); | 1821 | u32 sset, u8 *data); |
1822 | |||
1823 | void (*set_monitor_enabled)(struct wiphy *wiphy, bool enabled); | ||
1820 | }; | 1824 | }; |
1821 | 1825 | ||
1822 | /* | 1826 | /* |
diff --git a/net/wireless/core.c b/net/wireless/core.c index c65f59c952c9..8412da7d0f25 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -717,6 +717,24 @@ static struct device_type wiphy_type = { | |||
717 | .name = "wlan", | 717 | .name = "wlan", |
718 | }; | 718 | }; |
719 | 719 | ||
720 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, | ||
721 | enum nl80211_iftype iftype, int num) | ||
722 | { | ||
723 | bool has_monitors_only_old = cfg80211_has_monitors_only(rdev); | ||
724 | bool has_monitors_only_new; | ||
725 | |||
726 | ASSERT_RDEV_LOCK(rdev); | ||
727 | |||
728 | rdev->num_running_ifaces += num; | ||
729 | if (iftype == NL80211_IFTYPE_MONITOR) | ||
730 | rdev->num_running_monitor_ifaces += num; | ||
731 | |||
732 | has_monitors_only_new = cfg80211_has_monitors_only(rdev); | ||
733 | if (has_monitors_only_new != has_monitors_only_old) | ||
734 | rdev->ops->set_monitor_enabled(&rdev->wiphy, | ||
735 | has_monitors_only_new); | ||
736 | } | ||
737 | |||
720 | static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | 738 | static int cfg80211_netdev_notifier_call(struct notifier_block *nb, |
721 | unsigned long state, | 739 | unsigned long state, |
722 | void *ndev) | 740 | void *ndev) |
@@ -820,6 +838,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | |||
820 | break; | 838 | break; |
821 | case NETDEV_DOWN: | 839 | case NETDEV_DOWN: |
822 | dev_hold(dev); | 840 | dev_hold(dev); |
841 | cfg80211_lock_rdev(rdev); | ||
842 | cfg80211_update_iface_num(rdev, wdev->iftype, -1); | ||
843 | cfg80211_unlock_rdev(rdev); | ||
823 | queue_work(cfg80211_wq, &wdev->cleanup_work); | 844 | queue_work(cfg80211_wq, &wdev->cleanup_work); |
824 | break; | 845 | break; |
825 | case NETDEV_UP: | 846 | case NETDEV_UP: |
@@ -927,6 +948,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, | |||
927 | ret = cfg80211_can_add_interface(rdev, wdev->iftype); | 948 | ret = cfg80211_can_add_interface(rdev, wdev->iftype); |
928 | if (ret) | 949 | if (ret) |
929 | return notifier_from_errno(ret); | 950 | return notifier_from_errno(ret); |
951 | cfg80211_lock_rdev(rdev); | ||
952 | cfg80211_update_iface_num(rdev, wdev->iftype, 1); | ||
953 | cfg80211_unlock_rdev(rdev); | ||
930 | break; | 954 | break; |
931 | } | 955 | } |
932 | 956 | ||
diff --git a/net/wireless/core.h b/net/wireless/core.h index 56f18c2eb919..99acd51343b1 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -56,6 +56,9 @@ struct cfg80211_registered_device { | |||
56 | 56 | ||
57 | u32 ap_beacons_nlpid; | 57 | u32 ap_beacons_nlpid; |
58 | 58 | ||
59 | int num_running_ifaces; | ||
60 | int num_running_monitor_ifaces; | ||
61 | |||
59 | /* BSSes/scanning */ | 62 | /* BSSes/scanning */ |
60 | spinlock_t bss_lock; | 63 | spinlock_t bss_lock; |
61 | struct list_head bss_list; | 64 | struct list_head bss_list; |
@@ -197,6 +200,14 @@ static inline void wdev_unlock(struct wireless_dev *wdev) | |||
197 | #define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) | 200 | #define ASSERT_RDEV_LOCK(rdev) lockdep_assert_held(&(rdev)->mtx) |
198 | #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) | 201 | #define ASSERT_WDEV_LOCK(wdev) lockdep_assert_held(&(wdev)->mtx) |
199 | 202 | ||
203 | static inline bool cfg80211_has_monitors_only(struct cfg80211_registered_device *rdev) | ||
204 | { | ||
205 | ASSERT_RDEV_LOCK(rdev); | ||
206 | |||
207 | return rdev->num_running_ifaces == rdev->num_running_monitor_ifaces && | ||
208 | rdev->num_running_ifaces > 0; | ||
209 | } | ||
210 | |||
200 | enum cfg80211_event_type { | 211 | enum cfg80211_event_type { |
201 | EVENT_CONNECT_RESULT, | 212 | EVENT_CONNECT_RESULT, |
202 | EVENT_ROAMED, | 213 | EVENT_ROAMED, |
@@ -444,6 +455,9 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, | |||
444 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | 455 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, |
445 | u32 beacon_int); | 456 | u32 beacon_int); |
446 | 457 | ||
458 | void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, | ||
459 | enum nl80211_iftype iftype, int num); | ||
460 | |||
447 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS | 461 | #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS |
448 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) | 462 | #define CFG80211_DEV_WARN_ON(cond) WARN_ON(cond) |
449 | #else | 463 | #else |
diff --git a/net/wireless/util.c b/net/wireless/util.c index fc948d0a53f3..9b92ec57d07b 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -871,6 +871,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
871 | } | 871 | } |
872 | } | 872 | } |
873 | 873 | ||
874 | if (!err && ntype != otype && netif_running(dev)) { | ||
875 | cfg80211_update_iface_num(rdev, ntype, 1); | ||
876 | cfg80211_update_iface_num(rdev, otype, -1); | ||
877 | } | ||
878 | |||
874 | return err; | 879 | return err; |
875 | } | 880 | } |
876 | 881 | ||