diff options
author | Jouni Malinen <jouni@qca.qualcomm.com> | 2014-04-28 04:22:08 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2014-04-28 12:09:59 -0400 |
commit | e16821bcfb364b0c41142db275dc74b39fa42c30 (patch) | |
tree | 238b98e192130cd99cee979cf9e540b217f878c8 /net/wireless/nl80211.c | |
parent | b205786e38b156d1ccaccd4f4ee780345a69cfeb (diff) |
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>
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 40 |
1 files changed, 28 insertions, 12 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ca75f60041d2..0f1b18f209d6 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -1928,18 +1928,20 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev, | |||
1928 | } | 1928 | } |
1929 | 1929 | ||
1930 | static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | 1930 | static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, |
1931 | struct wireless_dev *wdev, | 1931 | struct net_device *dev, |
1932 | struct genl_info *info) | 1932 | struct genl_info *info) |
1933 | { | 1933 | { |
1934 | struct cfg80211_chan_def chandef; | 1934 | struct cfg80211_chan_def chandef; |
1935 | int result; | 1935 | int result; |
1936 | enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; | 1936 | enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR; |
1937 | struct wireless_dev *wdev = NULL; | ||
1937 | 1938 | ||
1938 | if (wdev) | 1939 | if (dev) |
1939 | iftype = wdev->iftype; | 1940 | wdev = dev->ieee80211_ptr; |
1940 | |||
1941 | if (!nl80211_can_set_dev_channel(wdev)) | 1941 | if (!nl80211_can_set_dev_channel(wdev)) |
1942 | return -EOPNOTSUPP; | 1942 | return -EOPNOTSUPP; |
1943 | if (wdev) | ||
1944 | iftype = wdev->iftype; | ||
1943 | 1945 | ||
1944 | result = nl80211_parse_chandef(rdev, info, &chandef); | 1946 | result = nl80211_parse_chandef(rdev, info, &chandef); |
1945 | if (result) | 1947 | if (result) |
@@ -1948,14 +1950,27 @@ static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | |||
1948 | switch (iftype) { | 1950 | switch (iftype) { |
1949 | case NL80211_IFTYPE_AP: | 1951 | case NL80211_IFTYPE_AP: |
1950 | case NL80211_IFTYPE_P2P_GO: | 1952 | case NL80211_IFTYPE_P2P_GO: |
1951 | if (wdev->beacon_interval) { | ||
1952 | result = -EBUSY; | ||
1953 | break; | ||
1954 | } | ||
1955 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) { | 1953 | if (!cfg80211_reg_can_beacon(&rdev->wiphy, &chandef, iftype)) { |
1956 | result = -EINVAL; | 1954 | result = -EINVAL; |
1957 | break; | 1955 | break; |
1958 | } | 1956 | } |
1957 | if (wdev->beacon_interval) { | ||
1958 | if (!dev || !rdev->ops->set_ap_chanwidth || | ||
1959 | !(rdev->wiphy.features & | ||
1960 | NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)) { | ||
1961 | result = -EBUSY; | ||
1962 | break; | ||
1963 | } | ||
1964 | |||
1965 | /* Only allow dynamic channel width changes */ | ||
1966 | if (chandef.chan != wdev->preset_chandef.chan) { | ||
1967 | result = -EBUSY; | ||
1968 | break; | ||
1969 | } | ||
1970 | result = rdev_set_ap_chanwidth(rdev, dev, &chandef); | ||
1971 | if (result) | ||
1972 | break; | ||
1973 | } | ||
1959 | wdev->preset_chandef = chandef; | 1974 | wdev->preset_chandef = chandef; |
1960 | result = 0; | 1975 | result = 0; |
1961 | break; | 1976 | break; |
@@ -1977,7 +1992,7 @@ static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) | |||
1977 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | 1992 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; |
1978 | struct net_device *netdev = info->user_ptr[1]; | 1993 | struct net_device *netdev = info->user_ptr[1]; |
1979 | 1994 | ||
1980 | return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); | 1995 | return __nl80211_set_channel(rdev, netdev, info); |
1981 | } | 1996 | } |
1982 | 1997 | ||
1983 | static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info) | 1998 | 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) | |||
2099 | } | 2114 | } |
2100 | 2115 | ||
2101 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 2116 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
2102 | result = __nl80211_set_channel(rdev, | 2117 | result = __nl80211_set_channel( |
2103 | nl80211_can_set_dev_channel(wdev) ? wdev : NULL, | 2118 | rdev, |
2104 | info); | 2119 | nl80211_can_set_dev_channel(wdev) ? netdev : NULL, |
2120 | info); | ||
2105 | if (result) | 2121 | if (result) |
2106 | return result; | 2122 | return result; |
2107 | } | 2123 | } |