cfg80211: support virtual interfaces with different beacon intervals
authorPurushottam Kushwaha <pkushwah@qti.qualcomm.com>
Wed, 12 Oct 2016 12:56:51 +0000 (18:26 +0530)
committerJohannes Berg <johannes.berg@intel.com>
Thu, 13 Oct 2016 12:28:29 +0000 (14:28 +0200)
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>
include/net/cfg80211.h
include/uapi/linux/nl80211.h
net/wireless/core.h
net/wireless/nl80211.c
net/wireless/util.c

index ea108541e1e02b541b2ebeb0855cbaf2012e217c..5000ec758eb3d3fa3b418749a2c8ade6d0cf753d 100644 (file)
@@ -784,11 +784,19 @@ 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.
  */
 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;
 };
 
 /**
@@ -3100,6 +3108,12 @@ struct ieee80211_iface_limit {
  *     only in special cases.
  * @radar_detect_widths: bitmap of channel widths supported for radar detection
  * @radar_detect_regions: bitmap of regions supported for radar detection
+ * @beacon_int_min_gcd: This interface combination supports different
+ *     beacon intervals.
+ *     = 0 - all beacon intervals for different interface must be same.
+ *     > 0 - any beacon interval for the interface part of this combination AND
+ *           *GCD* of all beacon intervals from beaconing interfaces of this
+ *           combination must be greater or equal to this value.
  *
  * With this structure the driver can describe which interface
  * combinations it supports concurrently.
@@ -3158,6 +3172,7 @@ struct ieee80211_iface_combination {
        bool beacon_int_infra_match;
        u8 radar_detect_widths;
        u8 radar_detect_regions;
+       u32 beacon_int_min_gcd;
 };
 
 struct ieee80211_txrx_stypes {
index 56368e9b462245b6a50a2756abac6233b95650d9..1362d24957b514044d61721469b2c66bf740affb 100644 (file)
@@ -4280,6 +4280,9 @@ enum nl80211_iface_limit_attrs {
  *     of supported channel widths for radar detection.
  * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
  *     of supported regulatory regions for radar detection.
+ * @NL80211_IFACE_COMB_BI_MIN_GCD: u32 attribute specifying the minimum GCD of
+ *     different beacon intervals supported by all the interface combinations
+ *     in this group (if not present, all beacon intervals be identical).
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -4287,8 +4290,8 @@ enum nl80211_iface_limit_attrs {
  *     limits = [ #{STA} <= 1, #{AP} <= 1 ], matching BI, channels = 1, max = 2
  *     => allows an AP and a STA that must match BIs
  *
- *     numbers = [ #{AP, P2P-GO} <= 8 ], channels = 1, max = 8
- *     => allows 8 of AP/GO
+ *     numbers = [ #{AP, P2P-GO} <= 8 ], BI min gcd, channels = 1, max = 8,
+ *     => allows 8 of AP/GO that can have BI gcd >= min gcd
  *
  *     numbers = [ #{STA} <= 2 ], channels = 2, max = 2
  *     => allows two STAs on different channels
@@ -4314,6 +4317,7 @@ enum nl80211_if_combination_attrs {
        NL80211_IFACE_COMB_NUM_CHANNELS,
        NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
        NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
+       NL80211_IFACE_COMB_BI_MIN_GCD,
 
        /* keep last */
        NUM_NL80211_IFACE_COMB,
index 08d2e948c9ad306a0ed40531efc4103877b9f4ae..21e31888cfa93f4b63be12e6144145817291992a 100644 (file)
@@ -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);
index c510810f0b7c14909fc3b83ec31ce6c4ac45dfc2..903cd5a5d1cebd06162090c0f14e27cab4a79ac3 100644 (file)
@@ -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;
        }
index 0d69b257793d6f531346d98d8cc34a07ca4f7594..d2ea1f152d17a6eda18b2a8b485e9d79807c4ed7 100644 (file)
@@ -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, &params);
 }
 
 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.
                 */