cfg80211: add channel checking for iface combinations
authorMichal Kazior <michal.kazior@tieto.com>
Fri, 29 Jun 2012 10:47:07 +0000 (12:47 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Fri, 29 Jun 2012 11:39:19 +0000 (13:39 +0200)
.connect cannot be handled since the driver scans
and connects on its own. It is up to the driver
then to refuse a connection (with -EBUSY for
example).

Non-fixed channel IBSSes always take a single
channel resource. For example two non-fixed
channel IBSSes always take up 2
num_different_channels, even if they operate on
the same channel at a given point of time.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/wireless/core.c
net/wireless/core.h
net/wireless/util.c

index 580551e9cabaccbf5adef1fb3e8be4e668e31ae4..b26695ad3e97ddb6913e40ecff3f2c615c54b073 100644 (file)
@@ -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;
 
index d5efe1b0a8f7ac269eba6293bfe4eb18ed73f9fa..81fef3ddb5a8d83bc85d4e44c03994a6a6a7a5d8 100644 (file)
@@ -428,9 +428,20 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                          u32 *flags, struct vif_params *params);
 void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
 
-int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
-                                 struct wireless_dev *wdev,
-                                 enum nl80211_iftype iftype);
+int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
+                                struct wireless_dev *wdev,
+                                enum nl80211_iftype iftype,
+                                struct ieee80211_channel *chan,
+                                enum cfg80211_chan_mode chanmode);
+
+static inline int
+cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
+                             struct wireless_dev *wdev,
+                             enum nl80211_iftype iftype)
+{
+       return cfg80211_can_use_iftype_chan(rdev, wdev, iftype, NULL,
+                                           CHAN_MODE_UNDEFINED);
+}
 
 static inline int
 cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
@@ -439,6 +450,16 @@ cfg80211_can_add_interface(struct cfg80211_registered_device *rdev,
        return cfg80211_can_change_interface(rdev, NULL, iftype);
 }
 
+static inline int
+cfg80211_can_use_chan(struct cfg80211_registered_device *rdev,
+                     struct wireless_dev *wdev,
+                     struct ieee80211_channel *chan,
+                     enum cfg80211_chan_mode chanmode)
+{
+       return cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
+                                           chan, chanmode);
+}
+
 void
 cfg80211_get_chan_state(struct cfg80211_registered_device *rdev,
                        struct wireless_dev *wdev,
@@ -461,6 +482,8 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
 void cfg80211_update_iface_num(struct cfg80211_registered_device *rdev,
                               enum nl80211_iftype iftype, int num);
 
+#define CFG80211_MAX_NUM_DIFFERENT_CHANNELS 10
+
 #ifdef CONFIG_CFG80211_DEVELOPER_WARNINGS
 #define CFG80211_DEV_WARN_ON(cond)     WARN_ON(cond)
 #else
index 9b92ec57d07bd66c4ef11afa3dae829b39d49ba3..4713cea9a2fa62b1118d3162ca182d6809f8c3f0 100644 (file)
@@ -938,13 +938,20 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
        return res;
 }
 
-int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
-                                 struct wireless_dev *wdev,
-                                 enum nl80211_iftype iftype)
+int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
+                                struct wireless_dev *wdev,
+                                enum nl80211_iftype iftype,
+                                struct ieee80211_channel *chan,
+                                enum cfg80211_chan_mode chanmode)
 {
        struct wireless_dev *wdev_iter;
        u32 used_iftypes = BIT(iftype);
        int num[NUM_NL80211_IFTYPES];
+       struct ieee80211_channel
+                       *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
+       struct ieee80211_channel *ch;
+       enum cfg80211_chan_mode chmode;
+       int num_different_channels = 0;
        int total = 1;
        int i, j;
 
@@ -955,9 +962,23 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                return 0;
 
        memset(num, 0, sizeof(num));
+       memset(used_channels, 0, sizeof(used_channels));
 
        num[iftype] = 1;
 
+       switch (chanmode) {
+       case CHAN_MODE_UNDEFINED:
+               break;
+       case CHAN_MODE_SHARED:
+               WARN_ON(!chan);
+               used_channels[0] = chan;
+               num_different_channels++;
+               break;
+       case CHAN_MODE_EXCLUSIVE:
+               num_different_channels++;
+               break;
+       }
+
        mutex_lock(&rdev->devlist_mtx);
        list_for_each_entry(wdev_iter, &rdev->netdev_list, list) {
                if (wdev_iter == wdev)
@@ -968,6 +989,31 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
                        continue;
 
+               cfg80211_get_chan_state(rdev, wdev_iter, &ch, &chmode);
+
+               switch (chmode) {
+               case CHAN_MODE_UNDEFINED:
+                       break;
+               case CHAN_MODE_SHARED:
+                       for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
+                               if (!used_channels[i] || used_channels[i] == ch)
+                                       break;
+
+                       if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS) {
+                               mutex_unlock(&rdev->devlist_mtx);
+                               return -EBUSY;
+                       }
+
+                       if (used_channels[i] == NULL) {
+                               used_channels[i] = ch;
+                               num_different_channels++;
+                       }
+                       break;
+               case CHAN_MODE_EXCLUSIVE:
+                       num_different_channels++;
+                       break;
+               }
+
                num[wdev_iter->iftype]++;
                total++;
                used_iftypes |= BIT(wdev_iter->iftype);
@@ -984,12 +1030,15 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
 
                c = &rdev->wiphy.iface_combinations[i];
 
+               if (total > c->max_interfaces)
+                       continue;
+               if (num_different_channels > c->num_different_channels)
+                       continue;
+
                limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
                                 GFP_KERNEL);
                if (!limits)
                        return -ENOMEM;
-               if (total > c->max_interfaces)
-                       goto cont;
 
                for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
                        if (rdev->wiphy.software_iftypes & BIT(iftype))