diff options
author | Purushottam Kushwaha <pkushwah@qti.qualcomm.com> | 2016-10-12 18:26:51 +0530 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2016-10-13 14:28:29 +0200 |
commit | 0c317a02ca982ca093e71bf07cb562265ba40032 (patch) | |
tree | 43f40ded70233ed84076b9c4579cc4df713455ad /net/wireless | |
parent | e227300c8395dffaa7614ce7c7666a82180ebc60 (diff) | |
download | linux-0c317a02ca982ca093e71bf07cb562265ba40032.tar.bz2 |
cfg80211: support virtual interfaces with different beacon intervals
This commit provides a mechanism for the host drivers to advertise the
support for different beacon intervals among the respective interface
combinations in a group, through NL80211_IFACE_COMB_BI_MIN_GCD (u32).
This value will be compared against GCD of all beaconing interfaces of
matching combinations.
If the driver doesn't advertise this value, the old behaviour where
all beacon intervals must be identical is retained.
If it is specified, then any beacon interval for an interface in the
interface combination as well as the GCD of all active beacon intervals
in the combination must be greater or equal to this value.
Signed-off-by: Purushottam Kushwaha <pkushwah@qti.qualcomm.com>
[change commit message, some variable names, small other things]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/core.h | 2 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 14 | ||||
-rw-r--r-- | net/wireless/util.c | 43 |
3 files changed, 49 insertions, 10 deletions
diff --git a/net/wireless/core.h b/net/wireless/core.h index 08d2e948c9ad..21e31888cfa9 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -475,7 +475,7 @@ int ieee80211_get_ratemask(struct ieee80211_supported_band *sband, u32 *mask); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int); + enum nl80211_iftype iftype, u32 beacon_int); void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, int num); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c510810f0b7c..903cd5a5d1ce 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1075,6 +1075,10 @@ static int nl80211_put_iface_combinations(struct wiphy *wiphy, nla_put_u32(msg, NL80211_IFACE_COMB_RADAR_DETECT_REGIONS, c->radar_detect_regions))) goto nla_put_failure; + if (c->beacon_int_min_gcd && + nla_put_u32(msg, NL80211_IFACE_COMB_BI_MIN_GCD, + c->beacon_int_min_gcd)) + goto nla_put_failure; nla_nest_end(msg, nl_combi); } @@ -3803,7 +3807,8 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info) params.dtim_period = nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); - err = cfg80211_validate_beacon_int(rdev, params.beacon_interval); + err = cfg80211_validate_beacon_int(rdev, dev->ieee80211_ptr->iftype, + params.beacon_interval); if (err) return err; @@ -8152,7 +8157,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) ibss.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - err = cfg80211_validate_beacon_int(rdev, ibss.beacon_interval); + err = cfg80211_validate_beacon_int(rdev, NL80211_IFTYPE_ADHOC, + ibss.beacon_interval); if (err) return err; @@ -9417,7 +9423,9 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) setup.beacon_interval = nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); - err = cfg80211_validate_beacon_int(rdev, setup.beacon_interval); + err = cfg80211_validate_beacon_int(rdev, + NL80211_IFTYPE_MESH_POINT, + setup.beacon_interval); if (err) return err; } diff --git a/net/wireless/util.c b/net/wireless/util.c index 0d69b257793d..d2ea1f152d17 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -1559,24 +1559,46 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, EXPORT_SYMBOL(ieee80211_chandef_to_operating_class); int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, - u32 beacon_int) + enum nl80211_iftype iftype, u32 beacon_int) { struct wireless_dev *wdev; - int res = 0; + struct iface_combination_params params = { + .beacon_int_gcd = beacon_int, /* GCD(n) = n */ + }; if (beacon_int < 10 || beacon_int > 10000) return -EINVAL; + params.iftype_num[iftype] = 1; list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { if (!wdev->beacon_interval) continue; - if (wdev->beacon_interval != beacon_int) { - res = -EINVAL; - break; + + params.iftype_num[wdev->iftype]++; + } + + list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { + u32 bi_prev = wdev->beacon_interval; + + if (!wdev->beacon_interval) + continue; + + /* slight optimisation - skip identical BIs */ + if (wdev->beacon_interval == beacon_int) + continue; + + params.beacon_int_different = true; + + /* Get the GCD */ + while (bi_prev != 0) { + u32 tmp_bi = bi_prev; + + bi_prev = params.beacon_int_gcd % bi_prev; + params.beacon_int_gcd = tmp_bi; } } - return res; + return cfg80211_check_combinations(&rdev->wiphy, ¶ms); } int cfg80211_iter_combinations(struct wiphy *wiphy, @@ -1652,6 +1674,15 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if ((all_iftypes & used_iftypes) != used_iftypes) goto cont; + if (params->beacon_int_gcd) { + if (c->beacon_int_min_gcd && + params->beacon_int_gcd < c->beacon_int_min_gcd) + return -EINVAL; + if (!c->beacon_int_min_gcd && + params->beacon_int_different) + goto cont; + } + /* This combination covered all interface types and * supported the requested numbers, so we're good. */ |