diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index ab25e888a7c8..cc1e8d6b3454 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -796,19 +796,15 @@ struct cfg80211_csa_settings { * @iftype_num: array with the number of interfaces of each interface * type. The index is the interface type as specified in &enum * nl80211_iftype. - * @beacon_int_gcd: a value specifying GCD of all beaconing interfaces, - * the GCD of a single value is considered the value itself, so for - * a single interface this should be set to that interface's beacon - * interval - * @beacon_int_different: a flag indicating whether or not all beacon - * intervals (of beaconing interfaces) are different or not. + * @new_beacon_int: set this to the beacon interval of a new interface + * that's not operating yet, if such is to be checked as part of + * the verification */ struct iface_combination_params { int num_different_channels; u8 radar_detect; int iftype_num[NUM_NL80211_IFTYPES]; - u32 beacon_int_gcd; - bool beacon_int_different; + u32 new_beacon_int; }; /** diff --git a/net/wireless/util.c b/net/wireless/util.c index 2334d3d65173..ef394e8a42bc 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "core.h" #include "rdev-ops.h" @@ -1482,47 +1483,53 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef, } EXPORT_SYMBOL(ieee80211_chandef_to_operating_class); +static void cfg80211_calculate_bi_data(struct wiphy *wiphy, u32 new_beacon_int, + u32 *beacon_int_gcd, + bool *beacon_int_different) +{ + struct wireless_dev *wdev; + + *beacon_int_gcd = 0; + *beacon_int_different = false; + + list_for_each_entry(wdev, &wiphy->wdev_list, list) { + if (!wdev->beacon_interval) + continue; + + if (!*beacon_int_gcd) { + *beacon_int_gcd = wdev->beacon_interval; + continue; + } + + if (wdev->beacon_interval == *beacon_int_gcd) + continue; + + *beacon_int_different = true; + *beacon_int_gcd = gcd(*beacon_int_gcd, wdev->beacon_interval); + } + + if (new_beacon_int && *beacon_int_gcd != new_beacon_int) { + if (*beacon_int_gcd) + *beacon_int_different = true; + *beacon_int_gcd = gcd(*beacon_int_gcd, new_beacon_int); + } +} + int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, enum nl80211_iftype iftype, u32 beacon_int) { - struct wireless_dev *wdev; - struct iface_combination_params params = { - .beacon_int_gcd = beacon_int, /* GCD(n) = n */ - }; + /* + * This is just a basic pre-condition check; if interface combinations + * are possible the driver must already be checking those with a call + * to cfg80211_check_combinations(), in which case we'll validate more + * through the cfg80211_calculate_bi_data() call and code in + * cfg80211_iter_combinations(). + */ 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; - - 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 cfg80211_check_combinations(&rdev->wiphy, ¶ms); + return 0; } int cfg80211_iter_combinations(struct wiphy *wiphy, @@ -1536,6 +1543,21 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, int i, j, iftype; int num_interfaces = 0; u32 used_iftypes = 0; + u32 beacon_int_gcd; + bool beacon_int_different; + + /* + * This is a bit strange, since the iteration used to rely only on + * the data given by the driver, but here it now relies on context, + * in form of the currently operating interfaces. + * This is OK for all current users, and saves us from having to + * push the GCD calculations into all the drivers. + * In the future, this should probably rely more on data that's in + * cfg80211 already - the only thing not would appear to be any new + * interfaces (while being brought up) and channel/radar data. + */ + cfg80211_calculate_bi_data(wiphy, params->new_beacon_int, + &beacon_int_gcd, &beacon_int_different); if (params->radar_detect) { rcu_read_lock(); @@ -1598,12 +1620,11 @@ int cfg80211_iter_combinations(struct wiphy *wiphy, if ((all_iftypes & used_iftypes) != used_iftypes) goto cont; - if (params->beacon_int_gcd) { + if (beacon_int_gcd) { if (c->beacon_int_min_gcd && - params->beacon_int_gcd < c->beacon_int_min_gcd) + beacon_int_gcd < c->beacon_int_min_gcd) goto cont; - if (!c->beacon_int_min_gcd && - params->beacon_int_different) + if (!c->beacon_int_min_gcd && beacon_int_different) goto cont; }