cfg80211: Dynamic channel bandwidth changes in AP mode

This extends NL80211_CMD_SET_CHANNEL to allow dynamic channel bandwidth
changes in AP mode (including P2P GO) during a lifetime of the BSS. This
can be used to implement, e.g., HT 20/40 MHz co-existence rules on the
2.4 GHz band.

Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
Jouni Malinen
2014-04-28 11:22:08 +03:00
committed by Johannes Berg
parent b205786e38
commit e16821bcfb
5 changed files with 71 additions and 12 deletions

View File

@@ -1928,18 +1928,20 @@ static int nl80211_parse_chandef(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 cfg80211_chan_def chandef;
int result;
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))
return -EOPNOTSUPP;
if (wdev)
iftype = wdev->iftype;
result = nl80211_parse_chandef(rdev, info, &chandef);
if (result)
@@ -1948,14 +1950,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
switch (iftype) {
case NL80211_IFTYPE_AP:
case NL80211_IFTYPE_P2P_GO:
if (wdev->beacon_interval) {
result = -EBUSY;
break;
}
if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) {
result = -EINVAL;
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;
result = 0;
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 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)
@@ -2099,9 +2114,10 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
}
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)
return result;
}