|
@@ -938,13 +938,20 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
|
|
return res;
|
|
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;
|
|
struct wireless_dev *wdev_iter;
|
|
u32 used_iftypes = BIT(iftype);
|
|
u32 used_iftypes = BIT(iftype);
|
|
int num[NUM_NL80211_IFTYPES];
|
|
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 total = 1;
|
|
int i, j;
|
|
int i, j;
|
|
|
|
|
|
@@ -955,9 +962,23 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
memset(num, 0, sizeof(num));
|
|
memset(num, 0, sizeof(num));
|
|
|
|
+ memset(used_channels, 0, sizeof(used_channels));
|
|
|
|
|
|
num[iftype] = 1;
|
|
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);
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
list_for_each_entry(wdev_iter, &rdev->netdev_list, list) {
|
|
list_for_each_entry(wdev_iter, &rdev->netdev_list, list) {
|
|
if (wdev_iter == wdev)
|
|
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))
|
|
if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
|
|
continue;
|
|
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]++;
|
|
num[wdev_iter->iftype]++;
|
|
total++;
|
|
total++;
|
|
used_iftypes |= BIT(wdev_iter->iftype);
|
|
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];
|
|
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,
|
|
limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
|
|
GFP_KERNEL);
|
|
GFP_KERNEL);
|
|
if (!limits)
|
|
if (!limits)
|
|
return -ENOMEM;
|
|
return -ENOMEM;
|
|
- if (total > c->max_interfaces)
|
|
|
|
- goto cont;
|
|
|
|
|
|
|
|
for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
|
|
for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
|
|
if (rdev->wiphy.software_iftypes & BIT(iftype))
|
|
if (rdev->wiphy.software_iftypes & BIT(iftype))
|