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 beacon_int_min_gcd (u32).
This beacon_int_min_gcd will be compared against GCD of all beaconing
interfaces of matching combinations.

Following sets the expectation for beacon_int_min_gcd.

= 0 - all beacon intervals for different interfaces 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 greator or equal to this value.

Signed-off-by: Purushottam Kushwaha <pkush...@qti.qualcomm.com>
---
 .../broadcom/brcm80211/brcmfmac/cfg80211.c         |  4 +++
 include/net/cfg80211.h                             | 14 +++++++-
 include/uapi/linux/nl80211.h                       |  8 +++--
 net/wireless/core.h                                |  2 +-
 net/wireless/nl80211.c                             | 14 ++++++--
 net/wireless/util.c                                | 37 ++++++++++++++++++++--
 6 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 44e1c70..2624569 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -421,6 +421,8 @@ static int brcmf_vif_change_validate(struct 
brcmf_cfg80211_info *cfg,
                .num_different_channels = 1,
                .radar_detect = 0,
                .iftype_num = {0},
+               .beacon_gcd = 0,
+               .diff_bi = false,
        };
 
        list_for_each_entry(pos, &cfg->vif_list, list)
@@ -446,6 +448,8 @@ static int brcmf_vif_add_validate(struct 
brcmf_cfg80211_info *cfg,
                .num_different_channels = 1,
                .radar_detect = 0,
                .iftype_num = {0},
+               .beacon_gcd = 0,
+               .diff_bi = false,
        };
 
        list_for_each_entry(pos, &cfg->vif_list, list)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 21bbe44..f0bcd55 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -784,11 +784,15 @@ struct cfg80211_csa_settings {
  * @iftype_num: array with the numbers of interfaces of each interface
  *     type.  The index is the interface type as specified in &enum
  *     nl80211_iftype.
+ * @beacon_gcd: a value specifying GCD of all beaconing interfaces.
+ * @diff_bi: a flag which denotes beacon intervals are different or same.
  */
 struct iface_combination_params {
        int num_different_channels;
        u8 radar_detect;
        int iftype_num[NUM_NL80211_IFTYPES];
+       u32 beacon_gcd;
+       bool diff_bi;
 };
 
 /**
@@ -3100,6 +3104,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 greator or equal to this value.
  *
  * With this structure the driver can describe which interface
  * combinations it supports concurrently.
@@ -3120,7 +3130,7 @@ struct ieee80211_iface_limit {
  *  };
  *
  *
- * 2. Allow #{AP, P2P-GO} <= 8, channels = 1, 8 total:
+ * 2. Allow #{AP, P2P-GO} <= 8, BI min gcd = 10, channels = 1, 8 total:
  *
  *  struct ieee80211_iface_limit limits2[] = {
  *     { .max = 8, .types = BIT(NL80211_IFTYPE_AP) |
@@ -3131,6 +3141,7 @@ struct ieee80211_iface_limit {
  *     .n_limits = ARRAY_SIZE(limits2),
  *     .max_interfaces = 8,
  *     .num_different_channels = 1,
+ *     .beacon_int_min_gcd = 10,
  *  };
  *
  *
@@ -3158,6 +3169,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 {
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 56368e9..3c19cca 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -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 interval must match).
  * @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,
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 08d2e94..21e3188 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 c510810..903cd5a 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 0d69b25..3f3d684 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -1559,24 +1559,49 @@ 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,
+                                enum nl80211_iftype iftype,
                                 u32 beacon_int)
 {
        struct wireless_dev *wdev;
        int res = 0;
+       u32 bi_prev, tmp_bi;
+       struct iface_combination_params params = {
+               .num_different_channels = 0,
+               .radar_detect = 0,
+               .iftype_num = {0},
+               .beacon_gcd = beacon_int,       /* GCD(n) = n */
+               .diff_bi = false,
+       };
 
        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) {
                if (!wdev->beacon_interval)
                        continue;
                if (wdev->beacon_interval != beacon_int) {
-                       res = -EINVAL;
+                       params.diff_bi = true;
+                       /* Get the GCD */
+                       bi_prev = wdev->beacon_interval;
+                       while (bi_prev != 0) {
+                               tmp_bi = bi_prev;
+                               bi_prev = params.beacon_gcd % bi_prev;
+                               params.beacon_gcd = tmp_bi;
+                       }
                        break;
                }
        }
 
-       return res;
+       res = cfg80211_check_combinations(&rdev->wiphy, &params);
+       return (res < 0) ? res : 0;
 }
 
 int cfg80211_iter_combinations(struct wiphy *wiphy,
@@ -1652,6 +1677,14 @@ int cfg80211_iter_combinations(struct wiphy *wiphy,
                if ((all_iftypes & used_iftypes) != used_iftypes)
                        goto cont;
 
+               if (params->beacon_gcd) {
+                       if (c->beacon_int_min_gcd &&
+                           params->beacon_gcd < c->beacon_int_min_gcd)
+                               return -EINVAL;
+                       if (!c->beacon_int_min_gcd && params->diff_bi)
+                               goto cont;
+               }
+
                /* This combination covered all interface types and
                 * supported the requested numbers, so we're good.
                 */
-- 
1.9.1

Reply via email to