diff options
Diffstat (limited to 'net/wireless/core.c')
-rw-r--r-- | net/wireless/core.c | 84 |
1 files changed, 82 insertions, 2 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 907f62c80e28..eb60410ae588 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -373,6 +373,14 @@ static int wiphy_verify_combinations(struct wiphy *wiphy) if (WARN_ON(!c->num_different_channels)) return -EINVAL; + /* + * Put a sane limit on maximum number of different + * channels to simplify channel accounting code. + */ + if (WARN_ON(c->num_different_channels > + CFG80211_MAX_NUM_DIFFERENT_CHANNELS)) + return -EINVAL; + if (WARN_ON(!c->n_limits)) return -EINVAL; @@ -421,9 +429,11 @@ int wiphy_register(struct wiphy *wiphy) int i; u16 ifmodes = wiphy->interface_modes; +#ifdef CONFIG_PM if (WARN_ON((wiphy->wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) && !(wiphy->wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY))) return -EINVAL; +#endif if (WARN_ON(wiphy->ap_sme_capa && !(wiphy->flags & WIPHY_FLAG_HAVE_AP_SME))) @@ -458,8 +468,14 @@ int wiphy_register(struct wiphy *wiphy) continue; sband->band = band; - - if (WARN_ON(!sband->n_channels || !sband->n_bitrates)) + if (WARN_ON(!sband->n_channels)) + return -EINVAL; + /* + * on 60gHz band, there are no legacy rates, so + * n_bitrates is 0 + */ + if (WARN_ON(band != IEEE80211_BAND_60GHZ && + !sband->n_bitrates)) return -EINVAL; /* @@ -500,12 +516,14 @@ int wiphy_register(struct wiphy *wiphy) return -EINVAL; } +#ifdef CONFIG_PM if (rdev->wiphy.wowlan.n_patterns) { if (WARN_ON(!rdev->wiphy.wowlan.pattern_min_len || rdev->wiphy.wowlan.pattern_min_len > rdev->wiphy.wowlan.pattern_max_len)) return -EINVAL; } +#endif /* check and set up bitrates */ ieee80211_set_bitrate_flags(wiphy); @@ -713,6 +731,61 @@ static struct device_type wiphy_type = { .name = "wlan", }; +static struct ieee80211_channel * +cfg80211_get_any_chan(struct cfg80211_registered_device *rdev) +{ + struct ieee80211_supported_band *sband; + int i; + + for (i = 0; i < IEEE80211_NUM_BANDS; i++) { + sband = rdev->wiphy.bands[i]; + if (sband && sband->n_channels > 0) + return &sband->channels[0]; + } + + return NULL; +} + +static void cfg80211_init_mon_chan(struct cfg80211_registered_device *rdev) +{ + struct ieee80211_channel *chan; + + chan = cfg80211_get_any_chan(rdev); + if (WARN_ON(!chan)) + return; + + mutex_lock(&rdev->devlist_mtx); + WARN_ON(cfg80211_set_monitor_channel(rdev, chan->center_freq, + NL80211_CHAN_NO_HT)); + mutex_unlock(&rdev->devlist_mtx); +} + +void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, + enum nl80211_iftype iftype, int num) +{ + bool has_monitors_only_old = cfg80211_has_monitors_only(rdev); + bool has_monitors_only_new; + + ASSERT_RTNL(); + + rdev->num_running_ifaces += num; + if (iftype == NL80211_IFTYPE_MONITOR) + rdev->num_running_monitor_ifaces += num; + + has_monitors_only_new = cfg80211_has_monitors_only(rdev); + if (has_monitors_only_new != has_monitors_only_old) { + rdev->ops->set_monitor_enabled(&rdev->wiphy, + has_monitors_only_new); + + if (!has_monitors_only_new) { + rdev->monitor_channel = NULL; + rdev->monitor_channel_type = NL80211_CHAN_NO_HT; + } else { + cfg80211_init_mon_chan(rdev); + } + } +} + static int cfg80211_netdev_notifier_call(struct notifier_block *nb, unsigned long state, void *ndev) @@ -806,12 +879,16 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, case NL80211_IFTYPE_MESH_POINT: cfg80211_leave_mesh(rdev, dev); break; + case NL80211_IFTYPE_AP: + cfg80211_stop_ap(rdev, dev); + break; default: break; } wdev->beacon_interval = 0; break; case NETDEV_DOWN: + cfg80211_update_iface_num(rdev, wdev->iftype, -1); dev_hold(dev); queue_work(cfg80211_wq, &wdev->cleanup_work); break; @@ -917,9 +994,12 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb, return notifier_from_errno(-EOPNOTSUPP); if (rfkill_blocked(rdev->rfkill)) return notifier_from_errno(-ERFKILL); + mutex_lock(&rdev->devlist_mtx); ret = cfg80211_can_add_interface(rdev, wdev->iftype); + mutex_unlock(&rdev->devlist_mtx); if (ret) return notifier_from_errno(ret); + cfg80211_update_iface_num(rdev, wdev->iftype, 1); break; } |