|
@@ -1928,18 +1928,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
|
|
}
|
|
}
|
|
|
|
|
|
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
|
static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
|
- struct wireless_dev *wdev,
|
|
|
|
|
|
+ struct net_device *dev,
|
|
struct genl_info *info)
|
|
struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_chan_def chandef;
|
|
struct cfg80211_chan_def chandef;
|
|
int result;
|
|
int result;
|
|
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
|
|
enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
|
|
|
|
+ struct wireless_dev *wdev = NULL;
|
|
|
|
|
|
- if (wdev)
|
|
|
|
- iftype = wdev->iftype;
|
|
|
|
-
|
|
|
|
|
|
+ if (dev)
|
|
|
|
+ wdev = dev->ieee80211_ptr;
|
|
if (!nl80211_can_set_dev_channel(wdev))
|
|
if (!nl80211_can_set_dev_channel(wdev))
|
|
return -EOPNOTSUPP;
|
|
return -EOPNOTSUPP;
|
|
|
|
+ if (wdev)
|
|
|
|
+ iftype = wdev->iftype;
|
|
|
|
|
|
result = nl80211_parse_chandef(rdev, info, &chandef);
|
|
result = nl80211_parse_chandef(rdev, info, &chandef);
|
|
if (result)
|
|
if (result)
|
|
@@ -1948,14 +1950,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
|
|
switch (iftype) {
|
|
switch (iftype) {
|
|
case NL80211_IFTYPE_AP:
|
|
case NL80211_IFTYPE_AP:
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
case NL80211_IFTYPE_P2P_GO:
|
|
- if (wdev->beacon_interval) {
|
|
|
|
- result = -EBUSY;
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
|
|
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
|
|
result = -EINVAL;
|
|
result = -EINVAL;
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
|
|
+ if (wdev->beacon_interval) {
|
|
|
|
+ if (!dev || !rdev->ops->set_ap_chanwidth ||
|
|
|
|
+ !(rdev->wiphy.features &
|
|
|
|
+ NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) {
|
|
|
|
+ result = -EBUSY;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Only allow dynamic channel width changes */
|
|
|
|
+ if (chandef.chan != wdev->preset_chandef.chan) {
|
|
|
|
+ result = -EBUSY;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ result = rdev_set_ap_chanwidth(rdev, dev, &chandef);
|
|
|
|
+ if (result)
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
wdev->preset_chandef = chandef;
|
|
wdev->preset_chandef = chandef;
|
|
result = 0;
|
|
result = 0;
|
|
break;
|
|
break;
|
|
@@ -1977,7 +1992,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct cfg80211_registered_device *rdev = info->user_ptr[0];
|
|
struct net_device *netdev = info->user_ptr[1];
|
|
struct net_device *netdev = info->user_ptr[1];
|
|
|
|
|
|
- return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
|
|
|
|
|
|
+ return __nl80211_set_channel(rdev, netdev, info);
|
|
}
|
|
}
|
|
|
|
|
|
static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
|
|
@@ -2099,9 +2114,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
|
|
}
|
|
}
|
|
|
|
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
|
if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
|
|
- result = __nl80211_set_channel(rdev,
|
|
|
|
- nl80211_can_set_dev_channel(wdev) ? wdev : NULL,
|
|
|
|
- info);
|
|
|
|
|
|
+ result = __nl80211_set_channel(
|
|
|
|
+ rdev,
|
|
|
|
+ nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
|
|
|
|
+ info);
|
|
if (result)
|
|
if (result)
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|